83 * track of the current code generator and what it is doing.
84 * <p>
85 * There is, however, nothing stopping you from using this in a
86 * completely self contained environment, for example in ObjectGenerator
87 * where there are no visitors or external hooks.
88 * <p>
89 * MethodEmitter makes it simple to generate code for methods without
90 * having to do arduous type checking. It maintains a type stack
91 * and will pick the appropriate operation for all operations sent to it
92 * We also allow chained called to a MethodEmitter for brevity, e.g.
93 * it is legal to write _new(className).dup() or
94 * load(slot).load(slot2).xor().store(slot3);
95 * <p>
96 * If running with assertions enabled, any type conflict, such as different
97 * bytecode stack sizes or operating on the wrong type will be detected
98 * and an error thrown.
99 * <p>
100 * There is also a very nice debug interface that can emit formatted
101 * bytecodes that have been written. This is enabled by setting the
102 * environment "nashorn.codegen.debug" to true, or --log=codegen:{@literal <level>}
103 * <p>
104 *
105 * @see Compiler
106 */
107 public class ClassEmitter {
108 /** Default flags for class generation - public class */
109 private static final EnumSet<Flag> DEFAULT_METHOD_FLAGS = EnumSet.of(Flag.PUBLIC);
110
111 /** Sanity check flag - have we started on a class? */
112 private boolean classStarted;
113
114 /** Sanity check flag - have we ended this emission? */
115 private boolean classEnded;
116
117 /**
118 * Sanity checks - which methods have we currently
119 * started for generation in this class?
120 */
121 private final HashSet<MethodEmitter> methodsStarted;
122
123 /** The ASM classwriter that we use for all bytecode operations */
127 protected final Context context;
128
129 /** Compile unit class name. */
130 private String unitClassName;
131
132 /** Set of constants access methods required. */
133 private Set<Class<?>> constantMethodNeeded;
134
135 private int methodCount;
136
137 private int initCount;
138
139 private int clinitCount;
140
141 private int fieldCount;
142
143 private final Set<String> methodNames;
144
145 /**
146 * Constructor - only used internally in this class as it breaks
147 * abstraction towards ASM or other code generator below
148 *
149 * @param env script environment
150 * @param cw ASM classwriter
151 */
152 private ClassEmitter(final Context context, final ClassWriter cw) {
153 this.context = context;
154 this.cw = cw;
155 this.methodsStarted = new HashSet<>();
156 this.methodNames = new HashSet<>();
157 }
158
159 /**
160 * Return the method names encountered
161 * @return method names
162 */
163 public Set<String> getMethodNames() {
164 return Collections.unmodifiableSet(methodNames);
165 }
166
167 /**
168 * Constructor
169 *
170 * @param env script environment
171 * @param className name of class to weave
172 * @param superClassName super class name for class
173 * @param interfaceNames names of interfaces implemented by this class, or null if none
174 */
175 ClassEmitter(final Context context, final String className, final String superClassName, final String... interfaceNames) {
176 this(context, new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS));
177 cw.visit(V1_7, ACC_PUBLIC | ACC_SUPER, className, null, superClassName, interfaceNames);
178 }
179
180 /**
181 * Constructor from the compiler
182 *
183 * @param env Script environment
184 * @param sourceName Source name
185 * @param unitClassName Compile unit class name.
186 * @param strictMode Should we generate this method in strict mode
187 */
188 ClassEmitter(final Context context, final String sourceName, final String unitClassName, final boolean strictMode) {
189 this(context,
190 new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS) {
191 private static final String OBJECT_CLASS = "java/lang/Object";
192
193 @Override
194 protected String getCommonSuperClass(final String type1, final String type2) {
195 try {
196 return super.getCommonSuperClass(type1, type2);
197 } catch (final RuntimeException e) {
198 if (isScriptObject(Compiler.SCRIPTS_PACKAGE, type1) && isScriptObject(Compiler.SCRIPTS_PACKAGE, type2)) {
199 return className(ScriptObject.class);
200 }
201 return OBJECT_CLASS;
202 }
203 }
204 });
205
206 this.unitClassName = unitClassName;
207 this.constantMethodNeeded = new HashSet<>();
208
209 cw.visit(V1_7, ACC_PUBLIC | ACC_SUPER, unitClassName, null, pathName(jdk.nashorn.internal.scripts.JS.class.getName()), null);
210 cw.visitSource(sourceName, null);
211
212 defineCommonStatics(strictMode);
213 }
214
215 Context getContext() {
216 return context;
217 }
218
219 /**
220 * Returns the name of the compile unit class name.
221 * @return the name of the compile unit class name.
222 */
223 String getUnitClassName() {
224 return unitClassName;
225 }
226
227 /**
228 * Get the method count, including init and clinit methods
229 * @return method count
230 */
231 public int getMethodCount() {
232 return methodCount;
233 }
234
235 /**
236 * Get the clinit count
237 * @return clinit count
238 */
239 public int getClinitCount() {
240 return clinitCount;
241 }
242
243 /**
244 * Get the init count
245 * @return init count
246 */
247 public int getInitCount() {
248 return initCount;
249 }
250
251 /**
252 * Get the field count
253 * @return field count
254 */
255 public int getFieldCount() {
256 return fieldCount;
257 }
258
259 /**
260 * Convert a binary name to a package/class name.
261 *
262 * @param name Binary name.
263 * @return Package/class name.
264 */
265 private static String pathName(final String name) {
266 return name.replace('.', '/');
267 }
268
269 /**
270 * Define the static fields common in all scripts.
271 * @param strictMode Should we generate this method in strict mode
272 */
273 private void defineCommonStatics(final boolean strictMode) {
274 // source - used to store the source data (text) for this script. Shared across
275 // compile units. Set externally by the compiler.
276 field(EnumSet.of(Flag.PRIVATE, Flag.STATIC), SOURCE.symbolName(), Source.class);
277
278 // constants - used to the constants array for this script. Shared across
279 // compile units. Set externally by the compiler.
280 field(EnumSet.of(Flag.PRIVATE, Flag.STATIC), CONSTANTS.symbolName(), Object[].class);
281
282 // strictMode - was this script compiled in strict mode. Set externally by the compiler.
283 field(EnumSet.of(Flag.PUBLIC, Flag.STATIC, Flag.FINAL), STRICT_MODE.symbolName(), boolean.class, strictMode);
284 }
285
286 /**
287 * Define static utilities common needed in scripts. These are per compile unit
288 * and therefore have to be defined here and not in code gen.
289 */
290 private void defineCommonUtilities() {
291 assert unitClassName != null;
292
293 if (constantMethodNeeded.contains(String.class)) {
294 // $getString - get the ith entry from the constants table and cast to String.
295 final MethodEmitter getStringMethod = method(EnumSet.of(Flag.PRIVATE, Flag.STATIC), GET_STRING.symbolName(), String.class, int.class);
296 getStringMethod.begin();
297 getStringMethod.getStatic(unitClassName, CONSTANTS.symbolName(), CONSTANTS.descriptor())
298 .load(Type.INT, 0)
299 .arrayload()
300 .checkcast(String.class)
301 ._return();
302 getStringMethod.end();
303 }
304
305 if (constantMethodNeeded.contains(PropertyMap.class)) {
306 // $getMap - get the ith entry from the constants table and cast to PropertyMap.
307 final MethodEmitter getMapMethod = method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), GET_MAP.symbolName(), PropertyMap.class, int.class);
308 getMapMethod.begin();
316 // $setMap - overwrite an existing map.
317 final MethodEmitter setMapMethod = method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), SET_MAP.symbolName(), void.class, int.class, PropertyMap.class);
318 setMapMethod.begin();
319 setMapMethod.loadConstants()
320 .load(Type.INT, 0)
321 .load(Type.OBJECT, 1)
322 .arraystore();
323 setMapMethod.returnVoid();
324 setMapMethod.end();
325 }
326
327 // $getXXXX$array - get the ith entry from the constants table and cast to XXXX[].
328 for (final Class<?> clazz : constantMethodNeeded) {
329 if (clazz.isArray()) {
330 defineGetArrayMethod(clazz);
331 }
332 }
333 }
334
335 /**
336 * Constructs a primitive specific method for getting the ith entry from the constants table as an array.
337 * @param clazz Array class.
338 */
339 private void defineGetArrayMethod(final Class<?> clazz) {
340 assert unitClassName != null;
341
342 final String methodName = getArrayMethodName(clazz);
343 final MethodEmitter getArrayMethod = method(EnumSet.of(Flag.PRIVATE, Flag.STATIC), methodName, clazz, int.class);
344
345 getArrayMethod.begin();
346 getArrayMethod.getStatic(unitClassName, CONSTANTS.symbolName(), CONSTANTS.descriptor())
347 .load(Type.INT, 0)
348 .arrayload()
349 .checkcast(clazz)
350 .invoke(virtualCallNoLookup(clazz, "clone", Object.class))
351 .checkcast(clazz)
352 ._return();
353 getArrayMethod.end();
354 }
355
356
357 /**
358 * Generate the name of a get array from constant pool method.
359 * @param clazz Name of array class.
360 * @return Method name.
361 */
362 static String getArrayMethodName(final Class<?> clazz) {
363 assert clazz.isArray();
364 return GET_ARRAY_PREFIX.symbolName() + clazz.getComponentType().getSimpleName() + GET_ARRAY_SUFFIX.symbolName();
365 }
366
367 /**
368 * Ensure a get constant method is issued for the class.
369 * @param clazz Class of constant.
370 */
371 void needGetConstantMethod(final Class<?> clazz) {
372 constantMethodNeeded.add(clazz);
373 }
374
375 /**
376 * Inspect class name and decide whether we are generating a ScriptObject class
377 *
378 * @param scriptPrefix the script class prefix for the current script
379 * @param type the type to check
380 *
381 * @return true if type is ScriptObject
382 */
383 private static boolean isScriptObject(final String scriptPrefix, final String type) {
384 if (type.startsWith(scriptPrefix)) {
385 return true;
386 } else if (type.equals(CompilerConstants.className(ScriptObject.class))) {
387 return true;
388 } else if (type.startsWith(Compiler.OBJECTS_PACKAGE)) {
389 return true;
390 }
391
392 return false;
393 }
394
395 /**
396 * Call at beginning of class emission
397 */
398 public void begin() {
399 classStarted = true;
400 }
401
402 /**
403 * Call at end of class emission
404 */
405 public void end() {
406 assert classStarted : "class not started for " + unitClassName;
407
408 if (unitClassName != null) {
409 final MethodEmitter initMethod = init(EnumSet.of(Flag.PRIVATE));
410 initMethod.begin();
411 initMethod.load(Type.OBJECT, 0);
412 initMethod.newInstance(jdk.nashorn.internal.scripts.JS.class);
413 initMethod.returnVoid();
414 initMethod.end();
415
416 defineCommonUtilities();
417 }
418
419 cw.visitEnd();
420 classStarted = false;
421 classEnded = true;
422 assert methodsStarted.isEmpty() : "methodsStarted not empty " + methodsStarted;
423 }
424
425 /**
426 * Disassemble an array of byte code.
427 * @param bytecode byte array representing bytecode
428 * @return disassembly as human readable string
429 */
430 static String disassemble(final byte[] bytecode) {
431 final ByteArrayOutputStream baos = new ByteArrayOutputStream();
432 try (final PrintWriter pw = new PrintWriter(baos)) {
433 final NashornClassReader cr = new NashornClassReader(bytecode);
434 final Context ctx = AccessController.doPrivileged(new PrivilegedAction<Context>() {
435 @Override
436 public Context run() {
437 return Context.getContext();
438 }
439 });
440 final TraceClassVisitor tcv = new TraceClassVisitor(null, new NashornTextifier(ctx.getEnv(), cr), pw);
441 cr.accept(tcv, 0);
442 }
443
444 final String str = new String(baos.toByteArray());
445 return str;
446 }
447
448 /**
449 * Call back from MethodEmitter for method start
450 *
451 * @see MethodEmitter
452 *
453 * @param method method emitter.
454 */
455 void beginMethod(final MethodEmitter method) {
456 assert !methodsStarted.contains(method);
457 methodsStarted.add(method);
458 }
459
460 /**
461 * Call back from MethodEmitter for method end
462 *
463 * @see MethodEmitter
464 *
465 * @param method
466 */
467 void endMethod(final MethodEmitter method) {
468 assert methodsStarted.contains(method);
469 methodsStarted.remove(method);
470 }
471
472 /**
473 * Add a new method to the class - defaults to public method
474 *
475 * @param methodName name of method
476 * @param rtype return type of the method
477 * @param ptypes parameter types the method
478 *
479 * @return method emitter to use for weaving this method
480 */
481 MethodEmitter method(final String methodName, final Class<?> rtype, final Class<?>... ptypes) {
482 return method(DEFAULT_METHOD_FLAGS, methodName, rtype, ptypes); //TODO why public default ?
483 }
484
485 /**
486 * Add a new method to the class - defaults to public method
487 *
488 * @param methodFlags access flags for the method
489 * @param methodName name of method
490 * @param rtype return type of the method
491 * @param ptypes parameter types the method
492 *
493 * @return method emitter to use for weaving this method
494 */
495 MethodEmitter method(final EnumSet<Flag> methodFlags, final String methodName, final Class<?> rtype, final Class<?>... ptypes) {
496 methodCount++;
497 methodNames.add(methodName);
498 return new MethodEmitter(this, methodVisitor(methodFlags, methodName, rtype, ptypes));
499 }
500
501 /**
502 * Add a new method to the class - defaults to public method
503 *
504 * @param methodName name of method
505 * @param descriptor descriptor of method
506 *
507 * @return method emitter to use for weaving this method
508 */
509 MethodEmitter method(final String methodName, final String descriptor) {
510 return method(DEFAULT_METHOD_FLAGS, methodName, descriptor);
511 }
512
513 /**
514 * Add a new method to the class - defaults to public method
515 *
516 * @param methodFlags access flags for the method
517 * @param methodName name of method
518 * @param descriptor descriptor of method
519 *
520 * @return method emitter to use for weaving this method
521 */
522 MethodEmitter method(final EnumSet<Flag> methodFlags, final String methodName, final String descriptor) {
523 methodCount++;
524 methodNames.add(methodName);
525 return new MethodEmitter(this, cw.visitMethod(Flag.getValue(methodFlags), methodName, descriptor, null, null));
526 }
527
528 /**
529 * Add a new method to the class, representing a function node
530 *
531 * @param functionNode the function node to generate a method for
532 * @return method emitter to use for weaving this method
533 */
534 MethodEmitter method(final FunctionNode functionNode) {
535 methodCount++;
536 methodNames.add(functionNode.getName());
537 final FunctionSignature signature = new FunctionSignature(functionNode);
538 final MethodVisitor mv = cw.visitMethod(
539 ACC_PUBLIC | ACC_STATIC | (functionNode.isVarArg() ? ACC_VARARGS : 0),
540 functionNode.getName(),
541 signature.toString(),
542 null,
543 null);
544
545 return new MethodEmitter(this, mv, functionNode);
546 }
547
548 /**
549 * Add a new method to the class, representing a rest-of version of the function node
550 *
551 * @param functionNode the function node to generate a method for
552 * @return method emitter to use for weaving this method
553 */
554 MethodEmitter restOfMethod(final FunctionNode functionNode) {
555 methodCount++;
556 methodNames.add(functionNode.getName());
557 final MethodVisitor mv = cw.visitMethod(
558 ACC_PUBLIC | ACC_STATIC,
559 functionNode.getName(),
560 Type.getMethodDescriptor(functionNode.getReturnType().getTypeClass(), RewriteException.class),
561 null,
562 null);
563
564 return new MethodEmitter(this, mv, functionNode);
565 }
566
567
568 /**
569 * Start generating the <clinit> method in the class
570 *
571 * @return method emitter to use for weaving <clinit>
572 */
573 MethodEmitter clinit() {
574 clinitCount++;
575 return method(EnumSet.of(Flag.STATIC), CLINIT.symbolName(), void.class);
576 }
577
578 /**
579 * Start generating an <init>()V method in the class
580 *
581 * @return method emitter to use for weaving <init>()V
582 */
583 MethodEmitter init() {
584 initCount++;
585 return method(INIT.symbolName(), void.class);
586 }
587
588 /**
589 * Start generating an <init>()V method in the class
590 *
591 * @param ptypes parameter types for constructor
592 * @return method emitter to use for weaving <init>()V
593 */
594 MethodEmitter init(final Class<?>... ptypes) {
595 initCount++;
596 return method(INIT.symbolName(), void.class, ptypes);
597 }
598
599 /**
600 * Start generating an <init>(...)V method in the class
601 *
602 * @param flags access flags for the constructor
603 * @param ptypes parameter types for the constructor
604 *
605 * @return method emitter to use for weaving <init>(...)V
606 */
607 MethodEmitter init(final EnumSet<Flag> flags, final Class<?>... ptypes) {
608 initCount++;
609 return method(flags, INIT.symbolName(), void.class, ptypes);
610 }
611
612 /**
613 * Add a field to the class, initialized to a value
614 *
615 * @param fieldFlags flags, e.g. should it be static or public etc
616 * @param fieldName name of field
617 * @param fieldType the type of the field
618 * @param value the value
619 *
620 * @see ClassEmitter.Flag
621 */
622 final void field(final EnumSet<Flag> fieldFlags, final String fieldName, final Class<?> fieldType, final Object value) {
623 fieldCount++;
624 cw.visitField(Flag.getValue(fieldFlags), fieldName, typeDescriptor(fieldType), null, value).visitEnd();
625 }
626
627 /**
628 * Add a field to the class
629 *
630 * @param fieldFlags access flags for the field
631 * @param fieldName name of field
632 * @param fieldType type of the field
633 *
634 * @see ClassEmitter.Flag
635 */
636 final void field(final EnumSet<Flag> fieldFlags, final String fieldName, final Class<?> fieldType) {
637 field(fieldFlags, fieldName, fieldType, null);
638 }
639
640 /**
641 * Add a field to the class - defaults to public
642 *
643 * @param fieldName name of field
644 * @param fieldType type of field
645 */
646 final void field(final String fieldName, final Class<?> fieldType) {
647 field(EnumSet.of(Flag.PUBLIC), fieldName, fieldType, null);
648 }
649
650 /**
651 * Return a bytecode array from this ClassEmitter. The ClassEmitter must
652 * have been ended (having its end function called) for this to work.
653 *
654 * @return byte code array for generated class, null if class generation hasn't been ended with {@link ClassEmitter#end()}
655 */
656 byte[] toByteArray() {
657 assert classEnded;
658 if (!classEnded) {
659 return null;
660 }
661
662 return cw.toByteArray();
663 }
664
665 /**
666 * Abstraction for flags used in class emission
667 *
668 * We provide abstraction separating these from the underlying bytecode
669 * emitter.
670 *
671 * Flags are provided for method handles, protection levels, static/virtual
672 * fields/methods.
673 */
674 static enum Flag {
675 /** method handle with static access */
676 HANDLE_STATIC(H_INVOKESTATIC),
677 /** method handle with new invoke special access */
678 HANDLE_NEWSPECIAL(H_NEWINVOKESPECIAL),
679 /** method handle with invoke special access */
680 HANDLE_SPECIAL(H_INVOKESPECIAL),
681 /** method handle with invoke virtual access */
682 HANDLE_VIRTUAL(H_INVOKEVIRTUAL),
683 /** method handle with invoke interface access */
684 HANDLE_INTERFACE(H_INVOKEINTERFACE),
685
686 /** final access */
687 FINAL(ACC_FINAL),
688 /** static access */
689 STATIC(ACC_STATIC),
690 /** public access */
691 PUBLIC(ACC_PUBLIC),
692 /** private access */
693 PRIVATE(ACC_PRIVATE);
694
695 private int value;
696
697 private Flag(final int value) {
698 this.value = value;
699 }
700
701 /**
702 * Get the value of this flag
703 * @return the int value
704 */
705 int getValue() {
706 return value;
707 }
708
709 /**
710 * Return the corresponding ASM flag value for an enum set of flags
711 *
712 * @param flags enum set of flags
713 * @return an integer value representing the flags intrinsic values or:ed together
714 */
715 static int getValue(final EnumSet<Flag> flags) {
716 int v = 0;
717 for (final Flag flag : flags) {
718 v |= flag.getValue();
719 }
720 return v;
721 }
722 }
723
724 private MethodVisitor methodVisitor(final EnumSet<Flag> flags, final String methodName, final Class<?> rtype, final Class<?>... ptypes) {
725 return cw.visitMethod(Flag.getValue(flags), methodName, methodDescriptor(rtype, ptypes), null, null);
726 }
727
728 }
|
83 * track of the current code generator and what it is doing.
84 * <p>
85 * There is, however, nothing stopping you from using this in a
86 * completely self contained environment, for example in ObjectGenerator
87 * where there are no visitors or external hooks.
88 * <p>
89 * MethodEmitter makes it simple to generate code for methods without
90 * having to do arduous type checking. It maintains a type stack
91 * and will pick the appropriate operation for all operations sent to it
92 * We also allow chained called to a MethodEmitter for brevity, e.g.
93 * it is legal to write _new(className).dup() or
94 * load(slot).load(slot2).xor().store(slot3);
95 * <p>
96 * If running with assertions enabled, any type conflict, such as different
97 * bytecode stack sizes or operating on the wrong type will be detected
98 * and an error thrown.
99 * <p>
100 * There is also a very nice debug interface that can emit formatted
101 * bytecodes that have been written. This is enabled by setting the
102 * environment "nashorn.codegen.debug" to true, or --log=codegen:{@literal <level>}
103 *
104 * @see Compiler
105 */
106 public class ClassEmitter {
107 /** Default flags for class generation - public class */
108 private static final EnumSet<Flag> DEFAULT_METHOD_FLAGS = EnumSet.of(Flag.PUBLIC);
109
110 /** Sanity check flag - have we started on a class? */
111 private boolean classStarted;
112
113 /** Sanity check flag - have we ended this emission? */
114 private boolean classEnded;
115
116 /**
117 * Sanity checks - which methods have we currently
118 * started for generation in this class?
119 */
120 private final HashSet<MethodEmitter> methodsStarted;
121
122 /** The ASM classwriter that we use for all bytecode operations */
126 protected final Context context;
127
128 /** Compile unit class name. */
129 private String unitClassName;
130
131 /** Set of constants access methods required. */
132 private Set<Class<?>> constantMethodNeeded;
133
134 private int methodCount;
135
136 private int initCount;
137
138 private int clinitCount;
139
140 private int fieldCount;
141
142 private final Set<String> methodNames;
143
144 /**
145 * Constructor - only used internally in this class as it breaks
146 * abstraction towards ASM or other code generator below.
147 *
148 * @param env script environment
149 * @param cw ASM classwriter
150 */
151 private ClassEmitter(final Context context, final ClassWriter cw) {
152 this.context = context;
153 this.cw = cw;
154 this.methodsStarted = new HashSet<>();
155 this.methodNames = new HashSet<>();
156 }
157
158 /**
159 * Return the method names encountered.
160 *
161 * @return method names
162 */
163 public Set<String> getMethodNames() {
164 return Collections.unmodifiableSet(methodNames);
165 }
166
167 /**
168 * Constructor.
169 *
170 * @param env script environment
171 * @param className name of class to weave
172 * @param superClassName super class name for class
173 * @param interfaceNames names of interfaces implemented by this class, or
174 * {@code null} if none
175 */
176 ClassEmitter(final Context context, final String className, final String superClassName, final String... interfaceNames) {
177 this(context, new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS));
178 cw.visit(V1_7, ACC_PUBLIC | ACC_SUPER, className, null, superClassName, interfaceNames);
179 }
180
181 /**
182 * Constructor from the compiler.
183 *
184 * @param env Script environment
185 * @param sourceName Source name
186 * @param unitClassName Compile unit class name.
187 * @param strictMode Should we generate this method in strict mode
188 */
189 ClassEmitter(final Context context, final String sourceName, final String unitClassName, final boolean strictMode) {
190 this(context,
191 new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS) {
192 private static final String OBJECT_CLASS = "java/lang/Object";
193
194 @Override
195 protected String getCommonSuperClass(final String type1, final String type2) {
196 try {
197 return super.getCommonSuperClass(type1, type2);
198 } catch (final RuntimeException e) {
199 if (isScriptObject(Compiler.SCRIPTS_PACKAGE, type1) && isScriptObject(Compiler.SCRIPTS_PACKAGE, type2)) {
200 return className(ScriptObject.class);
201 }
202 return OBJECT_CLASS;
203 }
204 }
205 });
206
207 this.unitClassName = unitClassName;
208 this.constantMethodNeeded = new HashSet<>();
209
210 cw.visit(V1_7, ACC_PUBLIC | ACC_SUPER, unitClassName, null, pathName(jdk.nashorn.internal.scripts.JS.class.getName()), null);
211 cw.visitSource(sourceName, null);
212
213 defineCommonStatics(strictMode);
214 }
215
216 Context getContext() {
217 return context;
218 }
219
220 /**
221 * @return the name of the compile unit class name.
222 */
223 String getUnitClassName() {
224 return unitClassName;
225 }
226
227 /**
228 * Get the method count, including init and clinit methods.
229 *
230 * @return method count
231 */
232 public int getMethodCount() {
233 return methodCount;
234 }
235
236 /**
237 * Get the clinit count.
238 *
239 * @return clinit count
240 */
241 public int getClinitCount() {
242 return clinitCount;
243 }
244
245 /**
246 * Get the init count.
247 *
248 * @return init count
249 */
250 public int getInitCount() {
251 return initCount;
252 }
253
254 /**
255 * Get the field count.
256 *
257 * @return field count
258 */
259 public int getFieldCount() {
260 return fieldCount;
261 }
262
263 /**
264 * Convert a binary name to a package/class name.
265 *
266 * @param name Binary name.
267 *
268 * @return Package/class name.
269 */
270 private static String pathName(final String name) {
271 return name.replace('.', '/');
272 }
273
274 /**
275 * Define the static fields common in all scripts.
276 *
277 * @param strictMode Should we generate this method in strict mode
278 */
279 private void defineCommonStatics(final boolean strictMode) {
280 // source - used to store the source data (text) for this script. Shared across
281 // compile units. Set externally by the compiler.
282 field(EnumSet.of(Flag.PRIVATE, Flag.STATIC), SOURCE.symbolName(), Source.class);
283
284 // constants - used to the constants array for this script. Shared across
285 // compile units. Set externally by the compiler.
286 field(EnumSet.of(Flag.PRIVATE, Flag.STATIC), CONSTANTS.symbolName(), Object[].class);
287
288 // strictMode - was this script compiled in strict mode. Set externally by the compiler.
289 field(EnumSet.of(Flag.PUBLIC, Flag.STATIC, Flag.FINAL), STRICT_MODE.symbolName(), boolean.class, strictMode);
290 }
291
292 /**
293 * Define static utilities common needed in scripts. These are per compile
294 * unit and therefore have to be defined here and not in code gen.
295 */
296 private void defineCommonUtilities() {
297 assert unitClassName != null;
298
299 if (constantMethodNeeded.contains(String.class)) {
300 // $getString - get the ith entry from the constants table and cast to String.
301 final MethodEmitter getStringMethod = method(EnumSet.of(Flag.PRIVATE, Flag.STATIC), GET_STRING.symbolName(), String.class, int.class);
302 getStringMethod.begin();
303 getStringMethod.getStatic(unitClassName, CONSTANTS.symbolName(), CONSTANTS.descriptor())
304 .load(Type.INT, 0)
305 .arrayload()
306 .checkcast(String.class)
307 ._return();
308 getStringMethod.end();
309 }
310
311 if (constantMethodNeeded.contains(PropertyMap.class)) {
312 // $getMap - get the ith entry from the constants table and cast to PropertyMap.
313 final MethodEmitter getMapMethod = method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), GET_MAP.symbolName(), PropertyMap.class, int.class);
314 getMapMethod.begin();
322 // $setMap - overwrite an existing map.
323 final MethodEmitter setMapMethod = method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), SET_MAP.symbolName(), void.class, int.class, PropertyMap.class);
324 setMapMethod.begin();
325 setMapMethod.loadConstants()
326 .load(Type.INT, 0)
327 .load(Type.OBJECT, 1)
328 .arraystore();
329 setMapMethod.returnVoid();
330 setMapMethod.end();
331 }
332
333 // $getXXXX$array - get the ith entry from the constants table and cast to XXXX[].
334 for (final Class<?> clazz : constantMethodNeeded) {
335 if (clazz.isArray()) {
336 defineGetArrayMethod(clazz);
337 }
338 }
339 }
340
341 /**
342 * Constructs a primitive specific method for getting the ith entry from the
343 * constants table as an array.
344 *
345 * @param clazz Array class.
346 */
347 private void defineGetArrayMethod(final Class<?> clazz) {
348 assert unitClassName != null;
349
350 final String methodName = getArrayMethodName(clazz);
351 final MethodEmitter getArrayMethod = method(EnumSet.of(Flag.PRIVATE, Flag.STATIC), methodName, clazz, int.class);
352
353 getArrayMethod.begin();
354 getArrayMethod.getStatic(unitClassName, CONSTANTS.symbolName(), CONSTANTS.descriptor())
355 .load(Type.INT, 0)
356 .arrayload()
357 .checkcast(clazz)
358 .invoke(virtualCallNoLookup(clazz, "clone", Object.class))
359 .checkcast(clazz)
360 ._return();
361 getArrayMethod.end();
362 }
363
364
365 /**
366 * Generate the name of a get array from constant pool method.
367 *
368 * @param clazz Name of array class.
369 *
370 * @return Method name.
371 */
372 static String getArrayMethodName(final Class<?> clazz) {
373 assert clazz.isArray();
374 return GET_ARRAY_PREFIX.symbolName() + clazz.getComponentType().getSimpleName() + GET_ARRAY_SUFFIX.symbolName();
375 }
376
377 /**
378 * Ensure a get constant method is issued for the class.
379 *
380 * @param clazz Class of constant.
381 */
382 void needGetConstantMethod(final Class<?> clazz) {
383 constantMethodNeeded.add(clazz);
384 }
385
386 /**
387 * Inspect class name and decide whether we are generating a ScriptObject class.
388 *
389 * @param scriptPrefix the script class prefix for the current script
390 * @param type the type to check
391 *
392 * @return {@code true} if type is ScriptObject
393 */
394 private static boolean isScriptObject(final String scriptPrefix, final String type) {
395 if (type.startsWith(scriptPrefix)) {
396 return true;
397 } else if (type.equals(CompilerConstants.className(ScriptObject.class))) {
398 return true;
399 } else if (type.startsWith(Compiler.OBJECTS_PACKAGE)) {
400 return true;
401 }
402
403 return false;
404 }
405
406 /**
407 * Call at beginning of class emission.
408 */
409 public void begin() {
410 classStarted = true;
411 }
412
413 /**
414 * Call at end of class emission.
415 */
416 public void end() {
417 assert classStarted : "class not started for " + unitClassName;
418
419 if (unitClassName != null) {
420 final MethodEmitter initMethod = init(EnumSet.of(Flag.PRIVATE));
421 initMethod.begin();
422 initMethod.load(Type.OBJECT, 0);
423 initMethod.newInstance(jdk.nashorn.internal.scripts.JS.class);
424 initMethod.returnVoid();
425 initMethod.end();
426
427 defineCommonUtilities();
428 }
429
430 cw.visitEnd();
431 classStarted = false;
432 classEnded = true;
433 assert methodsStarted.isEmpty() : "methodsStarted not empty " + methodsStarted;
434 }
435
436 /**
437 * Disassemble an array of byte code.
438 *
439 * @param bytecode byte array representing bytecode
440 *
441 * @return disassembly as human readable string
442 */
443 static String disassemble(final byte[] bytecode) {
444 final ByteArrayOutputStream baos = new ByteArrayOutputStream();
445 try (final PrintWriter pw = new PrintWriter(baos)) {
446 final NashornClassReader cr = new NashornClassReader(bytecode);
447 final Context ctx = AccessController.doPrivileged(new PrivilegedAction<Context>() {
448 @Override
449 public Context run() {
450 return Context.getContext();
451 }
452 });
453 final TraceClassVisitor tcv = new TraceClassVisitor(null, new NashornTextifier(ctx.getEnv(), cr), pw);
454 cr.accept(tcv, 0);
455 }
456
457 final String str = new String(baos.toByteArray());
458 return str;
459 }
460
461 /**
462 * Call back from MethodEmitter for method start.
463 *
464 * @see MethodEmitter
465 *
466 * @param method method emitter.
467 */
468 void beginMethod(final MethodEmitter method) {
469 assert !methodsStarted.contains(method);
470 methodsStarted.add(method);
471 }
472
473 /**
474 * Call back from MethodEmitter for method end.
475 *
476 * @see MethodEmitter
477 *
478 * @param method
479 */
480 void endMethod(final MethodEmitter method) {
481 assert methodsStarted.contains(method);
482 methodsStarted.remove(method);
483 }
484
485 /**
486 * Add a new method to the class - defaults to public method.
487 *
488 * @param methodName name of method
489 * @param rtype return type of the method
490 * @param ptypes parameter types the method
491 *
492 * @return method emitter to use for weaving this method
493 */
494 MethodEmitter method(final String methodName, final Class<?> rtype, final Class<?>... ptypes) {
495 return method(DEFAULT_METHOD_FLAGS, methodName, rtype, ptypes); //TODO why public default ?
496 }
497
498 /**
499 * Add a new method to the class - defaults to public method.
500 *
501 * @param methodFlags access flags for the method
502 * @param methodName name of method
503 * @param rtype return type of the method
504 * @param ptypes parameter types the method
505 *
506 * @return method emitter to use for weaving this method
507 */
508 MethodEmitter method(final EnumSet<Flag> methodFlags, final String methodName, final Class<?> rtype, final Class<?>... ptypes) {
509 methodCount++;
510 methodNames.add(methodName);
511 return new MethodEmitter(this, methodVisitor(methodFlags, methodName, rtype, ptypes));
512 }
513
514 /**
515 * Add a new method to the class - defaults to public method.
516 *
517 * @param methodName name of method
518 * @param descriptor descriptor of method
519 *
520 * @return method emitter to use for weaving this method
521 */
522 MethodEmitter method(final String methodName, final String descriptor) {
523 return method(DEFAULT_METHOD_FLAGS, methodName, descriptor);
524 }
525
526 /**
527 * Add a new method to the class - defaults to public method.
528 *
529 * @param methodFlags access flags for the method
530 * @param methodName name of method
531 * @param descriptor descriptor of method
532 *
533 * @return method emitter to use for weaving this method
534 */
535 MethodEmitter method(final EnumSet<Flag> methodFlags, final String methodName, final String descriptor) {
536 methodCount++;
537 methodNames.add(methodName);
538 return new MethodEmitter(this, cw.visitMethod(Flag.getValue(methodFlags), methodName, descriptor, null, null));
539 }
540
541 /**
542 * Add a new method to the class, representing a function node.
543 *
544 * @param functionNode the function node to generate a method for
545 *
546 * @return method emitter to use for weaving this method
547 */
548 MethodEmitter method(final FunctionNode functionNode) {
549 methodCount++;
550 methodNames.add(functionNode.getName());
551 final FunctionSignature signature = new FunctionSignature(functionNode);
552 final MethodVisitor mv = cw.visitMethod(
553 ACC_PUBLIC | ACC_STATIC | (functionNode.isVarArg() ? ACC_VARARGS : 0),
554 functionNode.getName(),
555 signature.toString(),
556 null,
557 null);
558
559 return new MethodEmitter(this, mv, functionNode);
560 }
561
562 /**
563 * Add a new method to the class, representing a rest-of version of the
564 * function node.
565 *
566 * @param functionNode the function node to generate a method for
567 *
568 * @return method emitter to use for weaving this method
569 */
570 MethodEmitter restOfMethod(final FunctionNode functionNode) {
571 methodCount++;
572 methodNames.add(functionNode.getName());
573 final MethodVisitor mv = cw.visitMethod(
574 ACC_PUBLIC | ACC_STATIC,
575 functionNode.getName(),
576 Type.getMethodDescriptor(functionNode.getReturnType().getTypeClass(), RewriteException.class),
577 null,
578 null);
579
580 return new MethodEmitter(this, mv, functionNode);
581 }
582
583
584 /**
585 * Start generating the <clinit> method in the class.
586 *
587 * @return method emitter to use for weaving <clinit>
588 */
589 MethodEmitter clinit() {
590 clinitCount++;
591 return method(EnumSet.of(Flag.STATIC), CLINIT.symbolName(), void.class);
592 }
593
594 /**
595 * Start generating an <init>()V method in the class.
596 *
597 * @return method emitter to use for weaving <init>()V
598 */
599 MethodEmitter init() {
600 initCount++;
601 return method(INIT.symbolName(), void.class);
602 }
603
604 /**
605 * Start generating an <init>()V method in the class.
606 *
607 * @param ptypes parameter types for constructor
608 * @return method emitter to use for weaving <init>()V
609 */
610 MethodEmitter init(final Class<?>... ptypes) {
611 initCount++;
612 return method(INIT.symbolName(), void.class, ptypes);
613 }
614
615 /**
616 * Start generating an <init>(...)V method in the class.
617 *
618 * @param flags access flags for the constructor
619 * @param ptypes parameter types for the constructor
620 *
621 * @return method emitter to use for weaving <init>(...)V
622 */
623 MethodEmitter init(final EnumSet<Flag> flags, final Class<?>... ptypes) {
624 initCount++;
625 return method(flags, INIT.symbolName(), void.class, ptypes);
626 }
627
628 /**
629 * Add a field to the class, initialized to a value.
630 *
631 * @param fieldFlags flags, e.g. should it be static or public etc
632 * @param fieldName name of field
633 * @param fieldType the type of the field
634 * @param value the value
635 *
636 * @see ClassEmitter.Flag
637 */
638 final void field(final EnumSet<Flag> fieldFlags, final String fieldName, final Class<?> fieldType, final Object value) {
639 fieldCount++;
640 cw.visitField(Flag.getValue(fieldFlags), fieldName, typeDescriptor(fieldType), null, value).visitEnd();
641 }
642
643 /**
644 * Add a field to the class.
645 *
646 * @param fieldFlags access flags for the field
647 * @param fieldName name of field
648 * @param fieldType type of the field
649 *
650 * @see ClassEmitter.Flag
651 */
652 final void field(final EnumSet<Flag> fieldFlags, final String fieldName, final Class<?> fieldType) {
653 field(fieldFlags, fieldName, fieldType, null);
654 }
655
656 /**
657 * Add a field to the class - defaults to public.
658 *
659 * @param fieldName name of field
660 * @param fieldType type of field
661 */
662 final void field(final String fieldName, final Class<?> fieldType) {
663 field(EnumSet.of(Flag.PUBLIC), fieldName, fieldType, null);
664 }
665
666 /**
667 * Return a bytecode array from this ClassEmitter. The ClassEmitter must
668 * have been ended (having its end function called) for this to work.
669 *
670 * @return byte code array for generated class, {@code null} if class
671 * generation hasn't been ended with {@link ClassEmitter#end()}.
672 */
673 byte[] toByteArray() {
674 assert classEnded;
675 if (!classEnded) {
676 return null;
677 }
678
679 return cw.toByteArray();
680 }
681
682 /**
683 * Abstraction for flags used in class emission. We provide abstraction
684 * separating these from the underlying bytecode emitter. Flags are provided
685 * for method handles, protection levels, static/virtual fields/methods.
686 */
687 static enum Flag {
688 /** method handle with static access */
689 HANDLE_STATIC(H_INVOKESTATIC),
690 /** method handle with new invoke special access */
691 HANDLE_NEWSPECIAL(H_NEWINVOKESPECIAL),
692 /** method handle with invoke special access */
693 HANDLE_SPECIAL(H_INVOKESPECIAL),
694 /** method handle with invoke virtual access */
695 HANDLE_VIRTUAL(H_INVOKEVIRTUAL),
696 /** method handle with invoke interface access */
697 HANDLE_INTERFACE(H_INVOKEINTERFACE),
698
699 /** final access */
700 FINAL(ACC_FINAL),
701 /** static access */
702 STATIC(ACC_STATIC),
703 /** public access */
704 PUBLIC(ACC_PUBLIC),
705 /** private access */
706 PRIVATE(ACC_PRIVATE);
707
708 private int value;
709
710 private Flag(final int value) {
711 this.value = value;
712 }
713
714 /**
715 * Get the value of this flag
716 * @return the int value
717 */
718 int getValue() {
719 return value;
720 }
721
722 /**
723 * Return the corresponding ASM flag value for an enum set of flags.
724 *
725 * @param flags enum set of flags
726 *
727 * @return an integer value representing the flags intrinsic values
728 * or:ed together
729 */
730 static int getValue(final EnumSet<Flag> flags) {
731 int v = 0;
732 for (final Flag flag : flags) {
733 v |= flag.getValue();
734 }
735 return v;
736 }
737 }
738
739 private MethodVisitor methodVisitor(final EnumSet<Flag> flags, final String methodName, final Class<?> rtype, final Class<?>... ptypes) {
740 return cw.visitMethod(Flag.getValue(flags), methodName, methodDescriptor(rtype, ptypes), null, null);
741 }
742
743 }
|