Wed, 27 Apr 2016 01:36:41 +0800
Initial load
http://hg.openjdk.java.net/jdk8u/jdk8u/nashorn/
changeset: 1034:4b9cc65dd24d
tag: jdk8u25-b17
1 /*
2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
26 package jdk.nashorn.internal.codegen;
28 import static jdk.internal.org.objectweb.asm.Opcodes.ATHROW;
29 import static jdk.internal.org.objectweb.asm.Opcodes.CHECKCAST;
30 import static jdk.internal.org.objectweb.asm.Opcodes.DUP2;
31 import static jdk.internal.org.objectweb.asm.Opcodes.GETFIELD;
32 import static jdk.internal.org.objectweb.asm.Opcodes.GETSTATIC;
33 import static jdk.internal.org.objectweb.asm.Opcodes.GOTO;
34 import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKESTATIC;
35 import static jdk.internal.org.objectweb.asm.Opcodes.IFEQ;
36 import static jdk.internal.org.objectweb.asm.Opcodes.IFGE;
37 import static jdk.internal.org.objectweb.asm.Opcodes.IFGT;
38 import static jdk.internal.org.objectweb.asm.Opcodes.IFLE;
39 import static jdk.internal.org.objectweb.asm.Opcodes.IFLT;
40 import static jdk.internal.org.objectweb.asm.Opcodes.IFNE;
41 import static jdk.internal.org.objectweb.asm.Opcodes.IFNONNULL;
42 import static jdk.internal.org.objectweb.asm.Opcodes.IFNULL;
43 import static jdk.internal.org.objectweb.asm.Opcodes.IF_ACMPEQ;
44 import static jdk.internal.org.objectweb.asm.Opcodes.IF_ACMPNE;
45 import static jdk.internal.org.objectweb.asm.Opcodes.IF_ICMPEQ;
46 import static jdk.internal.org.objectweb.asm.Opcodes.IF_ICMPNE;
47 import static jdk.internal.org.objectweb.asm.Opcodes.INSTANCEOF;
48 import static jdk.internal.org.objectweb.asm.Opcodes.INVOKEINTERFACE;
49 import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESPECIAL;
50 import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESTATIC;
51 import static jdk.internal.org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
52 import static jdk.internal.org.objectweb.asm.Opcodes.NEW;
53 import static jdk.internal.org.objectweb.asm.Opcodes.PUTFIELD;
54 import static jdk.internal.org.objectweb.asm.Opcodes.PUTSTATIC;
55 import static jdk.internal.org.objectweb.asm.Opcodes.RETURN;
56 import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
57 import static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS;
58 import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
59 import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
60 import static jdk.nashorn.internal.codegen.CompilerConstants.THIS_DEBUGGER;
61 import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
62 import static jdk.nashorn.internal.codegen.CompilerConstants.className;
63 import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
64 import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor;
65 import static jdk.nashorn.internal.codegen.CompilerConstants.staticField;
66 import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
68 import java.io.PrintStream;
69 import java.lang.reflect.Array;
70 import java.util.EnumSet;
71 import java.util.List;
72 import jdk.internal.dynalink.support.NameCodec;
73 import jdk.internal.org.objectweb.asm.Handle;
74 import jdk.internal.org.objectweb.asm.MethodVisitor;
75 import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
76 import jdk.nashorn.internal.codegen.CompilerConstants.Call;
77 import jdk.nashorn.internal.codegen.CompilerConstants.FieldAccess;
78 import jdk.nashorn.internal.codegen.types.ArrayType;
79 import jdk.nashorn.internal.codegen.types.BitwiseType;
80 import jdk.nashorn.internal.codegen.types.NumericType;
81 import jdk.nashorn.internal.codegen.types.Type;
82 import jdk.nashorn.internal.ir.FunctionNode;
83 import jdk.nashorn.internal.ir.IdentNode;
84 import jdk.nashorn.internal.ir.LexicalContext;
85 import jdk.nashorn.internal.ir.LiteralNode;
86 import jdk.nashorn.internal.ir.RuntimeNode;
87 import jdk.nashorn.internal.ir.Symbol;
88 import jdk.nashorn.internal.runtime.ArgumentSetter;
89 import jdk.nashorn.internal.runtime.Debug;
90 import jdk.nashorn.internal.runtime.DebugLogger;
91 import jdk.nashorn.internal.runtime.JSType;
92 import jdk.nashorn.internal.runtime.ScriptEnvironment;
93 import jdk.nashorn.internal.runtime.ScriptObject;
94 import jdk.nashorn.internal.runtime.linker.Bootstrap;
95 import jdk.nashorn.internal.runtime.options.Options;
97 /**
98 * This is the main function responsible for emitting method code
99 * in a class. It maintains a type stack and keeps track of control
100 * flow to make sure that the registered instructions don't violate
101 * byte code verification.
102 *
103 * Running Nashorn with -ea will assert as soon as a type stack
104 * becomes corrupt, for easier debugging
105 *
106 * Running Nashorn with -Dnashorn.codegen.debug=true will print
107 * all generated bytecode and labels to stderr, for easier debugging,
108 * including bytecode stack contents
109 */
110 public class MethodEmitter implements Emitter {
111 /** The ASM MethodVisitor we are plugged into */
112 private final MethodVisitor method;
114 /** Current type stack for current evaluation */
115 private Label.Stack stack;
117 /** Parent classEmitter representing the class of this method */
118 private final ClassEmitter classEmitter;
120 /** FunctionNode representing this method, or null if none exists */
121 protected FunctionNode functionNode;
123 /** Check whether this emitter ever has a function return point */
124 private boolean hasReturn;
126 /** The script environment */
127 private final ScriptEnvironment env;
129 /** Threshold in chars for when string constants should be split */
130 static final int LARGE_STRING_THRESHOLD = 32 * 1024;
132 /** Debug flag, should we dump all generated bytecode along with stacks? */
133 private static final DebugLogger LOG = new DebugLogger("codegen", "nashorn.codegen.debug");
134 private static final boolean DEBUG = LOG.isEnabled();
136 /** dump stack on a particular line, or -1 if disabled */
137 private static final int DEBUG_TRACE_LINE;
139 static {
140 final String tl = Options.getStringProperty("nashorn.codegen.debug.trace", "-1");
141 int line = -1;
142 try {
143 line = Integer.parseInt(tl);
144 } catch (final NumberFormatException e) {
145 //fallthru
146 }
147 DEBUG_TRACE_LINE = line;
148 }
150 /** Bootstrap for normal indy:s */
151 private static final Handle LINKERBOOTSTRAP = new Handle(H_INVOKESTATIC, Bootstrap.BOOTSTRAP.className(), Bootstrap.BOOTSTRAP.name(), Bootstrap.BOOTSTRAP.descriptor());
153 /** Bootstrap for runtime node indy:s */
154 private static final Handle RUNTIMEBOOTSTRAP = new Handle(H_INVOKESTATIC, RuntimeCallSite.BOOTSTRAP.className(), RuntimeCallSite.BOOTSTRAP.name(), RuntimeCallSite.BOOTSTRAP.descriptor());
156 /**
157 * Constructor - internal use from ClassEmitter only
158 * @see ClassEmitter#method
159 *
160 * @param classEmitter the class emitter weaving the class this method is in
161 * @param method a method visitor
162 */
163 MethodEmitter(final ClassEmitter classEmitter, final MethodVisitor method) {
164 this(classEmitter, method, null);
165 }
167 /**
168 * Constructor - internal use from ClassEmitter only
169 * @see ClassEmitter#method
170 *
171 * @param classEmitter the class emitter weaving the class this method is in
172 * @param method a method visitor
173 * @param functionNode a function node representing this method
174 */
175 MethodEmitter(final ClassEmitter classEmitter, final MethodVisitor method, final FunctionNode functionNode) {
176 this.env = classEmitter.getEnv();
177 this.classEmitter = classEmitter;
178 this.method = method;
179 this.functionNode = functionNode;
180 this.stack = null;
181 }
183 /**
184 * Begin a method
185 * @see Emitter
186 */
187 @Override
188 public void begin() {
189 classEmitter.beginMethod(this);
190 newStack();
191 method.visitCode();
192 }
194 /**
195 * End a method
196 * @see Emitter
197 */
198 @Override
199 public void end() {
200 method.visitMaxs(0, 0);
201 method.visitEnd();
203 classEmitter.endMethod(this);
204 }
206 private void newStack() {
207 stack = new Label.Stack();
208 }
210 @Override
211 public String toString() {
212 return "methodEmitter: " + (functionNode == null ? method : functionNode.getName()).toString() + ' ' + Debug.id(this);
213 }
215 /**
216 * Push a type to the existing stack
217 * @param type the type
218 */
219 private void pushType(final Type type) {
220 if (type != null) {
221 stack.push(type);
222 }
223 }
225 /**
226 * Pop a type from the existing stack
227 *
228 * @param expected expected type - will assert if wrong
229 *
230 * @return the type that was retrieved
231 */
232 private Type popType(final Type expected) {
233 final Type type = stack.pop();
234 assert type.isObject() && expected.isObject() ||
235 type.isEquivalentTo(expected) : type + " is not compatible with " + expected;
236 return type;
237 }
239 /**
240 * Pop a type from the existing stack, no matter what it is.
241 *
242 * @return the type
243 */
244 private Type popType() {
245 return stack.pop();
246 }
248 /**
249 * Pop a type from the existing stack, ensuring that it is numeric,
250 * assert if not
251 *
252 * @return the type
253 */
254 private NumericType popNumeric() {
255 final Type type = stack.pop();
256 assert type.isNumeric() : type + " is not numeric";
257 return (NumericType)type;
258 }
260 /**
261 * Pop a type from the existing stack, ensuring that it is an integer type
262 * (integer or long), assert if not
263 *
264 * @return the type
265 */
266 private BitwiseType popInteger() {
267 final Type type = stack.pop();
268 assert type.isInteger() || type.isLong() : type + " is not an integer or long";
269 return (BitwiseType)type;
270 }
272 /**
273 * Pop a type from the existing stack, ensuring that it is an array type,
274 * assert if not
275 *
276 * @return the type
277 */
278 private ArrayType popArray() {
279 final Type type = stack.pop();
280 assert type.isArray() : type;
281 return (ArrayType)type;
282 }
284 /**
285 * Peek a given number of slots from the top of the stack and return the
286 * type in that slot
287 *
288 * @param pos the number of positions from the top, 0 is the top element
289 *
290 * @return the type at position "pos" on the stack
291 */
292 final Type peekType(final int pos) {
293 return stack.peek(pos);
294 }
296 /**
297 * Peek at the type at the top of the stack
298 *
299 * @return the type at the top of the stack
300 */
301 final Type peekType() {
302 return stack.peek();
303 }
305 /**
306 * Generate code a for instantiating a new object and push the
307 * object type on the stack
308 *
309 * @param classDescriptor class descriptor for the object type
310 *
311 * @return the method emitter
312 */
313 MethodEmitter _new(final String classDescriptor) {
314 debug("new", classDescriptor);
315 method.visitTypeInsn(NEW, classDescriptor);
316 pushType(Type.OBJECT);
317 return this;
318 }
320 /**
321 * Generate code a for instantiating a new object and push the
322 * object type on the stack
323 *
324 * @param clazz class type to instatiate
325 *
326 * @return the method emitter
327 */
328 MethodEmitter _new(final Class<?> clazz) {
329 return _new(className(clazz));
330 }
332 /**
333 * Generate code to call the empty constructor for a class
334 *
335 * @param clazz class type to instatiate
336 *
337 * @return the method emitter
338 */
339 MethodEmitter newInstance(final Class<?> clazz) {
340 return invoke(constructorNoLookup(clazz));
341 }
343 /**
344 * Perform a dup, that is, duplicate the top element and
345 * push the duplicate down a given number of positions
346 * on the stack. This is totally type agnostic.
347 *
348 * @param depth the depth on which to put the copy
349 *
350 * @return the method emitter, or null if depth is illegal and
351 * has no instruction equivalent.
352 */
353 MethodEmitter dup(final int depth) {
354 if (peekType().dup(method, depth) == null) {
355 return null;
356 }
358 debug("dup", depth);
360 switch (depth) {
361 case 0:
362 pushType(peekType());
363 break;
364 case 1: {
365 final Type p0 = popType();
366 final Type p1 = popType();
367 pushType(p0);
368 pushType(p1);
369 pushType(p0);
370 break;
371 }
372 case 2: {
373 final Type p0 = popType();
374 final Type p1 = popType();
375 final Type p2 = popType();
376 pushType(p0);
377 pushType(p2);
378 pushType(p1);
379 pushType(p0);
380 break;
381 }
382 default:
383 assert false : "illegal dup depth = " + depth;
384 return null;
385 }
387 return this;
388 }
390 /**
391 * Perform a dup2, that is, duplicate the top element if it
392 * is a category 2 type, or two top elements if they are category
393 * 1 types, and push them on top of the stack
394 *
395 * @return the method emitter
396 */
397 MethodEmitter dup2() {
398 debug("dup2");
400 if (peekType().isCategory2()) {
401 pushType(peekType());
402 } else {
403 final Type type = get2();
404 pushType(type);
405 pushType(type);
406 pushType(type);
407 pushType(type);
408 }
409 method.visitInsn(DUP2);
410 return this;
411 }
413 /**
414 * Duplicate the top element on the stack and push it
415 *
416 * @return the method emitter
417 */
418 MethodEmitter dup() {
419 return dup(0);
420 }
422 /**
423 * Pop the top element of the stack and throw it away
424 *
425 * @return the method emitter
426 */
427 MethodEmitter pop() {
428 debug("pop", peekType());
429 popType().pop(method);
430 return this;
431 }
433 /**
434 * Pop the top element of the stack if category 2 type, or the two
435 * top elements of the stack if category 1 types
436 *
437 * @return the method emitter
438 */
439 MethodEmitter pop2() {
440 if (peekType().isCategory2()) {
441 popType();
442 } else {
443 get2n();
444 }
445 return this;
446 }
448 /**
449 * Swap the top two elements of the stack. This is totally
450 * type agnostic and works for all types
451 *
452 * @return the method emitter
453 */
454 MethodEmitter swap() {
455 debug("swap");
457 final Type p0 = popType();
458 final Type p1 = popType();
459 p0.swap(method, p1);
461 pushType(p0);
462 pushType(p1);
463 debug("after ", p0, p1);
464 return this;
465 }
467 /**
468 * Add a local variable. This is a nop if the symbol has no slot
469 *
470 * @param symbol symbol for the local variable
471 * @param start start of scope
472 * @param end end of scope
473 */
474 void localVariable(final Symbol symbol, final Label start, final Label end) {
475 if (!symbol.hasSlot()) {
476 return;
477 }
479 String name = symbol.getName();
481 if (name.equals(THIS.symbolName())) {
482 name = THIS_DEBUGGER.symbolName();
483 }
485 method.visitLocalVariable(name, symbol.getSymbolType().getDescriptor(), null, start.getLabel(), end.getLabel(), symbol.getSlot());
486 }
488 /**
489 * Create a new string builder, call the constructor and push the instance to the stack.
490 *
491 * @return the method emitter
492 */
493 MethodEmitter newStringBuilder() {
494 return invoke(constructorNoLookup(StringBuilder.class)).dup();
495 }
497 /**
498 * Pop a string and a StringBuilder from the top of the stack and call the append
499 * function of the StringBuilder, appending the string. Pushes the StringBuilder to
500 * the stack when finished.
501 *
502 * @return the method emitter
503 */
504 MethodEmitter stringBuilderAppend() {
505 convert(Type.STRING);
506 return invoke(virtualCallNoLookup(StringBuilder.class, "append", StringBuilder.class, String.class));
507 }
509 /**
510 * Pops two integer types from the stack, performs a bitwise and and pushes
511 * the result
512 *
513 * @return the method emitter
514 */
515 MethodEmitter and() {
516 debug("and");
517 pushType(get2i().and(method));
518 return this;
519 }
521 /**
522 * Pops two integer types from the stack, performs a bitwise or and pushes
523 * the result
524 *
525 * @return the method emitter
526 */
527 MethodEmitter or() {
528 debug("or");
529 pushType(get2i().or(method));
530 return this;
531 }
533 /**
534 * Pops two integer types from the stack, performs a bitwise xor and pushes
535 * the result
536 *
537 * @return the method emitter
538 */
539 MethodEmitter xor() {
540 debug("xor");
541 pushType(get2i().xor(method));
542 return this;
543 }
545 /**
546 * Pops two integer types from the stack, performs a bitwise logic shift right and pushes
547 * the result. The shift count, the first element, must be INT.
548 *
549 * @return the method emitter
550 */
551 MethodEmitter shr() {
552 debug("shr");
553 popType(Type.INT);
554 pushType(popInteger().shr(method));
555 return this;
556 }
558 /**
559 * Pops two integer types from the stack, performs a bitwise shift left and and pushes
560 * the result. The shift count, the first element, must be INT.
561 *
562 * @return the method emitter
563 */
564 MethodEmitter shl() {
565 debug("shl");
566 popType(Type.INT);
567 pushType(popInteger().shl(method));
568 return this;
569 }
571 /**
572 * Pops two integer types from the stack, performs a bitwise arithetic shift right and pushes
573 * the result. The shift count, the first element, must be INT.
574 *
575 * @return the method emitter
576 */
577 MethodEmitter sar() {
578 debug("sar");
579 popType(Type.INT);
580 pushType(popInteger().sar(method));
581 return this;
582 }
584 /**
585 * Pops a numeric type from the stack, negates it and pushes the result
586 *
587 * @return the method emitter
588 */
589 MethodEmitter neg() {
590 debug("neg");
591 pushType(popNumeric().neg(method));
592 return this;
593 }
595 /**
596 * Add label for the start of a catch block and push the exception to the
597 * stack
598 *
599 * @param recovery label pointing to start of catch block
600 */
601 void _catch(final Label recovery) {
602 stack.clear();
603 stack.push(Type.OBJECT);
604 label(recovery);
605 }
607 /**
608 * Start a try/catch block.
609 *
610 * @param entry start label for try
611 * @param exit end label for try
612 * @param recovery start label for catch
613 * @param typeDescriptor type descriptor for exception
614 */
615 void _try(final Label entry, final Label exit, final Label recovery, final String typeDescriptor) {
616 method.visitTryCatchBlock(entry.getLabel(), exit.getLabel(), recovery.getLabel(), typeDescriptor);
617 }
619 /**
620 * Start a try/catch block.
621 *
622 * @param entry start label for try
623 * @param exit end label for try
624 * @param recovery start label for catch
625 * @param clazz exception class
626 */
627 void _try(final Label entry, final Label exit, final Label recovery, final Class<?> clazz) {
628 method.visitTryCatchBlock(entry.getLabel(), exit.getLabel(), recovery.getLabel(), CompilerConstants.className(clazz));
629 }
631 /**
632 * Start a try/catch block. The catch is "Throwable" - i.e. catch-all
633 *
634 * @param entry start label for try
635 * @param exit end label for try
636 * @param recovery start label for catch
637 */
638 void _try(final Label entry, final Label exit, final Label recovery) {
639 _try(entry, exit, recovery, (String)null);
640 }
643 /**
644 * Load the constants array
645 * @return this method emitter
646 */
647 MethodEmitter loadConstants() {
648 getStatic(classEmitter.getUnitClassName(), CONSTANTS.symbolName(), CONSTANTS.descriptor());
649 assert peekType().isArray() : peekType();
650 return this;
651 }
653 /**
654 * Push the undefined value for the given type, i.e.
655 * UNDEFINED or UNDEFINEDNUMBER. Currently we have no way of
656 * representing UNDEFINED for INTs and LONGs, so they are not
657 * allowed to be local variables (yet)
658 *
659 * @param type the type for which to push UNDEFINED
660 * @return the method emitter
661 */
662 MethodEmitter loadUndefined(final Type type) {
663 debug("load undefined ", type);
664 pushType(type.loadUndefined(method));
665 return this;
666 }
668 /**
669 * Push the empty value for the given type, i.e. EMPTY.
670 *
671 * @param type the type
672 * @return the method emitter
673 */
674 MethodEmitter loadEmpty(final Type type) {
675 debug("load empty ", type);
676 pushType(type.loadEmpty(method));
677 return this;
678 }
680 /**
681 * Push null to stack
682 *
683 * @return the method emitter
684 */
685 MethodEmitter loadNull() {
686 debug("aconst_null");
687 pushType(Type.OBJECT.ldc(method, null));
688 return this;
689 }
691 /**
692 * Push a handle representing this class top stack
693 *
694 * @param className name of the class
695 *
696 * @return the method emitter
697 */
698 MethodEmitter loadType(final String className) {
699 debug("load type", className);
700 method.visitLdcInsn(jdk.internal.org.objectweb.asm.Type.getObjectType(className));
701 pushType(Type.OBJECT);
702 return this;
703 }
705 /**
706 * Push a boolean constant to the stack.
707 *
708 * @param b value of boolean
709 *
710 * @return the method emitter
711 */
712 MethodEmitter load(final boolean b) {
713 debug("load boolean", b);
714 pushType(Type.BOOLEAN.ldc(method, b));
715 return this;
716 }
718 /**
719 * Push an int constant to the stack
720 *
721 * @param i value of the int
722 *
723 * @return the method emitter
724 */
725 MethodEmitter load(final int i) {
726 debug("load int", i);
727 pushType(Type.INT.ldc(method, i));
728 return this;
729 }
731 /**
732 * Push a double constant to the stack
733 *
734 * @param d value of the double
735 *
736 * @return the method emitter
737 */
738 MethodEmitter load(final double d) {
739 debug("load double", d);
740 pushType(Type.NUMBER.ldc(method, d));
741 return this;
742 }
744 /**
745 * Push an long constant to the stack
746 *
747 * @param l value of the long
748 *
749 * @return the method emitter
750 */
751 MethodEmitter load(final long l) {
752 debug("load long", l);
753 pushType(Type.LONG.ldc(method, l));
754 return this;
755 }
757 /**
758 * Fetch the length of an array.
759 * @return Array length.
760 */
761 MethodEmitter arraylength() {
762 debug("arraylength");
763 popType(Type.OBJECT);
764 pushType(Type.OBJECT_ARRAY.arraylength(method));
765 return this;
766 }
768 /**
769 * Push a String constant to the stack
770 *
771 * @param s value of the String
772 *
773 * @return the method emitter
774 */
775 MethodEmitter load(final String s) {
776 debug("load string", s);
778 if (s == null) {
779 loadNull();
780 return this;
781 }
783 //NASHORN-142 - split too large string
784 final int length = s.length();
785 if (length > LARGE_STRING_THRESHOLD) {
787 _new(StringBuilder.class);
788 dup();
789 load(length);
790 invoke(constructorNoLookup(StringBuilder.class, int.class));
792 for (int n = 0; n < length; n += LARGE_STRING_THRESHOLD) {
793 final String part = s.substring(n, Math.min(n + LARGE_STRING_THRESHOLD, length));
794 load(part);
795 stringBuilderAppend();
796 }
798 invoke(virtualCallNoLookup(StringBuilder.class, "toString", String.class));
800 return this;
801 }
803 pushType(Type.OBJECT.ldc(method, s));
804 return this;
805 }
807 /**
808 * Push a local variable to the stack. If the symbol representing
809 * the local variable doesn't have a slot, this is a NOP
810 *
811 * @param symbol the symbol representing the local variable.
812 *
813 * @return the method emitter
814 */
815 MethodEmitter load(final Symbol symbol) {
816 assert symbol != null;
817 if (symbol.hasSlot()) {
818 final int slot = symbol.getSlot();
819 debug("load symbol", symbol.getName(), " slot=", slot);
820 final Type type = symbol.getSymbolType().load(method, slot);
821 pushType(type == Type.OBJECT && symbol.isThis() ? Type.THIS : type);
822 } else if (symbol.isParam()) {
823 assert !symbol.isScope();
824 assert functionNode.isVarArg() : "Non-vararg functions have slotted parameters";
825 final int index = symbol.getFieldIndex();
826 if (functionNode.needsArguments()) {
827 // ScriptObject.getArgument(int) on arguments
828 debug("load symbol", symbol.getName(), " arguments index=", index);
829 loadCompilerConstant(ARGUMENTS);
830 load(index);
831 ScriptObject.GET_ARGUMENT.invoke(this);
832 } else {
833 // array load from __varargs__
834 debug("load symbol", symbol.getName(), " array index=", index);
835 loadCompilerConstant(VARARGS);
836 load(symbol.getFieldIndex());
837 arrayload();
838 }
839 }
840 return this;
841 }
843 /**
844 * Push a local variable to the stack, given an explicit bytecode slot
845 * This is used e.g. for stub generation where we know where items like
846 * "this" and "scope" reside.
847 *
848 * @param type the type of the variable
849 * @param slot the slot the variable is in
850 *
851 * @return the method emitter
852 */
853 MethodEmitter load(final Type type, final int slot) {
854 debug("explicit load", type, slot);
855 final Type loadType = type.load(method, slot);
856 pushType(loadType == Type.OBJECT && isThisSlot(slot) ? Type.THIS : loadType);
857 return this;
858 }
860 private boolean isThisSlot(final int slot) {
861 if (functionNode == null) {
862 return slot == CompilerConstants.JAVA_THIS.slot();
863 }
864 final int thisSlot = compilerConstant(THIS).getSlot();
865 assert !functionNode.needsCallee() || thisSlot == 1; // needsCallee -> thisSlot == 1
866 assert functionNode.needsCallee() || thisSlot == 0; // !needsCallee -> thisSlot == 0
867 return slot == thisSlot;
868 }
870 /**
871 * Push a method handle to the stack
872 *
873 * @param className class name
874 * @param methodName method name
875 * @param descName descriptor
876 * @param flags flags that describe this handle, e.g. invokespecial new, or invoke virtual
877 *
878 * @return the method emitter
879 */
880 MethodEmitter loadHandle(final String className, final String methodName, final String descName, final EnumSet<Flag> flags) {
881 debug("load handle ");
882 pushType(Type.OBJECT.ldc(method, new Handle(Flag.getValue(flags), className, methodName, descName)));
883 return this;
884 }
886 private Symbol compilerConstant(final CompilerConstants cc) {
887 return functionNode.getBody().getExistingSymbol(cc.symbolName());
888 }
890 /**
891 * True if this method has a slot allocated for the scope variable (meaning, something in the method actually needs
892 * the scope).
893 * @return if this method has a slot allocated for the scope variable.
894 */
895 boolean hasScope() {
896 return compilerConstant(SCOPE).hasSlot();
897 }
899 MethodEmitter loadCompilerConstant(final CompilerConstants cc) {
900 final Symbol symbol = compilerConstant(cc);
901 if (cc == SCOPE && peekType() == Type.SCOPE) {
902 dup();
903 return this;
904 }
905 return load(symbol);
906 }
908 void storeCompilerConstant(final CompilerConstants cc) {
909 final Symbol symbol = compilerConstant(cc);
910 debug("store compiler constant ", symbol);
911 store(symbol);
912 }
914 /**
915 * Load an element from an array, determining type automatically
916 * @return the method emitter
917 */
918 MethodEmitter arrayload() {
919 debug("Xaload");
920 popType(Type.INT);
921 pushType(popArray().aload(method));
922 return this;
923 }
925 /**
926 * Pop a value, an index and an array from the stack and store
927 * the value at the given index in the array.
928 */
929 void arraystore() {
930 debug("Xastore");
931 final Type value = popType();
932 final Type index = popType(Type.INT);
933 assert index.isInteger() : "array index is not integer, but " + index;
934 final ArrayType array = popArray();
936 assert value.isEquivalentTo(array.getElementType()) : "Storing "+value+" into "+array;
937 assert array.isObject();
938 array.astore(method);
939 }
941 /**
942 * Pop a value from the stack and store it in a local variable represented
943 * by the given symbol. If the symbol has no slot, this is a NOP
944 *
945 * @param symbol symbol to store stack to
946 */
947 void store(final Symbol symbol) {
948 assert symbol != null : "No symbol to store";
949 if (symbol.hasSlot()) {
950 final int slot = symbol.getSlot();
951 debug("store symbol", symbol.getName(), " slot=", slot);
952 popType(symbol.getSymbolType()).store(method, slot);
953 } else if (symbol.isParam()) {
954 assert !symbol.isScope();
955 assert functionNode.isVarArg() : "Non-vararg functions have slotted parameters";
956 final int index = symbol.getFieldIndex();
957 if (functionNode.needsArguments()) {
958 debug("store symbol", symbol.getName(), " arguments index=", index);
959 loadCompilerConstant(ARGUMENTS);
960 load(index);
961 ArgumentSetter.SET_ARGUMENT.invoke(this);
962 } else {
963 // varargs without arguments object - just do array store to __varargs__
964 debug("store symbol", symbol.getName(), " array index=", index);
965 loadCompilerConstant(VARARGS);
966 load(index);
967 ArgumentSetter.SET_ARRAY_ELEMENT.invoke(this);
968 }
969 }
970 }
972 /**
973 * Pop a value from the stack and store it in a given local variable
974 * slot.
975 *
976 * @param type the type to pop
977 * @param slot the slot
978 */
979 void store(final Type type, final int slot) {
980 popType(type);
981 type.store(method, slot);
982 }
984 /**
985 * Increment/Decrement a local integer by the given value.
986 *
987 * @param slot the int slot
988 * @param increment the amount to increment
989 */
990 void iinc(final int slot, final int increment) {
991 debug("iinc");
992 method.visitIincInsn(slot, increment);
993 }
995 /**
996 * Pop an exception object from the stack and generate code
997 * for throwing it
998 */
999 public void athrow() {
1000 debug("athrow");
1001 final Type receiver = popType(Type.OBJECT);
1002 assert receiver.isObject();
1003 method.visitInsn(ATHROW);
1004 stack = null;
1005 }
1007 /**
1008 * Pop an object from the stack and perform an instanceof
1009 * operation, given a classDescriptor to compare it to.
1010 * Push the boolean result 1/0 as an int to the stack
1011 *
1012 * @param classDescriptor descriptor of the class to type check against
1013 *
1014 * @return the method emitter
1015 */
1016 MethodEmitter _instanceof(final String classDescriptor) {
1017 debug("instanceof", classDescriptor);
1018 popType(Type.OBJECT);
1019 method.visitTypeInsn(INSTANCEOF, classDescriptor);
1020 pushType(Type.INT);
1021 return this;
1022 }
1024 /**
1025 * Pop an object from the stack and perform an instanceof
1026 * operation, given a classDescriptor to compare it to.
1027 * Push the boolean result 1/0 as an int to the stack
1028 *
1029 * @param clazz the type to check instanceof against
1030 *
1031 * @return the method emitter
1032 */
1033 MethodEmitter _instanceof(final Class<?> clazz) {
1034 return _instanceof(CompilerConstants.className(clazz));
1035 }
1037 /**
1038 * Perform a checkcast operation on the object at the top of the
1039 * stack.
1040 *
1041 * @param classDescriptor descriptor of the class to type check against
1042 *
1043 * @return the method emitter
1044 */
1045 MethodEmitter checkcast(final String classDescriptor) {
1046 debug("checkcast", classDescriptor);
1047 assert peekType().isObject();
1048 method.visitTypeInsn(CHECKCAST, classDescriptor);
1049 return this;
1050 }
1052 /**
1053 * Perform a checkcast operation on the object at the top of the
1054 * stack.
1055 *
1056 * @param clazz class to checkcast against
1057 *
1058 * @return the method emitter
1059 */
1060 MethodEmitter checkcast(final Class<?> clazz) {
1061 return checkcast(CompilerConstants.className(clazz));
1062 }
1064 /**
1065 * Instantiate a new array given a length that is popped
1066 * from the stack and the array type
1067 *
1068 * @param arrayType the type of the array
1069 *
1070 * @return the method emitter
1071 */
1072 MethodEmitter newarray(final ArrayType arrayType) {
1073 debug("newarray ", "arrayType=", arrayType);
1074 popType(Type.INT); //LENGTH
1075 pushType(arrayType.newarray(method));
1076 return this;
1077 }
1079 /**
1080 * Instantiate a multidimensional array with a given number of dimensions.
1081 * On the stack are dim lengths of the sub arrays.
1082 *
1083 * @param arrayType type of the array
1084 * @param dims number of dimensions
1085 *
1086 * @return the method emitter
1087 */
1088 MethodEmitter multinewarray(final ArrayType arrayType, final int dims) {
1089 debug("multianewarray ", arrayType, dims);
1090 for (int i = 0; i < dims; i++) {
1091 popType(Type.INT); //LENGTH
1092 }
1093 pushType(arrayType.newarray(method, dims));
1094 return this;
1095 }
1097 /**
1098 * Helper function to pop and type check the appropriate arguments
1099 * from the stack given a method signature
1100 *
1101 * @param signature method signature
1102 *
1103 * @return return type of method
1104 */
1105 private Type fixParamStack(final String signature) {
1106 final Type[] params = Type.getMethodArguments(signature);
1107 for (int i = params.length - 1; i >= 0; i--) {
1108 popType(params[i]);
1109 }
1110 final Type returnType = Type.getMethodReturnType(signature);
1111 return returnType;
1112 }
1114 /**
1115 * Generate an invocation to a Call structure
1116 * @see CompilerConstants
1117 *
1118 * @param call the call object
1119 *
1120 * @return the method emitter
1121 */
1122 MethodEmitter invoke(final Call call) {
1123 return call.invoke(this);
1124 }
1126 private MethodEmitter invoke(final int opcode, final String className, final String methodName, final String methodDescriptor, final boolean hasReceiver) {
1127 final Type returnType = fixParamStack(methodDescriptor);
1129 if (hasReceiver) {
1130 popType(Type.OBJECT);
1131 }
1133 if (opcode == INVOKEINTERFACE) {
1134 method.visitMethodInsn(opcode, className, methodName, methodDescriptor, true);
1135 } else {
1136 method.visitMethodInsn(opcode, className, methodName, methodDescriptor, false);
1137 }
1139 if (returnType != null) {
1140 pushType(returnType);
1141 }
1143 return this;
1144 }
1146 /**
1147 * Pop receiver from stack, perform an invoke special
1148 *
1149 * @param className class name
1150 * @param methodName method name
1151 * @param methodDescriptor descriptor
1152 *
1153 * @return the method emitter
1154 */
1155 MethodEmitter invokespecial(final String className, final String methodName, final String methodDescriptor) {
1156 debug("invokespecial", className, ".", methodName, methodDescriptor);
1157 return invoke(INVOKESPECIAL, className, methodName, methodDescriptor, true);
1158 }
1160 /**
1161 * Pop receiver from stack, perform an invoke virtual, push return value if any
1162 *
1163 * @param className class name
1164 * @param methodName method name
1165 * @param methodDescriptor descriptor
1166 *
1167 * @return the method emitter
1168 */
1169 MethodEmitter invokevirtual(final String className, final String methodName, final String methodDescriptor) {
1170 debug("invokevirtual", className, ".", methodName, methodDescriptor, " ", stack);
1171 return invoke(INVOKEVIRTUAL, className, methodName, methodDescriptor, true);
1172 }
1174 /**
1175 * Perform an invoke static and push the return value if any
1176 *
1177 * @param className class name
1178 * @param methodName method name
1179 * @param methodDescriptor descriptor
1180 *
1181 * @return the method emitter
1182 */
1183 MethodEmitter invokestatic(final String className, final String methodName, final String methodDescriptor) {
1184 debug("invokestatic", className, ".", methodName, methodDescriptor);
1185 invoke(INVOKESTATIC, className, methodName, methodDescriptor, false);
1186 return this;
1187 }
1189 /**
1190 * Perform an invoke static and replace the return type if we know better, e.g. Global.allocate
1191 * that allocates an array should return an ObjectArray type as a NativeArray counts as that
1192 *
1193 * @param className class name
1194 * @param methodName method name
1195 * @param methodDescriptor descriptor
1196 * @param returnType return type override
1197 *
1198 * @return the method emitter
1199 */
1200 MethodEmitter invokeStatic(final String className, final String methodName, final String methodDescriptor, final Type returnType) {
1201 invokestatic(className, methodName, methodDescriptor);
1202 popType();
1203 pushType(returnType);
1204 return this;
1205 }
1207 /**
1208 * Pop receiver from stack, perform an invoke interface and push return value if any
1209 *
1210 * @param className class name
1211 * @param methodName method name
1212 * @param methodDescriptor descriptor
1213 *
1214 * @return the method emitter
1215 */
1216 MethodEmitter invokeinterface(final String className, final String methodName, final String methodDescriptor) {
1217 debug("invokeinterface", className, ".", methodName, methodDescriptor);
1218 return invoke(INVOKEINTERFACE, className, methodName, methodDescriptor, true);
1219 }
1221 static jdk.internal.org.objectweb.asm.Label[] getLabels(final Label... table) {
1222 final jdk.internal.org.objectweb.asm.Label[] internalLabels = new jdk.internal.org.objectweb.asm.Label[table.length];
1223 for (int i = 0; i < table.length; i++) {
1224 internalLabels[i] = table[i].getLabel();
1225 }
1226 return internalLabels;
1227 }
1229 /**
1230 * Generate a lookup switch, popping the switch value from the stack
1231 *
1232 * @param defaultLabel default label
1233 * @param values case values for the table
1234 * @param table default label
1235 */
1236 void lookupswitch(final Label defaultLabel, final int[] values, final Label... table) {//Collection<Label> table) {
1237 debug("lookupswitch", peekType());
1238 popType(Type.INT);
1239 method.visitLookupSwitchInsn(defaultLabel.getLabel(), values, getLabels(table));
1240 }
1242 /**
1243 * Generate a table switch
1244 * @param lo low value
1245 * @param hi high value
1246 * @param defaultLabel default label
1247 * @param table label table
1248 */
1249 void tableswitch(final int lo, final int hi, final Label defaultLabel, final Label... table) {
1250 debug("tableswitch", peekType());
1251 popType(Type.INT);
1252 method.visitTableSwitchInsn(lo, hi, defaultLabel.getLabel(), getLabels(table));
1253 }
1255 /**
1256 * Abstraction for performing a conditional jump of any type
1257 *
1258 * @see MethodEmitter.Condition
1259 *
1260 * @param cond the condition to test
1261 * @param trueLabel the destination label is condition is true
1262 */
1263 void conditionalJump(final Condition cond, final Label trueLabel) {
1264 conditionalJump(cond, cond != Condition.GT && cond != Condition.GE, trueLabel);
1265 }
1267 /**
1268 * Abstraction for performing a conditional jump of any type,
1269 * including a dcmpg/dcmpl semantic for doubles.
1270 *
1271 * @param cond the condition to test
1272 * @param isCmpG is this a dcmpg for numbers, false if it's a dcmpl
1273 * @param trueLabel the destination label if condition is true
1274 */
1275 void conditionalJump(final Condition cond, final boolean isCmpG, final Label trueLabel) {
1276 if (peekType().isCategory2()) {
1277 debug("[ld]cmp isCmpG=", isCmpG);
1278 pushType(get2n().cmp(method, isCmpG));
1279 jump(Condition.toUnary(cond), trueLabel, 1);
1280 } else {
1281 debug("if", cond);
1282 jump(Condition.toBinary(cond, peekType().isObject()), trueLabel, 2);
1283 }
1284 }
1286 MethodEmitter registerReturn() {
1287 setHasReturn();
1288 return this;
1289 }
1291 void setHasReturn() {
1292 this.hasReturn = true;
1293 }
1295 /**
1296 * Perform a non void return, popping the type from the stack
1297 *
1298 * @param type the type for the return
1299 */
1300 void _return(final Type type) {
1301 debug("return", type);
1302 assert stack.size() == 1 : "Only return value on stack allowed at return point - depth=" + stack.size() + " stack = " + stack;
1303 final Type stackType = peekType();
1304 if (!Type.areEquivalent(type, stackType)) {
1305 convert(type);
1306 }
1307 popType(type)._return(method);
1308 stack = null;
1309 }
1311 /**
1312 * Perform a return using the stack top value as the guide for the type
1313 */
1314 void _return() {
1315 _return(peekType());
1316 }
1318 /**
1319 * Perform a void return.
1320 */
1321 void returnVoid() {
1322 debug("return [void]");
1323 assert stack.isEmpty() : stack;
1324 method.visitInsn(RETURN);
1325 stack = null;
1326 }
1328 /**
1329 * Goto, possibly when splitting is taking place. If
1330 * a splitNode exists, we need to handle the case that the
1331 * jump target is another method
1332 *
1333 * @param label destination label
1334 */
1335 void splitAwareGoto(final LexicalContext lc, final Label label) {
1336 _goto(label);
1337 }
1339 /**
1340 * Perform a comparison of two number types that are popped from the stack
1341 *
1342 * @param isCmpG is this a dcmpg semantic, false if it's a dcmpl semantic
1343 *
1344 * @return the method emitter
1345 */
1346 MethodEmitter cmp(final boolean isCmpG) {
1347 pushType(get2n().cmp(method, isCmpG));
1348 return this;
1349 }
1351 /**
1352 * Helper function for jumps, conditional or not
1353 * @param opcode opcode for jump
1354 * @param label destination
1355 * @param n elements on stack to compare, 0-2
1356 */
1357 private void jump(final int opcode, final Label label, final int n) {
1358 for (int i = 0; i < n; i++) {
1359 assert peekType().isInteger() || peekType().isBoolean() || peekType().isObject() : "expecting integer type or object for jump, but found " + peekType();
1360 popType();
1361 }
1362 mergeStackTo(label);
1363 method.visitJumpInsn(opcode, label.getLabel());
1364 }
1366 /**
1367 * Generate an if_acmpeq
1368 *
1369 * @param label label to true case
1370 */
1371 void if_acmpeq(final Label label) {
1372 debug("if_acmpeq", label);
1373 jump(IF_ACMPEQ, label, 2);
1374 }
1376 /**
1377 * Generate an if_acmpne
1378 *
1379 * @param label label to true case
1380 */
1381 void if_acmpne(final Label label) {
1382 debug("if_acmpne", label);
1383 jump(IF_ACMPNE, label, 2);
1384 }
1386 /**
1387 * Generate an ifnull
1388 *
1389 * @param label label to true case
1390 */
1391 void ifnull(final Label label) {
1392 debug("ifnull", label);
1393 jump(IFNULL, label, 1);
1394 }
1396 /**
1397 * Generate an ifnonnull
1398 *
1399 * @param label label to true case
1400 */
1401 void ifnonnull(final Label label) {
1402 debug("ifnonnull", label);
1403 jump(IFNONNULL, label, 1);
1404 }
1406 /**
1407 * Generate an ifeq
1408 *
1409 * @param label label to true case
1410 */
1411 void ifeq(final Label label) {
1412 debug("ifeq ", label);
1413 jump(IFEQ, label, 1);
1414 }
1416 /**
1417 * Generate an if_icmpeq
1418 *
1419 * @param label label to true case
1420 */
1421 void if_icmpeq(final Label label) {
1422 debug("if_icmpeq", label);
1423 jump(IF_ICMPEQ, label, 2);
1424 }
1426 /**
1427 * Generate an if_ne
1428 *
1429 * @param label label to true case
1430 */
1431 void ifne(final Label label) {
1432 debug("ifne", label);
1433 jump(IFNE, label, 1);
1434 }
1436 /**
1437 * Generate an if_icmpne
1438 *
1439 * @param label label to true case
1440 */
1441 void if_icmpne(final Label label) {
1442 debug("if_icmpne", label);
1443 jump(IF_ICMPNE, label, 2);
1444 }
1446 /**
1447 * Generate an iflt
1448 *
1449 * @param label label to true case
1450 */
1451 void iflt(final Label label) {
1452 debug("iflt", label);
1453 jump(IFLT, label, 1);
1454 }
1456 /**
1457 * Generate an ifle
1458 *
1459 * @param label label to true case
1460 */
1461 void ifle(final Label label) {
1462 debug("ifle", label);
1463 jump(IFLE, label, 1);
1464 }
1466 /**
1467 * Generate an ifgt
1468 *
1469 * @param label label to true case
1470 */
1471 void ifgt(final Label label) {
1472 debug("ifgt", label);
1473 jump(IFGT, label, 1);
1474 }
1476 /**
1477 * Generate an ifge
1478 *
1479 * @param label label to true case
1480 */
1481 void ifge(final Label label) {
1482 debug("ifge", label);
1483 jump(IFGE, label, 1);
1484 }
1486 /**
1487 * Unconditional jump to a label
1488 *
1489 * @param label destination label
1490 */
1491 void _goto(final Label label) {
1492 //debug("goto", label);
1493 jump(GOTO, label, 0);
1494 stack = null; //whoever reaches the point after us provides the stack, because we don't
1495 }
1497 /**
1498 * Examine two stacks and make sure they are of the same size and their
1499 * contents are equivalent to each other
1500 * @param s0 first stack
1501 * @param s1 second stack
1502 *
1503 * @return true if stacks are equivalent, false otherwise
1504 */
1505 /**
1506 * A join in control flow - helper function that makes sure all entry stacks
1507 * discovered for the join point so far are equivalent
1508 *
1509 * MergeStack: we are about to enter a label. If its stack, label.getStack() is null
1510 * we have never been here before. Then we are expected to carry a stack with us.
1511 *
1512 * @param label label
1513 */
1514 private void mergeStackTo(final Label label) {
1515 //sometimes we can do a merge stack without having a stack - i.e. when jumping ahead to dead code
1516 //see NASHORN-73. So far we had been saved by the line number nodes. This should have been fixed
1517 //by Lower removing everything after an unconditionally executed terminating statement OR a break
1518 //or continue in a block. Previously code left over after breaks and continues was still there
1519 //and caused bytecode to be generated - which crashed on stack not being there, as the merge
1520 //was not in fact preceeded by a visit. Furthermore, this led to ASM putting out its NOP NOP NOP
1521 //ATHROW sequences instead of no code being generated at all. This should now be fixed.
1522 assert stack != null : label + " entered with no stack. deadcode that remains?";
1524 final Label.Stack labelStack = label.getStack();
1525 if (labelStack == null) {
1526 label.setStack(stack.copy());
1527 return;
1528 }
1529 assert stack.isEquivalentTo(labelStack) : "stacks " + stack + " is not equivalent with " + labelStack + " at join point";
1530 }
1532 /**
1533 * Register a new label, enter it here.
1534 *
1535 * @param label the label
1536 */
1537 void label(final Label label) {
1538 /*
1539 * If stack == null, this means that we came here not through a fallthrough.
1540 * E.g. a label after an athrow. Then we create a new stack if one doesn't exist
1541 * for this location already.
1542 */
1543 if (stack == null) {
1544 stack = label.getStack();
1545 if (stack == null) {
1546 newStack();
1547 }
1548 }
1549 debug_label(label);
1551 mergeStackTo(label); //we have to merge our stack to whatever is in the label
1553 method.visitLabel(label.getLabel());
1554 }
1556 /**
1557 * Pop element from stack, convert to given type
1558 *
1559 * @param to type to convert to
1560 *
1561 * @return the method emitter
1562 */
1563 MethodEmitter convert(final Type to) {
1564 final Type type = peekType().convert(method, to);
1565 if (type != null) {
1566 if (!peekType().isEquivalentTo(to)) {
1567 debug("convert", peekType(), "->", to);
1568 }
1569 popType();
1570 pushType(type);
1571 }
1572 return this;
1573 }
1575 /**
1576 * Helper function - expect two types that are equivalent
1577 *
1578 * @return common type
1579 */
1580 private Type get2() {
1581 final Type p0 = popType();
1582 final Type p1 = popType();
1583 assert p0.isEquivalentTo(p1) : "expecting equivalent types on stack but got " + p0 + " and " + p1;
1584 return p0;
1585 }
1587 /**
1588 * Helper function - expect two types that are integer types and equivalent
1589 *
1590 * @return common type
1591 */
1592 private BitwiseType get2i() {
1593 final BitwiseType p0 = popInteger();
1594 final BitwiseType p1 = popInteger();
1595 assert p0.isEquivalentTo(p1) : "expecting equivalent types on stack but got " + p0 + " and " + p1;
1596 return p0;
1597 }
1599 /**
1600 * Helper function - expect two types that are numbers and equivalent
1601 *
1602 * @return common type
1603 */
1604 private NumericType get2n() {
1605 final NumericType p0 = popNumeric();
1606 final NumericType p1 = popNumeric();
1607 assert p0.isEquivalentTo(p1) : "expecting equivalent types on stack but got " + p0 + " and " + p1;
1608 return p0;
1609 }
1611 /**
1612 * Pop two numbers, perform addition and push result
1613 *
1614 * @return the method emitter
1615 */
1616 MethodEmitter add() {
1617 debug("add");
1618 pushType(get2().add(method));
1619 return this;
1620 }
1622 /**
1623 * Pop two numbers, perform subtraction and push result
1624 *
1625 * @return the method emitter
1626 */
1627 MethodEmitter sub() {
1628 debug("sub");
1629 pushType(get2n().sub(method));
1630 return this;
1631 }
1633 /**
1634 * Pop two numbers, perform multiplication and push result
1635 *
1636 * @return the method emitter
1637 */
1638 MethodEmitter mul() {
1639 debug("mul ");
1640 pushType(get2n().mul(method));
1641 return this;
1642 }
1644 /**
1645 * Pop two numbers, perform division and push result
1646 *
1647 * @return the method emitter
1648 */
1649 MethodEmitter div() {
1650 debug("div");
1651 pushType(get2n().div(method));
1652 return this;
1653 }
1655 /**
1656 * Pop two numbers, calculate remainder and push result
1657 *
1658 * @return the method emitter
1659 */
1660 MethodEmitter rem() {
1661 debug("rem");
1662 pushType(get2n().rem(method));
1663 return this;
1664 }
1666 /**
1667 * Retrieve the top <tt>count</tt> types on the stack without modifying it.
1668 *
1669 * @param count number of types to return
1670 * @return array of Types
1671 */
1672 protected Type[] getTypesFromStack(final int count) {
1673 final Type[] types = new Type[count];
1674 int pos = 0;
1675 for (int i = count - 1; i >= 0; i--) {
1676 types[i] = stack.peek(pos++);
1677 }
1679 return types;
1680 }
1682 /**
1683 * Helper function to generate a function signature based on stack contents
1684 * and argument count and return type
1685 *
1686 * @param returnType return type
1687 * @param argCount argument count
1688 *
1689 * @return function signature for stack contents
1690 */
1691 private String getDynamicSignature(final Type returnType, final int argCount) {
1692 final Type[] paramTypes = new Type[argCount];
1694 int pos = 0;
1695 for (int i = argCount - 1; i >= 0; i--) {
1696 paramTypes[i] = stack.peek(pos++);
1697 }
1698 final String descriptor = Type.getMethodDescriptor(returnType, paramTypes);
1699 for (int i = 0; i < argCount; i++) {
1700 popType(paramTypes[argCount - i - 1]);
1701 }
1703 return descriptor;
1704 }
1706 /**
1707 * Generate a dynamic new
1708 *
1709 * @param argCount number of arguments
1710 * @param flags callsite flags
1711 *
1712 * @return the method emitter
1713 */
1714 MethodEmitter dynamicNew(final int argCount, final int flags) {
1715 debug("dynamic_new", "argcount=", argCount);
1716 final String signature = getDynamicSignature(Type.OBJECT, argCount);
1717 method.visitInvokeDynamicInsn("dyn:new", signature, LINKERBOOTSTRAP, flags);
1718 pushType(Type.OBJECT); //TODO fix result type
1719 return this;
1720 }
1722 /**
1723 * Generate a dynamic call
1724 *
1725 * @param returnType return type
1726 * @param argCount number of arguments
1727 * @param flags callsite flags
1728 *
1729 * @return the method emitter
1730 */
1731 MethodEmitter dynamicCall(final Type returnType, final int argCount, final int flags) {
1732 debug("dynamic_call", "args=", argCount, "returnType=", returnType);
1733 final String signature = getDynamicSignature(returnType, argCount); // +1 because the function itself is the 1st parameter for dynamic calls (what you call - call target)
1734 debug(" signature", signature);
1735 method.visitInvokeDynamicInsn("dyn:call", signature, LINKERBOOTSTRAP, flags);
1736 pushType(returnType);
1738 return this;
1739 }
1741 /**
1742 * Generate a dynamic call for a runtime node
1743 *
1744 * @param name tag for the invoke dynamic for this runtime node
1745 * @param returnType return type
1746 * @param request RuntimeNode request
1747 *
1748 * @return the method emitter
1749 */
1750 MethodEmitter dynamicRuntimeCall(final String name, final Type returnType, final RuntimeNode.Request request) {
1751 debug("dynamic_runtime_call", name, "args=", request.getArity(), "returnType=", returnType);
1752 final String signature = getDynamicSignature(returnType, request.getArity());
1753 debug(" signature", signature);
1754 method.visitInvokeDynamicInsn(name, signature, RUNTIMEBOOTSTRAP);
1755 pushType(returnType);
1757 return this;
1758 }
1760 /**
1761 * Generate dynamic getter. Pop scope from stack. Push result
1762 *
1763 * @param valueType type of the value to set
1764 * @param name name of property
1765 * @param flags call site flags
1766 * @param isMethod should it prefer retrieving methods
1767 *
1768 * @return the method emitter
1769 */
1770 MethodEmitter dynamicGet(final Type valueType, final String name, final int flags, final boolean isMethod) {
1771 debug("dynamic_get", name, valueType);
1773 Type type = valueType;
1774 if (type.isObject() || type.isBoolean()) {
1775 type = Type.OBJECT; //promote e.g strings to object generic setter
1776 }
1778 popType(Type.SCOPE);
1779 method.visitInvokeDynamicInsn((isMethod ? "dyn:getMethod|getProp|getElem:" : "dyn:getProp|getElem|getMethod:") +
1780 NameCodec.encode(name), Type.getMethodDescriptor(type, Type.OBJECT), LINKERBOOTSTRAP, flags);
1782 pushType(type);
1784 convert(valueType); //most probably a nop
1786 return this;
1787 }
1789 /**
1790 * Generate dynamic setter. Pop receiver and property from stack.
1791 *
1792 * @param valueType the type of the value to set
1793 * @param name name of property
1794 * @param flags call site flags
1795 */
1796 void dynamicSet(final String name, final int flags) {
1797 debug("dynamic_set", name, peekType());
1799 Type type = peekType();
1800 if (type.isObject() || type.isBoolean()) { //promote strings to objects etc
1801 type = Type.OBJECT;
1802 convert(Type.OBJECT); //TODO bad- until we specialize boolean setters,
1803 }
1804 popType(type);
1805 popType(Type.SCOPE);
1807 method.visitInvokeDynamicInsn("dyn:setProp|setElem:" + NameCodec.encode(name), methodDescriptor(void.class, Object.class, type.getTypeClass()), LINKERBOOTSTRAP, flags);
1808 }
1810 /**
1811 * Dynamic getter for indexed structures. Pop index and receiver from stack,
1812 * generate appropriate signatures based on types
1813 *
1814 * @param result result type for getter
1815 * @param flags call site flags for getter
1816 * @param isMethod should it prefer retrieving methods
1817 *
1818 * @return the method emitter
1819 */
1820 MethodEmitter dynamicGetIndex(final Type result, final int flags, final boolean isMethod) {
1821 debug("dynamic_get_index", peekType(1), "[", peekType(), "]");
1823 Type resultType = result;
1824 if (result.isBoolean()) {
1825 resultType = Type.OBJECT; // INT->OBJECT to avoid another dimension of cross products in the getters. TODO
1826 }
1828 Type index = peekType();
1829 if (index.isObject() || index.isBoolean()) {
1830 index = Type.OBJECT; //e.g. string->object
1831 convert(Type.OBJECT);
1832 }
1833 popType();
1835 popType(Type.OBJECT);
1837 final String signature = Type.getMethodDescriptor(resultType, Type.OBJECT /*e.g STRING->OBJECT*/, index);
1839 method.visitInvokeDynamicInsn(isMethod ? "dyn:getMethod|getElem|getProp" : "dyn:getElem|getProp|getMethod",
1840 signature, LINKERBOOTSTRAP, flags);
1841 pushType(resultType);
1843 if (result.isBoolean()) {
1844 convert(Type.BOOLEAN);
1845 }
1847 return this;
1848 }
1850 /**
1851 * Dynamic setter for indexed structures. Pop value, index and receiver from
1852 * stack, generate appropriate signature based on types
1853 *
1854 * @param flags call site flags for setter
1855 */
1856 void dynamicSetIndex(final int flags) {
1857 debug("dynamic_set_index", peekType(2), "[", peekType(1), "] =", peekType());
1859 Type value = peekType();
1860 if (value.isObject() || value.isBoolean()) {
1861 value = Type.OBJECT; //e.g. STRING->OBJECT - one descriptor for all object types
1862 convert(Type.OBJECT);
1863 }
1864 popType();
1866 Type index = peekType();
1867 if (index.isObject() || index.isBoolean()) {
1868 index = Type.OBJECT; //e.g. string->object
1869 convert(Type.OBJECT);
1870 }
1871 popType(index);
1873 final Type receiver = popType(Type.OBJECT);
1874 assert receiver.isObject();
1876 method.visitInvokeDynamicInsn("dyn:setElem|setProp", methodDescriptor(void.class, receiver.getTypeClass(), index.getTypeClass(), value.getTypeClass()), LINKERBOOTSTRAP, flags);
1877 }
1879 /**
1880 * Load a key value in the proper form.
1881 *
1882 * @param key
1883 */
1884 //TODO move this and break it apart
1885 MethodEmitter loadKey(final Object key) {
1886 if (key instanceof IdentNode) {
1887 method.visitLdcInsn(((IdentNode) key).getName());
1888 } else if (key instanceof LiteralNode) {
1889 method.visitLdcInsn(((LiteralNode<?>)key).getString());
1890 } else {
1891 method.visitLdcInsn(JSType.toString(key));
1892 }
1893 pushType(Type.OBJECT); //STRING
1894 return this;
1895 }
1897 @SuppressWarnings("fallthrough")
1898 private static Type fieldType(final String desc) {
1899 switch (desc) {
1900 case "Z":
1901 case "B":
1902 case "C":
1903 case "S":
1904 case "I":
1905 return Type.INT;
1906 case "F":
1907 assert false;
1908 case "D":
1909 return Type.NUMBER;
1910 case "J":
1911 return Type.LONG;
1912 default:
1913 assert desc.startsWith("[") || desc.startsWith("L") : desc + " is not an object type";
1914 switch (desc.charAt(0)) {
1915 case 'L':
1916 return Type.OBJECT;
1917 case '[':
1918 return Type.typeFor(Array.newInstance(fieldType(desc.substring(1)).getTypeClass(), 0).getClass());
1919 default:
1920 assert false;
1921 }
1922 return Type.OBJECT;
1923 }
1924 }
1926 /**
1927 * Generate get for a field access
1928 *
1929 * @param fa the field access
1930 *
1931 * @return the method emitter
1932 */
1933 MethodEmitter getField(final FieldAccess fa) {
1934 return fa.get(this);
1935 }
1937 /**
1938 * Generate set for a field access
1939 *
1940 * @param fa the field access
1941 */
1942 void putField(final FieldAccess fa) {
1943 fa.put(this);
1944 }
1946 /**
1947 * Get the value of a non-static field, pop the receiver from the stack,
1948 * push value to the stack
1949 *
1950 * @param className class
1951 * @param fieldName field name
1952 * @param fieldDescriptor field descriptor
1953 *
1954 * @return the method emitter
1955 */
1956 MethodEmitter getField(final String className, final String fieldName, final String fieldDescriptor) {
1957 debug("getfield", "receiver=", peekType(), className, ".", fieldName, fieldDescriptor);
1958 final Type receiver = popType();
1959 assert receiver.isObject();
1960 method.visitFieldInsn(GETFIELD, className, fieldName, fieldDescriptor);
1961 pushType(fieldType(fieldDescriptor));
1962 return this;
1963 }
1965 /**
1966 * Get the value of a static field, push it to the stack
1967 *
1968 * @param className class
1969 * @param fieldName field name
1970 * @param fieldDescriptor field descriptor
1971 *
1972 * @return the method emitter
1973 */
1974 MethodEmitter getStatic(final String className, final String fieldName, final String fieldDescriptor) {
1975 debug("getstatic", className, ".", fieldName, ".", fieldDescriptor);
1976 method.visitFieldInsn(GETSTATIC, className, fieldName, fieldDescriptor);
1977 pushType(fieldType(fieldDescriptor));
1978 return this;
1979 }
1981 /**
1982 * Pop value and field from stack and write to a non-static field
1983 *
1984 * @param className class
1985 * @param fieldName field name
1986 * @param fieldDescriptor field descriptor
1987 */
1988 void putField(final String className, final String fieldName, final String fieldDescriptor) {
1989 debug("putfield", "receiver=", peekType(1), "value=", peekType());
1990 popType(fieldType(fieldDescriptor));
1991 popType(Type.OBJECT);
1992 method.visitFieldInsn(PUTFIELD, className, fieldName, fieldDescriptor);
1993 }
1995 /**
1996 * Pop value from stack and write to a static field
1997 *
1998 * @param className class
1999 * @param fieldName field name
2000 * @param fieldDescriptor field descriptor
2001 */
2002 void putStatic(final String className, final String fieldName, final String fieldDescriptor) {
2003 debug("putfield", "value=", peekType());
2004 popType(fieldType(fieldDescriptor));
2005 method.visitFieldInsn(PUTSTATIC, className, fieldName, fieldDescriptor);
2006 }
2008 /**
2009 * Register line number at a label
2010 *
2011 * @param line line number
2012 * @param label label
2013 */
2014 void lineNumber(final int line) {
2015 if (env._debug_lines) {
2016 debug_label("[LINE]", line);
2017 final jdk.internal.org.objectweb.asm.Label l = new jdk.internal.org.objectweb.asm.Label();
2018 method.visitLabel(l);
2019 method.visitLineNumber(line, l);
2020 }
2021 }
2023 /*
2024 * Debugging below
2025 */
2027 private final FieldAccess ERR_STREAM = staticField(System.class, "err", PrintStream.class);
2028 private final Call PRINT = virtualCallNoLookup(PrintStream.class, "print", void.class, Object.class);
2029 private final Call PRINTLN = virtualCallNoLookup(PrintStream.class, "println", void.class, Object.class);
2030 private final Call PRINT_STACKTRACE = virtualCallNoLookup(Throwable.class, "printStackTrace", void.class);
2032 /**
2033 * Emit a System.err.print statement of whatever is on top of the bytecode stack
2034 */
2035 void print() {
2036 getField(ERR_STREAM);
2037 swap();
2038 convert(Type.OBJECT);
2039 invoke(PRINT);
2040 }
2042 /**
2043 * Emit a System.err.println statement of whatever is on top of the bytecode stack
2044 */
2045 void println() {
2046 getField(ERR_STREAM);
2047 swap();
2048 convert(Type.OBJECT);
2049 invoke(PRINTLN);
2050 }
2052 /**
2053 * Emit a System.err.print statement
2054 * @param string string to print
2055 */
2056 void print(final String string) {
2057 getField(ERR_STREAM);
2058 load(string);
2059 invoke(PRINT);
2060 }
2062 /**
2063 * Emit a System.err.println statement
2064 * @param string string to print
2065 */
2066 void println(final String string) {
2067 getField(ERR_STREAM);
2068 load(string);
2069 invoke(PRINTLN);
2070 }
2072 /**
2073 * Print a stacktrace to S
2074 */
2075 void stacktrace() {
2076 _new(Throwable.class);
2077 dup();
2078 invoke(constructorNoLookup(Throwable.class));
2079 invoke(PRINT_STACKTRACE);
2080 }
2082 private static int linePrefix = 0;
2084 /**
2085 * Debug function that outputs generated bytecode and stack contents
2086 *
2087 * @param args debug information to print
2088 */
2089 private void debug(final Object... args) {
2090 if (DEBUG) {
2091 debug(30, args);
2092 }
2093 }
2095 /**
2096 * Debug function that outputs generated bytecode and stack contents
2097 * for a label - indentation is currently the only thing that differs
2098 *
2099 * @param args debug information to print
2100 */
2101 private void debug_label(final Object... args) {
2102 if (DEBUG) {
2103 debug(22, args);
2104 }
2105 }
2107 private void debug(final int padConstant, final Object... args) {
2108 if (DEBUG) {
2109 final StringBuilder sb = new StringBuilder();
2110 int pad;
2112 sb.append('#');
2113 sb.append(++linePrefix);
2115 pad = 5 - sb.length();
2116 while (pad > 0) {
2117 sb.append(' ');
2118 pad--;
2119 }
2121 if (stack != null && !stack.isEmpty()) {
2122 sb.append("{");
2123 sb.append(stack.size());
2124 sb.append(":");
2125 for (int pos = 0; pos < stack.size(); pos++) {
2126 final Type t = stack.peek(pos);
2128 if (t == Type.SCOPE) {
2129 sb.append("scope");
2130 } else if (t == Type.THIS) {
2131 sb.append("this");
2132 } else if (t.isObject()) {
2133 String desc = t.getDescriptor();
2134 int i;
2135 for (i = 0; desc.charAt(i) == '[' && i < desc.length(); i++) {
2136 sb.append('[');
2137 }
2138 desc = desc.substring(i);
2139 final int slash = desc.lastIndexOf('/');
2140 if (slash != -1) {
2141 desc = desc.substring(slash + 1, desc.length() - 1);
2142 }
2143 if ("Object".equals(desc)) {
2144 sb.append('O');
2145 } else {
2146 sb.append(desc);
2147 }
2148 } else {
2149 sb.append(t.getDescriptor());
2150 }
2152 if (pos + 1 < stack.size()) {
2153 sb.append(' ');
2154 }
2155 }
2156 sb.append('}');
2157 sb.append(' ');
2158 }
2160 pad = padConstant - sb.length();
2161 while (pad > 0) {
2162 sb.append(' ');
2163 pad--;
2164 }
2166 for (final Object arg : args) {
2167 sb.append(arg);
2168 sb.append(' ');
2169 }
2171 if (env != null) { //early bootstrap code doesn't have inited context yet
2172 LOG.info(sb);
2173 if (DEBUG_TRACE_LINE == linePrefix) {
2174 new Throwable().printStackTrace(LOG.getOutputStream());
2175 }
2176 }
2177 }
2178 }
2180 /**
2181 * Set the current function node being emitted
2182 * @param functionNode the function node
2183 */
2184 void setFunctionNode(final FunctionNode functionNode) {
2185 this.functionNode = functionNode;
2186 }
2188 boolean hasReturn() {
2189 return hasReturn;
2190 }
2192 List<Label> getExternalTargets() {
2193 return null;
2194 }
2196 }