Thu, 24 May 2018 16:39:31 +0800
Merge
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_ICMPGE;
47 import static jdk.internal.org.objectweb.asm.Opcodes.IF_ICMPGT;
48 import static jdk.internal.org.objectweb.asm.Opcodes.IF_ICMPLE;
49 import static jdk.internal.org.objectweb.asm.Opcodes.IF_ICMPLT;
50 import static jdk.internal.org.objectweb.asm.Opcodes.IF_ICMPNE;
51 import static jdk.internal.org.objectweb.asm.Opcodes.INSTANCEOF;
52 import static jdk.internal.org.objectweb.asm.Opcodes.INVOKEINTERFACE;
53 import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESPECIAL;
54 import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESTATIC;
55 import static jdk.internal.org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
56 import static jdk.internal.org.objectweb.asm.Opcodes.NEW;
57 import static jdk.internal.org.objectweb.asm.Opcodes.PUTFIELD;
58 import static jdk.internal.org.objectweb.asm.Opcodes.PUTSTATIC;
59 import static jdk.internal.org.objectweb.asm.Opcodes.RETURN;
60 import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
61 import static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS;
62 import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
63 import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
64 import static jdk.nashorn.internal.codegen.CompilerConstants.THIS_DEBUGGER;
65 import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
66 import static jdk.nashorn.internal.codegen.CompilerConstants.className;
67 import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
68 import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor;
69 import static jdk.nashorn.internal.codegen.CompilerConstants.staticField;
70 import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
71 import static jdk.nashorn.internal.codegen.ObjectClassGenerator.PRIMITIVE_FIELD_TYPE;
72 import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_OPTIMISTIC;
73 import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_PROGRAM_POINT_SHIFT;
75 import java.io.PrintStream;
76 import java.lang.reflect.Array;
77 import java.util.Collection;
78 import java.util.EnumSet;
79 import java.util.IdentityHashMap;
80 import java.util.List;
81 import java.util.Map;
82 import jdk.internal.dynalink.support.NameCodec;
83 import jdk.internal.org.objectweb.asm.Handle;
84 import jdk.internal.org.objectweb.asm.MethodVisitor;
85 import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
86 import jdk.nashorn.internal.codegen.CompilerConstants.Call;
87 import jdk.nashorn.internal.codegen.CompilerConstants.FieldAccess;
88 import jdk.nashorn.internal.codegen.types.ArrayType;
89 import jdk.nashorn.internal.codegen.types.BitwiseType;
90 import jdk.nashorn.internal.codegen.types.NumericType;
91 import jdk.nashorn.internal.codegen.types.Type;
92 import jdk.nashorn.internal.ir.FunctionNode;
93 import jdk.nashorn.internal.ir.IdentNode;
94 import jdk.nashorn.internal.ir.JoinPredecessor;
95 import jdk.nashorn.internal.ir.LiteralNode;
96 import jdk.nashorn.internal.ir.LocalVariableConversion;
97 import jdk.nashorn.internal.ir.Symbol;
98 import jdk.nashorn.internal.ir.TryNode;
99 import jdk.nashorn.internal.objects.Global;
100 import jdk.nashorn.internal.objects.NativeArray;
101 import jdk.nashorn.internal.runtime.ArgumentSetter;
102 import jdk.nashorn.internal.runtime.Context;
103 import jdk.nashorn.internal.runtime.Debug;
104 import jdk.nashorn.internal.runtime.JSType;
105 import jdk.nashorn.internal.runtime.RewriteException;
106 import jdk.nashorn.internal.runtime.Scope;
107 import jdk.nashorn.internal.runtime.ScriptObject;
108 import jdk.nashorn.internal.runtime.ScriptRuntime;
109 import jdk.nashorn.internal.runtime.UnwarrantedOptimismException;
110 import jdk.nashorn.internal.runtime.linker.Bootstrap;
111 import jdk.nashorn.internal.runtime.logging.DebugLogger;
112 import jdk.nashorn.internal.runtime.options.Options;
114 /**
115 * This is the main function responsible for emitting method code
116 * in a class. It maintains a type stack and keeps track of control
117 * flow to make sure that the registered instructions don't violate
118 * byte code verification.
119 *
120 * Running Nashorn with -ea will assert as soon as a type stack
121 * becomes corrupt, for easier debugging
122 *
123 * Running Nashorn with -Dnashorn.codegen.debug=true will print
124 * all generated bytecode and labels to stderr, for easier debugging,
125 * including bytecode stack contents
126 */
127 public class MethodEmitter {
128 /** The ASM MethodVisitor we are plugged into */
129 private final MethodVisitor method;
131 /** Parent classEmitter representing the class of this method */
132 private final ClassEmitter classEmitter;
134 /** FunctionNode representing this method, or null if none exists */
135 protected FunctionNode functionNode;
137 /** Current type stack for current evaluation */
138 private Label.Stack stack;
140 private boolean preventUndefinedLoad;
142 /**
143 * Map of live local variable definitions.
144 */
145 private final Map<Symbol, LocalVariableDef> localVariableDefs = new IdentityHashMap<>();
147 /** The context */
148 private final Context context;
150 /** Threshold in chars for when string constants should be split */
151 static final int LARGE_STRING_THRESHOLD = 32 * 1024;
153 /** Debug flag, should we dump all generated bytecode along with stacks? */
154 private final DebugLogger log;
155 private final boolean debug;
157 /** dump stack on a particular line, or -1 if disabled */
158 private static final int DEBUG_TRACE_LINE;
160 static {
161 final String tl = Options.getStringProperty("nashorn.codegen.debug.trace", "-1");
162 int line = -1;
163 try {
164 line = Integer.parseInt(tl);
165 } catch (final NumberFormatException e) {
166 //fallthru
167 }
168 DEBUG_TRACE_LINE = line;
169 }
171 /** Bootstrap for normal indy:s */
172 private static final Handle LINKERBOOTSTRAP = new Handle(H_INVOKESTATIC, Bootstrap.BOOTSTRAP.className(), Bootstrap.BOOTSTRAP.name(), Bootstrap.BOOTSTRAP.descriptor());
174 /** Bootstrap for array populators */
175 private static final Handle POPULATE_ARRAY_BOOTSTRAP = new Handle(H_INVOKESTATIC, RewriteException.BOOTSTRAP.className(), RewriteException.BOOTSTRAP.name(), RewriteException.BOOTSTRAP.descriptor());
177 /**
178 * Constructor - internal use from ClassEmitter only
179 * @see ClassEmitter#method
180 *
181 * @param classEmitter the class emitter weaving the class this method is in
182 * @param method a method visitor
183 */
184 MethodEmitter(final ClassEmitter classEmitter, final MethodVisitor method) {
185 this(classEmitter, method, null);
186 }
188 /**
189 * Constructor - internal use from ClassEmitter only
190 * @see ClassEmitter#method
191 *
192 * @param classEmitter the class emitter weaving the class this method is in
193 * @param method a method visitor
194 * @param functionNode a function node representing this method
195 */
196 MethodEmitter(final ClassEmitter classEmitter, final MethodVisitor method, final FunctionNode functionNode) {
197 this.context = classEmitter.getContext();
198 this.classEmitter = classEmitter;
199 this.method = method;
200 this.functionNode = functionNode;
201 this.stack = null;
202 this.log = context.getLogger(CodeGenerator.class);
203 this.debug = log.isEnabled();
204 }
206 /**
207 * Begin a method
208 */
209 public void begin() {
210 classEmitter.beginMethod(this);
211 newStack();
212 method.visitCode();
213 }
215 /**
216 * End a method
217 */
218 public void end() {
219 method.visitMaxs(0, 0);
220 method.visitEnd();
222 classEmitter.endMethod(this);
223 }
225 boolean isReachable() {
226 return stack != null;
227 }
229 private void doesNotContinueSequentially() {
230 stack = null;
231 }
233 private void newStack() {
234 stack = new Label.Stack();
235 }
237 @Override
238 public String toString() {
239 return "methodEmitter: " + (functionNode == null ? method : functionNode.getName()).toString() + ' ' + Debug.id(this);
240 }
242 /**
243 * Push a type to the existing stack
244 * @param type the type
245 */
246 void pushType(final Type type) {
247 if (type != null) {
248 stack.push(type);
249 }
250 }
252 /**
253 * Pop a type from the existing stack
254 *
255 * @param expected expected type - will assert if wrong
256 *
257 * @return the type that was retrieved
258 */
259 private Type popType(final Type expected) {
260 final Type type = popType();
261 assert type.isEquivalentTo(expected) : type + " is not compatible with " + expected;
262 return type;
263 }
265 /**
266 * Pop a type from the existing stack, no matter what it is.
267 *
268 * @return the type
269 */
270 private Type popType() {
271 return stack.pop();
272 }
274 /**
275 * Pop a type from the existing stack, ensuring that it is numeric. Boolean type is popped as int type.
276 *
277 * @return the type
278 */
279 private NumericType popNumeric() {
280 final Type type = popType();
281 if(type.isBoolean()) {
282 // Booleans are treated as int for purposes of arithmetic operations
283 return Type.INT;
284 }
285 assert type.isNumeric();
286 return (NumericType)type;
287 }
289 /**
290 * Pop a type from the existing stack, ensuring that it is an integer type
291 * (integer or long). Boolean type is popped as int type.
292 *
293 * @return the type
294 */
295 private BitwiseType popBitwise() {
296 final Type type = popType();
297 if(type == Type.BOOLEAN) {
298 return Type.INT;
299 }
300 return (BitwiseType)type;
301 }
303 private BitwiseType popInteger() {
304 final Type type = popType();
305 if(type == Type.BOOLEAN) {
306 return Type.INT;
307 }
308 assert type == Type.INT;
309 return (BitwiseType)type;
310 }
312 /**
313 * Pop a type from the existing stack, ensuring that it is an array type,
314 * assert if not
315 *
316 * @return the type
317 */
318 private ArrayType popArray() {
319 final Type type = popType();
320 assert type.isArray() : type;
321 return (ArrayType)type;
322 }
324 /**
325 * Peek a given number of slots from the top of the stack and return the
326 * type in that slot
327 *
328 * @param pos the number of positions from the top, 0 is the top element
329 *
330 * @return the type at position "pos" on the stack
331 */
332 final Type peekType(final int pos) {
333 return stack.peek(pos);
334 }
336 /**
337 * Peek at the type at the top of the stack
338 *
339 * @return the type at the top of the stack
340 */
341 final Type peekType() {
342 return stack.peek();
343 }
345 /**
346 * Generate code a for instantiating a new object and push the
347 * object type on the stack
348 *
349 * @param classDescriptor class descriptor for the object type
350 * @param type the type of the new object
351 *
352 * @return the method emitter
353 */
354 MethodEmitter _new(final String classDescriptor, final Type type) {
355 debug("new", classDescriptor);
356 method.visitTypeInsn(NEW, classDescriptor);
357 pushType(type);
358 return this;
359 }
361 /**
362 * Generate code a for instantiating a new object and push the
363 * object type on the stack
364 *
365 * @param clazz class type to instatiate
366 *
367 * @return the method emitter
368 */
369 MethodEmitter _new(final Class<?> clazz) {
370 return _new(className(clazz), Type.typeFor(clazz));
371 }
373 /**
374 * Generate code to call the empty constructor for a class
375 *
376 * @param clazz class type to instatiate
377 *
378 * @return the method emitter
379 */
380 MethodEmitter newInstance(final Class<?> clazz) {
381 return invoke(constructorNoLookup(clazz));
382 }
384 /**
385 * Perform a dup, that is, duplicate the top element and
386 * push the duplicate down a given number of positions
387 * on the stack. This is totally type agnostic.
388 *
389 * @param depth the depth on which to put the copy
390 *
391 * @return the method emitter, or null if depth is illegal and
392 * has no instruction equivalent.
393 */
394 MethodEmitter dup(final int depth) {
395 if (peekType().dup(method, depth) == null) {
396 return null;
397 }
399 debug("dup", depth);
401 switch (depth) {
402 case 0: {
403 final int l0 = stack.getTopLocalLoad();
404 pushType(peekType());
405 stack.markLocalLoad(l0);
406 break;
407 }
408 case 1: {
409 final int l0 = stack.getTopLocalLoad();
410 final Type p0 = popType();
411 final int l1 = stack.getTopLocalLoad();
412 final Type p1 = popType();
413 pushType(p0);
414 stack.markLocalLoad(l0);
415 pushType(p1);
416 stack.markLocalLoad(l1);
417 pushType(p0);
418 stack.markLocalLoad(l0);
419 break;
420 }
421 case 2: {
422 final int l0 = stack.getTopLocalLoad();
423 final Type p0 = popType();
424 final int l1 = stack.getTopLocalLoad();
425 final Type p1 = popType();
426 final int l2 = stack.getTopLocalLoad();
427 final Type p2 = popType();
428 pushType(p0);
429 stack.markLocalLoad(l0);
430 pushType(p2);
431 stack.markLocalLoad(l2);
432 pushType(p1);
433 stack.markLocalLoad(l1);
434 pushType(p0);
435 stack.markLocalLoad(l0);
436 break;
437 }
438 default:
439 assert false : "illegal dup depth = " + depth;
440 return null;
441 }
443 return this;
444 }
446 /**
447 * Perform a dup2, that is, duplicate the top element if it
448 * is a category 2 type, or two top elements if they are category
449 * 1 types, and push them on top of the stack
450 *
451 * @return the method emitter
452 */
453 MethodEmitter dup2() {
454 debug("dup2");
456 if (peekType().isCategory2()) {
457 final int l0 = stack.getTopLocalLoad();
458 pushType(peekType());
459 stack.markLocalLoad(l0);
460 } else {
461 final int l0 = stack.getTopLocalLoad();
462 final Type p0 = popType();
463 final int l1 = stack.getTopLocalLoad();
464 final Type p1 = popType();
465 pushType(p0);
466 stack.markLocalLoad(l0);
467 pushType(p1);
468 stack.markLocalLoad(l1);
469 pushType(p0);
470 stack.markLocalLoad(l0);
471 pushType(p1);
472 stack.markLocalLoad(l1);
473 }
474 method.visitInsn(DUP2);
475 return this;
476 }
478 /**
479 * Duplicate the top element on the stack and push it
480 *
481 * @return the method emitter
482 */
483 MethodEmitter dup() {
484 return dup(0);
485 }
487 /**
488 * Pop the top element of the stack and throw it away
489 *
490 * @return the method emitter
491 */
492 MethodEmitter pop() {
493 debug("pop", peekType());
494 popType().pop(method);
495 return this;
496 }
498 /**
499 * Pop the top element of the stack if category 2 type, or the two
500 * top elements of the stack if category 1 types
501 *
502 * @return the method emitter
503 */
504 MethodEmitter pop2() {
505 if (peekType().isCategory2()) {
506 popType();
507 } else {
508 get2n();
509 }
510 return this;
511 }
513 /**
514 * Swap the top two elements of the stack. This is totally
515 * type agnostic and works for all types
516 *
517 * @return the method emitter
518 */
519 MethodEmitter swap() {
520 debug("swap");
522 final int l0 = stack.getTopLocalLoad();
523 final Type p0 = popType();
524 final int l1 = stack.getTopLocalLoad();
525 final Type p1 = popType();
526 p0.swap(method, p1);
528 pushType(p0);
529 stack.markLocalLoad(l0);
530 pushType(p1);
531 stack.markLocalLoad(l1);
532 return this;
533 }
535 void pack() {
536 final Type type = peekType();
537 if (type.isInteger()) {
538 convert(PRIMITIVE_FIELD_TYPE);
539 } else if (type.isLong()) {
540 //nop
541 } else if (type.isNumber()) {
542 invokestatic("java/lang/Double", "doubleToRawLongBits", "(D)J");
543 } else {
544 assert false : type + " cannot be packed!";
545 }
546 }
548 /**
549 * Initializes a bytecode method parameter
550 * @param symbol the symbol for the parameter
551 * @param type the type of the parameter
552 * @param start the label for the start of the method
553 */
554 void initializeMethodParameter(final Symbol symbol, final Type type, final Label start) {
555 assert symbol.isBytecodeLocal();
556 localVariableDefs.put(symbol, new LocalVariableDef(start.getLabel(), type));
557 }
559 /**
560 * Create a new string builder, call the constructor and push the instance to the stack.
561 *
562 * @return the method emitter
563 */
564 MethodEmitter newStringBuilder() {
565 return invoke(constructorNoLookup(StringBuilder.class)).dup();
566 }
568 /**
569 * Pop a string and a StringBuilder from the top of the stack and call the append
570 * function of the StringBuilder, appending the string. Pushes the StringBuilder to
571 * the stack when finished.
572 *
573 * @return the method emitter
574 */
575 MethodEmitter stringBuilderAppend() {
576 convert(Type.STRING);
577 return invoke(virtualCallNoLookup(StringBuilder.class, "append", StringBuilder.class, String.class));
578 }
580 /**
581 * Pops two integer types from the stack, performs a bitwise and and pushes
582 * the result
583 *
584 * @return the method emitter
585 */
586 MethodEmitter and() {
587 debug("and");
588 pushType(get2i().and(method));
589 return this;
590 }
592 /**
593 * Pops two integer types from the stack, performs a bitwise or and pushes
594 * the result
595 *
596 * @return the method emitter
597 */
598 MethodEmitter or() {
599 debug("or");
600 pushType(get2i().or(method));
601 return this;
602 }
604 /**
605 * Pops two integer types from the stack, performs a bitwise xor and pushes
606 * the result
607 *
608 * @return the method emitter
609 */
610 MethodEmitter xor() {
611 debug("xor");
612 pushType(get2i().xor(method));
613 return this;
614 }
616 /**
617 * Pops two integer types from the stack, performs a bitwise logic shift right and pushes
618 * the result. The shift count, the first element, must be INT.
619 *
620 * @return the method emitter
621 */
622 MethodEmitter shr() {
623 debug("shr");
624 popInteger();
625 pushType(popBitwise().shr(method));
626 return this;
627 }
629 /**
630 * Pops two integer types from the stack, performs a bitwise shift left and and pushes
631 * the result. The shift count, the first element, must be INT.
632 *
633 * @return the method emitter
634 */
635 MethodEmitter shl() {
636 debug("shl");
637 popInteger();
638 pushType(popBitwise().shl(method));
639 return this;
640 }
642 /**
643 * Pops two integer types from the stack, performs a bitwise arithmetic shift right and pushes
644 * the result. The shift count, the first element, must be INT.
645 *
646 * @return the method emitter
647 */
648 MethodEmitter sar() {
649 debug("sar");
650 popInteger();
651 pushType(popBitwise().sar(method));
652 return this;
653 }
655 /**
656 * Pops a numeric type from the stack, negates it and pushes the result
657 *
658 * @return the method emitter
659 */
660 MethodEmitter neg(final int programPoint) {
661 debug("neg");
662 pushType(popNumeric().neg(method, programPoint));
663 return this;
664 }
666 /**
667 * Add label for the start of a catch block and push the exception to the
668 * stack
669 *
670 * @param recovery label pointing to start of catch block
671 */
672 void _catch(final Label recovery) {
673 // While in JVM a catch block can be reached through normal control flow, our code generator never does this,
674 // so we might as well presume there's no stack on entry.
675 assert stack == null;
676 recovery.onCatch();
677 label(recovery);
678 beginCatchBlock();
679 }
681 /**
682 * Add any number of labels for the start of a catch block and push the exception to the
683 * stack
684 *
685 * @param recoveries labels pointing to start of catch block
686 */
687 void _catch(final Collection<Label> recoveries) {
688 assert stack == null;
689 for(final Label l: recoveries) {
690 label(l);
691 }
692 beginCatchBlock();
693 }
695 private void beginCatchBlock() {
696 // It can happen that the catch label wasn't marked as reachable. They are marked as reachable if there's an
697 // assignment in the try block, but it's possible that there was none.
698 if(!isReachable()) {
699 newStack();
700 }
701 pushType(Type.typeFor(Throwable.class));
702 }
703 /**
704 * Start a try/catch block.
705 *
706 * @param entry start label for try
707 * @param exit end label for try
708 * @param recovery start label for catch
709 * @param typeDescriptor type descriptor for exception
710 * @param isOptimismHandler true if this is a hander for {@code UnwarrantedOptimismException}. Normally joining on a
711 * catch handler kills temporary variables, but optimism handlers are an exception, as they need to capture
712 * temporaries as well, so they must remain live.
713 */
714 private void _try(final Label entry, final Label exit, final Label recovery, final String typeDescriptor, final boolean isOptimismHandler) {
715 recovery.joinFromTry(entry.getStack(), isOptimismHandler);
716 method.visitTryCatchBlock(entry.getLabel(), exit.getLabel(), recovery.getLabel(), typeDescriptor);
717 }
719 /**
720 * Start a try/catch block.
721 *
722 * @param entry start label for try
723 * @param exit end label for try
724 * @param recovery start label for catch
725 * @param clazz exception class
726 */
727 void _try(final Label entry, final Label exit, final Label recovery, final Class<?> clazz) {
728 _try(entry, exit, recovery, CompilerConstants.className(clazz), clazz == UnwarrantedOptimismException.class);
729 }
731 /**
732 * Start a try/catch block. The catch is "Throwable" - i.e. catch-all
733 *
734 * @param entry start label for try
735 * @param exit end label for try
736 * @param recovery start label for catch
737 */
738 void _try(final Label entry, final Label exit, final Label recovery) {
739 _try(entry, exit, recovery, (String)null, false);
740 }
742 void markLabelAsOptimisticCatchHandler(final Label label, final int liveLocalCount) {
743 label.markAsOptimisticCatchHandler(stack, liveLocalCount);
744 }
746 /**
747 * Load the constants array
748 * @return this method emitter
749 */
750 MethodEmitter loadConstants() {
751 getStatic(classEmitter.getUnitClassName(), CONSTANTS.symbolName(), CONSTANTS.descriptor());
752 assert peekType().isArray() : peekType();
753 return this;
754 }
756 /**
757 * Push the undefined value for the given type, i.e.
758 * UNDEFINED or UNDEFINEDNUMBER. Currently we have no way of
759 * representing UNDEFINED for INTs and LONGs, so they are not
760 * allowed to be local variables (yet)
761 *
762 * @param type the type for which to push UNDEFINED
763 * @return the method emitter
764 */
765 MethodEmitter loadUndefined(final Type type) {
766 debug("load undefined ", type);
767 pushType(type.loadUndefined(method));
768 return this;
769 }
771 MethodEmitter loadForcedInitializer(final Type type) {
772 debug("load forced initializer ", type);
773 pushType(type.loadForcedInitializer(method));
774 return this;
775 }
777 /**
778 * Push the empty value for the given type, i.e. EMPTY.
779 *
780 * @param type the type
781 * @return the method emitter
782 */
783 MethodEmitter loadEmpty(final Type type) {
784 debug("load empty ", type);
785 pushType(type.loadEmpty(method));
786 return this;
787 }
789 /**
790 * Push null to stack
791 *
792 * @return the method emitter
793 */
794 MethodEmitter loadNull() {
795 debug("aconst_null");
796 pushType(Type.OBJECT.ldc(method, null));
797 return this;
798 }
800 /**
801 * Push a handle representing this class top stack
802 *
803 * @param className name of the class
804 *
805 * @return the method emitter
806 */
807 MethodEmitter loadType(final String className) {
808 debug("load type", className);
809 method.visitLdcInsn(jdk.internal.org.objectweb.asm.Type.getObjectType(className));
810 pushType(Type.OBJECT);
811 return this;
812 }
814 /**
815 * Push a boolean constant to the stack.
816 *
817 * @param b value of boolean
818 *
819 * @return the method emitter
820 */
821 MethodEmitter load(final boolean b) {
822 debug("load boolean", b);
823 pushType(Type.BOOLEAN.ldc(method, b));
824 return this;
825 }
827 /**
828 * Push an int constant to the stack
829 *
830 * @param i value of the int
831 *
832 * @return the method emitter
833 */
834 MethodEmitter load(final int i) {
835 debug("load int", i);
836 pushType(Type.INT.ldc(method, i));
837 return this;
838 }
840 /**
841 * Push a double constant to the stack
842 *
843 * @param d value of the double
844 *
845 * @return the method emitter
846 */
847 MethodEmitter load(final double d) {
848 debug("load double", d);
849 pushType(Type.NUMBER.ldc(method, d));
850 return this;
851 }
853 /**
854 * Push an long constant to the stack
855 *
856 * @param l value of the long
857 *
858 * @return the method emitter
859 */
860 MethodEmitter load(final long l) {
861 debug("load long", l);
862 pushType(Type.LONG.ldc(method, l));
863 return this;
864 }
866 /**
867 * Fetch the length of an array.
868 * @return Array length.
869 */
870 MethodEmitter arraylength() {
871 debug("arraylength");
872 popType(Type.OBJECT);
873 pushType(Type.OBJECT_ARRAY.arraylength(method));
874 return this;
875 }
877 /**
878 * Push a String constant to the stack
879 *
880 * @param s value of the String
881 *
882 * @return the method emitter
883 */
884 MethodEmitter load(final String s) {
885 debug("load string", s);
887 if (s == null) {
888 loadNull();
889 return this;
890 }
892 //NASHORN-142 - split too large string
893 final int length = s.length();
894 if (length > LARGE_STRING_THRESHOLD) {
896 _new(StringBuilder.class);
897 dup();
898 load(length);
899 invoke(constructorNoLookup(StringBuilder.class, int.class));
901 for (int n = 0; n < length; n += LARGE_STRING_THRESHOLD) {
902 final String part = s.substring(n, Math.min(n + LARGE_STRING_THRESHOLD, length));
903 load(part);
904 stringBuilderAppend();
905 }
907 invoke(virtualCallNoLookup(StringBuilder.class, "toString", String.class));
909 return this;
910 }
912 pushType(Type.OBJECT.ldc(method, s));
913 return this;
914 }
916 /**
917 * Pushes the value of an identifier to the stack. If the identifier does not represent a local variable or a
918 * parameter, this will be a no-op.
919 *
920 * @param ident the identifier for the variable being loaded.
921 *
922 * @return the method emitter
923 */
924 MethodEmitter load(final IdentNode ident) {
925 return load(ident.getSymbol(), ident.getType());
926 }
928 /**
929 * Pushes the value of the symbol to the stack with the specified type. No type conversion is being performed, and
930 * the type is only being used if the symbol addresses a local variable slot. The value of the symbol is loaded if
931 * it addresses a local variable slot, or it is a parameter (in which case it can also be loaded from a vararg array
932 * or the arguments object). If it is neither, the operation is a no-op.
933 *
934 * @param symbol the symbol addressing the value being loaded
935 * @param type the presumed type of the value when it is loaded from a local variable slot
936 * @return the method emitter
937 */
938 MethodEmitter load(final Symbol symbol, final Type type) {
939 assert symbol != null;
940 if (symbol.hasSlot()) {
941 final int slot = symbol.getSlot(type);
942 debug("load symbol", symbol.getName(), " slot=", slot, "type=", type);
943 load(type, slot);
944 // _try(new Label("dummy"), new Label("dummy2"), recovery);
945 // method.visitTryCatchBlock(new Label(), arg1, arg2, arg3);
946 } else if (symbol.isParam()) {
947 assert functionNode.isVarArg() : "Non-vararg functions have slotted parameters";
948 final int index = symbol.getFieldIndex();
949 if (functionNode.needsArguments()) {
950 // ScriptObject.getArgument(int) on arguments
951 debug("load symbol", symbol.getName(), " arguments index=", index);
952 loadCompilerConstant(ARGUMENTS);
953 load(index);
954 ScriptObject.GET_ARGUMENT.invoke(this);
955 } else {
956 // array load from __varargs__
957 debug("load symbol", symbol.getName(), " array index=", index);
958 loadCompilerConstant(VARARGS);
959 load(symbol.getFieldIndex());
960 arrayload();
961 }
962 }
963 return this;
964 }
966 /**
967 * Push a local variable to the stack, given an explicit bytecode slot.
968 * This is used e.g. for stub generation where we know where items like
969 * "this" and "scope" reside.
970 *
971 * @param type the type of the variable
972 * @param slot the slot the variable is in
973 *
974 * @return the method emitter
975 */
976 MethodEmitter load(final Type type, final int slot) {
977 debug("explicit load", type, slot);
978 final Type loadType = type.load(method, slot);
979 assert loadType != null;
980 pushType(loadType == Type.OBJECT && isThisSlot(slot) ? Type.THIS : loadType);
981 assert !preventUndefinedLoad || (slot < stack.localVariableTypes.size() && stack.localVariableTypes.get(slot) != Type.UNKNOWN)
982 : "Attempted load of uninitialized slot " + slot + " (as type " + type + ")";
983 stack.markLocalLoad(slot);
984 return this;
985 }
987 private boolean isThisSlot(final int slot) {
988 if (functionNode == null) {
989 return slot == CompilerConstants.JAVA_THIS.slot();
990 }
991 final int thisSlot = getCompilerConstantSymbol(THIS).getSlot(Type.OBJECT);
992 assert !functionNode.needsCallee() || thisSlot == 1; // needsCallee -> thisSlot == 1
993 assert functionNode.needsCallee() || thisSlot == 0; // !needsCallee -> thisSlot == 0
994 return slot == thisSlot;
995 }
997 /**
998 * Push a method handle to the stack
999 *
1000 * @param className class name
1001 * @param methodName method name
1002 * @param descName descriptor
1003 * @param flags flags that describe this handle, e.g. invokespecial new, or invoke virtual
1004 *
1005 * @return the method emitter
1006 */
1007 MethodEmitter loadHandle(final String className, final String methodName, final String descName, final EnumSet<Flag> flags) {
1008 debug("load handle ");
1009 pushType(Type.OBJECT.ldc(method, new Handle(Flag.getValue(flags), className, methodName, descName)));
1010 return this;
1011 }
1013 private Symbol getCompilerConstantSymbol(final CompilerConstants cc) {
1014 return functionNode.getBody().getExistingSymbol(cc.symbolName());
1015 }
1017 /**
1018 * True if this method has a slot allocated for the scope variable (meaning, something in the method actually needs
1019 * the scope).
1020 * @return if this method has a slot allocated for the scope variable.
1021 */
1022 boolean hasScope() {
1023 return getCompilerConstantSymbol(SCOPE).hasSlot();
1024 }
1026 MethodEmitter loadCompilerConstant(final CompilerConstants cc) {
1027 return loadCompilerConstant(cc, null);
1028 }
1030 MethodEmitter loadCompilerConstant(final CompilerConstants cc, final Type type) {
1031 if (cc == SCOPE && peekType() == Type.SCOPE) {
1032 dup();
1033 return this;
1034 }
1035 return load(getCompilerConstantSymbol(cc), type != null ? type : getCompilerConstantType(cc));
1036 }
1038 MethodEmitter loadScope() {
1039 return loadCompilerConstant(SCOPE).checkcast(Scope.class);
1040 }
1042 MethodEmitter setSplitState(final int state) {
1043 return loadScope().load(state).invoke(Scope.SET_SPLIT_STATE);
1044 }
1046 void storeCompilerConstant(final CompilerConstants cc) {
1047 storeCompilerConstant(cc, null);
1048 }
1050 void storeCompilerConstant(final CompilerConstants cc, final Type type) {
1051 final Symbol symbol = getCompilerConstantSymbol(cc);
1052 if(!symbol.hasSlot()) {
1053 return;
1054 }
1055 debug("store compiler constant ", symbol);
1056 store(symbol, type != null ? type : getCompilerConstantType(cc));
1057 }
1059 private static Type getCompilerConstantType(final CompilerConstants cc) {
1060 final Class<?> constantType = cc.type();
1061 assert constantType != null;
1062 return Type.typeFor(constantType);
1063 }
1065 /**
1066 * Load an element from an array, determining type automatically
1067 * @return the method emitter
1068 */
1069 MethodEmitter arrayload() {
1070 debug("Xaload");
1071 popType(Type.INT);
1072 pushType(popArray().aload(method));
1073 return this;
1074 }
1076 /**
1077 * Pop a value, an index and an array from the stack and store
1078 * the value at the given index in the array.
1079 */
1080 void arraystore() {
1081 debug("Xastore");
1082 final Type value = popType();
1083 final Type index = popType(Type.INT);
1084 assert index.isInteger() : "array index is not integer, but " + index;
1085 final ArrayType array = popArray();
1087 assert value.isEquivalentTo(array.getElementType()) : "Storing "+value+" into "+array;
1088 assert array.isObject();
1089 array.astore(method);
1090 }
1092 /**
1093 * Pop a value from the stack and store it in a local variable represented
1094 * by the given identifier. If the symbol has no slot, this is a NOP
1095 *
1096 * @param ident identifier to store stack to
1097 */
1098 void store(final IdentNode ident) {
1099 final Type type = ident.getType();
1100 final Symbol symbol = ident.getSymbol();
1101 if(type == Type.UNDEFINED) {
1102 assert peekType() == Type.UNDEFINED;
1103 store(symbol, Type.OBJECT);
1104 } else {
1105 store(symbol, type);
1106 }
1107 }
1109 /**
1110 * Represents a definition of a local variable with a type. Used for local variable table building.
1111 */
1112 private static class LocalVariableDef {
1113 // The start label from where this definition lives.
1114 private final jdk.internal.org.objectweb.asm.Label label;
1115 // The currently live type of the local variable.
1116 private final Type type;
1118 LocalVariableDef(final jdk.internal.org.objectweb.asm.Label label, final Type type) {
1119 this.label = label;
1120 this.type = type;
1121 }
1123 }
1125 void closeLocalVariable(final Symbol symbol, final Label label) {
1126 final LocalVariableDef def = localVariableDefs.get(symbol);
1127 if(def != null) {
1128 endLocalValueDef(symbol, def, label.getLabel());
1129 }
1130 if(isReachable()) {
1131 markDeadLocalVariable(symbol);
1132 }
1133 }
1135 void markDeadLocalVariable(final Symbol symbol) {
1136 if(!symbol.isDead()) {
1137 markDeadSlots(symbol.getFirstSlot(), symbol.slotCount());
1138 }
1139 }
1141 void markDeadSlots(final int firstSlot, final int slotCount) {
1142 stack.markDeadLocalVariables(firstSlot, slotCount);
1143 }
1145 private void endLocalValueDef(final Symbol symbol, final LocalVariableDef def, final jdk.internal.org.objectweb.asm.Label label) {
1146 String name = symbol.getName();
1147 if (name.equals(THIS.symbolName())) {
1148 name = THIS_DEBUGGER.symbolName();
1149 }
1150 method.visitLocalVariable(name, def.type.getDescriptor(), null, def.label, label, symbol.getSlot(def.type));
1151 }
1153 void store(final Symbol symbol, final Type type) {
1154 store(symbol, type, true);
1155 }
1157 /**
1158 * Pop a value from the stack and store it in a variable denoted by the given symbol. The variable should be either
1159 * a local variable, or a function parameter (and not a scoped variable). For local variables, this method will also
1160 * do the bookkeeping of the local variable table as well as mark values in all alternative slots for the symbol as
1161 * dead. In this regard it differs from {@link #storeHidden(Type, int)}.
1162 *
1163 * @param symbol the symbol to store into.
1164 * @param type the type to store
1165 * @param onlySymbolLiveValue if true, this is the sole live value for the symbol. If false, currently live values should
1166 * be kept live.
1167 */
1168 void store(final Symbol symbol, final Type type, final boolean onlySymbolLiveValue) {
1169 assert symbol != null : "No symbol to store";
1170 if (symbol.hasSlot()) {
1171 final boolean isLiveType = symbol.hasSlotFor(type);
1172 final LocalVariableDef existingDef = localVariableDefs.get(symbol);
1173 if(existingDef == null || existingDef.type != type) {
1174 final jdk.internal.org.objectweb.asm.Label here = new jdk.internal.org.objectweb.asm.Label();
1175 if(isLiveType) {
1176 final LocalVariableDef newDef = new LocalVariableDef(here, type);
1177 localVariableDefs.put(symbol, newDef);
1178 }
1179 method.visitLabel(here);
1180 if(existingDef != null) {
1181 endLocalValueDef(symbol, existingDef, here);
1182 }
1183 }
1184 if(isLiveType) {
1185 final int slot = symbol.getSlot(type);
1186 debug("store symbol", symbol.getName(), " type=", type, " slot=", slot);
1187 storeHidden(type, slot, onlySymbolLiveValue);
1188 } else {
1189 if(onlySymbolLiveValue) {
1190 markDeadLocalVariable(symbol);
1191 }
1192 debug("dead store symbol ", symbol.getName(), " type=", type);
1193 pop();
1194 }
1195 } else if (symbol.isParam()) {
1196 assert !symbol.isScope();
1197 assert functionNode.isVarArg() : "Non-vararg functions have slotted parameters";
1198 final int index = symbol.getFieldIndex();
1199 if (functionNode.needsArguments()) {
1200 convert(Type.OBJECT);
1201 debug("store symbol", symbol.getName(), " arguments index=", index);
1202 loadCompilerConstant(ARGUMENTS);
1203 load(index);
1204 ArgumentSetter.SET_ARGUMENT.invoke(this);
1205 } else {
1206 convert(Type.OBJECT);
1207 // varargs without arguments object - just do array store to __varargs__
1208 debug("store symbol", symbol.getName(), " array index=", index);
1209 loadCompilerConstant(VARARGS);
1210 load(index);
1211 ArgumentSetter.SET_ARRAY_ELEMENT.invoke(this);
1212 }
1213 } else {
1214 debug("dead store symbol ", symbol.getName(), " type=", type);
1215 pop();
1216 }
1217 }
1219 /**
1220 * Pop a value from the stack and store it in a local variable slot. Note that in contrast with
1221 * {@link #store(Symbol, Type)}, this method does not adjust the local variable table, nor marks slots for
1222 * alternative value types for the symbol as being dead. For that reason, this method is usually not called
1223 * directly. Notable exceptions are temporary internal locals (e.g. quick store, last-catch-condition, etc.) that
1224 * are not desired to show up in the local variable table.
1225 *
1226 * @param type the type to pop
1227 * @param slot the slot
1228 */
1229 void storeHidden(final Type type, final int slot) {
1230 storeHidden(type, slot, true);
1231 }
1233 void storeHidden(final Type type, final int slot, final boolean onlyLiveSymbolValue) {
1234 explicitStore(type, slot);
1235 stack.onLocalStore(type, slot, onlyLiveSymbolValue);
1236 }
1238 void storeTemp(final Type type, final int slot) {
1239 explicitStore(type, slot);
1240 defineTemporaryLocalVariable(slot, slot + type.getSlots());
1241 onLocalStore(type, slot);
1242 }
1244 void onLocalStore(final Type type, final int slot) {
1245 stack.onLocalStore(type, slot, true);
1246 }
1248 private void explicitStore(final Type type, final int slot) {
1249 assert slot != -1;
1250 debug("explicit store", type, slot);
1251 popType(type);
1252 type.store(method, slot);
1253 }
1255 /**
1256 * Marks a range of slots as belonging to a defined local variable. The slots will start out with no live value
1257 * in them.
1258 * @param fromSlot first slot, inclusive.
1259 * @param toSlot last slot, exclusive.
1260 */
1261 void defineBlockLocalVariable(final int fromSlot, final int toSlot) {
1262 stack.defineBlockLocalVariable(fromSlot, toSlot);
1263 }
1265 /**
1266 * Marks a range of slots as belonging to a defined temporary local variable. The slots will start out with no
1267 * live value in them.
1268 * @param fromSlot first slot, inclusive.
1269 * @param toSlot last slot, exclusive.
1270 */
1271 void defineTemporaryLocalVariable(final int fromSlot, final int toSlot) {
1272 stack.defineTemporaryLocalVariable(fromSlot, toSlot);
1273 }
1275 /**
1276 * Defines a new temporary local variable and returns its allocated index.
1277 * @param width the required width (in slots) for the new variable.
1278 * @return the bytecode slot index where the newly allocated local begins.
1279 */
1280 int defineTemporaryLocalVariable(final int width) {
1281 return stack.defineTemporaryLocalVariable(width);
1282 }
1284 void undefineLocalVariables(final int fromSlot, final boolean canTruncateSymbol) {
1285 if(isReachable()) {
1286 stack.undefineLocalVariables(fromSlot, canTruncateSymbol);
1287 }
1288 }
1290 List<Type> getLocalVariableTypes() {
1291 return stack.localVariableTypes;
1292 }
1294 List<Type> getWidestLiveLocals(final List<Type> localTypes) {
1295 return stack.getWidestLiveLocals(localTypes);
1296 }
1298 String markSymbolBoundariesInLvarTypesDescriptor(final String lvarDescriptor) {
1299 return stack.markSymbolBoundariesInLvarTypesDescriptor(lvarDescriptor);
1300 }
1302 /**
1303 * Increment/Decrement a local integer by the given value.
1304 *
1305 * @param slot the int slot
1306 * @param increment the amount to increment
1307 */
1308 void iinc(final int slot, final int increment) {
1309 debug("iinc");
1310 method.visitIincInsn(slot, increment);
1311 }
1313 /**
1314 * Pop an exception object from the stack and generate code
1315 * for throwing it
1316 */
1317 public void athrow() {
1318 debug("athrow");
1319 final Type receiver = popType(Type.OBJECT);
1320 assert Throwable.class.isAssignableFrom(receiver.getTypeClass()) : receiver.getTypeClass();
1321 method.visitInsn(ATHROW);
1322 doesNotContinueSequentially();
1323 }
1325 /**
1326 * Pop an object from the stack and perform an instanceof
1327 * operation, given a classDescriptor to compare it to.
1328 * Push the boolean result 1/0 as an int to the stack
1329 *
1330 * @param classDescriptor descriptor of the class to type check against
1331 *
1332 * @return the method emitter
1333 */
1334 MethodEmitter _instanceof(final String classDescriptor) {
1335 debug("instanceof", classDescriptor);
1336 popType(Type.OBJECT);
1337 method.visitTypeInsn(INSTANCEOF, classDescriptor);
1338 pushType(Type.INT);
1339 return this;
1340 }
1342 /**
1343 * Pop an object from the stack and perform an instanceof
1344 * operation, given a classDescriptor to compare it to.
1345 * Push the boolean result 1/0 as an int to the stack
1346 *
1347 * @param clazz the type to check instanceof against
1348 *
1349 * @return the method emitter
1350 */
1351 MethodEmitter _instanceof(final Class<?> clazz) {
1352 return _instanceof(CompilerConstants.className(clazz));
1353 }
1355 /**
1356 * Perform a checkcast operation on the object at the top of the
1357 * stack.
1358 *
1359 * @param classDescriptor descriptor of the class to type check against
1360 *
1361 * @return the method emitter
1362 */
1363 MethodEmitter checkcast(final String classDescriptor) {
1364 debug("checkcast", classDescriptor);
1365 assert peekType().isObject();
1366 method.visitTypeInsn(CHECKCAST, classDescriptor);
1367 return this;
1368 }
1370 /**
1371 * Perform a checkcast operation on the object at the top of the
1372 * stack.
1373 *
1374 * @param clazz class to checkcast against
1375 *
1376 * @return the method emitter
1377 */
1378 MethodEmitter checkcast(final Class<?> clazz) {
1379 return checkcast(CompilerConstants.className(clazz));
1380 }
1382 /**
1383 * Instantiate a new array given a length that is popped
1384 * from the stack and the array type
1385 *
1386 * @param arrayType the type of the array
1387 *
1388 * @return the method emitter
1389 */
1390 MethodEmitter newarray(final ArrayType arrayType) {
1391 debug("newarray ", "arrayType=", arrayType);
1392 popType(Type.INT); //LENGTH
1393 pushType(arrayType.newarray(method));
1394 return this;
1395 }
1397 /**
1398 * Instantiate a multidimensional array with a given number of dimensions.
1399 * On the stack are dim lengths of the sub arrays.
1400 *
1401 * @param arrayType type of the array
1402 * @param dims number of dimensions
1403 *
1404 * @return the method emitter
1405 */
1406 MethodEmitter multinewarray(final ArrayType arrayType, final int dims) {
1407 debug("multianewarray ", arrayType, dims);
1408 for (int i = 0; i < dims; i++) {
1409 popType(Type.INT); //LENGTH
1410 }
1411 pushType(arrayType.newarray(method, dims));
1412 return this;
1413 }
1415 /**
1416 * Helper function to pop and type check the appropriate arguments
1417 * from the stack given a method signature
1418 *
1419 * @param signature method signature
1420 *
1421 * @return return type of method
1422 */
1423 private Type fixParamStack(final String signature) {
1424 final Type[] params = Type.getMethodArguments(signature);
1425 for (int i = params.length - 1; i >= 0; i--) {
1426 popType(params[i]);
1427 }
1428 final Type returnType = Type.getMethodReturnType(signature);
1429 return returnType;
1430 }
1432 /**
1433 * Generate an invocation to a Call structure
1434 * @see CompilerConstants
1435 *
1436 * @param call the call object
1437 *
1438 * @return the method emitter
1439 */
1440 MethodEmitter invoke(final Call call) {
1441 return call.invoke(this);
1442 }
1444 private MethodEmitter invoke(final int opcode, final String className, final String methodName, final String methodDescriptor, final boolean hasReceiver) {
1445 final Type returnType = fixParamStack(methodDescriptor);
1447 if (hasReceiver) {
1448 popType(Type.OBJECT);
1449 }
1451 method.visitMethodInsn(opcode, className, methodName, methodDescriptor, opcode == INVOKEINTERFACE);
1453 if (returnType != null) {
1454 pushType(returnType);
1455 }
1457 return this;
1458 }
1460 /**
1461 * Pop receiver from stack, perform an invoke special
1462 *
1463 * @param className class name
1464 * @param methodName method name
1465 * @param methodDescriptor descriptor
1466 *
1467 * @return the method emitter
1468 */
1469 MethodEmitter invokespecial(final String className, final String methodName, final String methodDescriptor) {
1470 debug("invokespecial", className, ".", methodName, methodDescriptor);
1471 return invoke(INVOKESPECIAL, className, methodName, methodDescriptor, true);
1472 }
1474 /**
1475 * Pop receiver from stack, perform an invoke virtual, push return value if any
1476 *
1477 * @param className class name
1478 * @param methodName method name
1479 * @param methodDescriptor descriptor
1480 *
1481 * @return the method emitter
1482 */
1483 MethodEmitter invokevirtual(final String className, final String methodName, final String methodDescriptor) {
1484 debug("invokevirtual", className, ".", methodName, methodDescriptor, " ", stack);
1485 return invoke(INVOKEVIRTUAL, className, methodName, methodDescriptor, true);
1486 }
1488 /**
1489 * Perform an invoke static and push the return value if any
1490 *
1491 * @param className class name
1492 * @param methodName method name
1493 * @param methodDescriptor descriptor
1494 *
1495 * @return the method emitter
1496 */
1497 MethodEmitter invokestatic(final String className, final String methodName, final String methodDescriptor) {
1498 debug("invokestatic", className, ".", methodName, methodDescriptor);
1499 invoke(INVOKESTATIC, className, methodName, methodDescriptor, false);
1500 return this;
1501 }
1503 /**
1504 * Perform an invoke static and replace the return type if we know better, e.g. Global.allocate
1505 * that allocates an array should return an ObjectArray type as a NativeArray counts as that
1506 *
1507 * @param className class name
1508 * @param methodName method name
1509 * @param methodDescriptor descriptor
1510 * @param returnType return type override
1511 *
1512 * @return the method emitter
1513 */
1514 MethodEmitter invokestatic(final String className, final String methodName, final String methodDescriptor, final Type returnType) {
1515 invokestatic(className, methodName, methodDescriptor);
1516 popType();
1517 pushType(returnType);
1518 return this;
1519 }
1521 /**
1522 * Pop receiver from stack, perform an invoke interface and push return value if any
1523 *
1524 * @param className class name
1525 * @param methodName method name
1526 * @param methodDescriptor descriptor
1527 *
1528 * @return the method emitter
1529 */
1530 MethodEmitter invokeinterface(final String className, final String methodName, final String methodDescriptor) {
1531 debug("invokeinterface", className, ".", methodName, methodDescriptor);
1532 return invoke(INVOKEINTERFACE, className, methodName, methodDescriptor, true);
1533 }
1535 static jdk.internal.org.objectweb.asm.Label[] getLabels(final Label... table) {
1536 final jdk.internal.org.objectweb.asm.Label[] internalLabels = new jdk.internal.org.objectweb.asm.Label[table.length];
1537 for (int i = 0; i < table.length; i++) {
1538 internalLabels[i] = table[i].getLabel();
1539 }
1540 return internalLabels;
1541 }
1543 /**
1544 * Generate a lookup switch, popping the switch value from the stack
1545 *
1546 * @param defaultLabel default label
1547 * @param values case values for the table
1548 * @param table default label
1549 */
1550 void lookupswitch(final Label defaultLabel, final int[] values, final Label... table) {//Collection<Label> table) {
1551 debug("lookupswitch", peekType());
1552 adjustStackForSwitch(defaultLabel, table);
1553 method.visitLookupSwitchInsn(defaultLabel.getLabel(), values, getLabels(table));
1554 doesNotContinueSequentially();
1555 }
1557 /**
1558 * Generate a table switch
1559 * @param lo low value
1560 * @param hi high value
1561 * @param defaultLabel default label
1562 * @param table label table
1563 */
1564 void tableswitch(final int lo, final int hi, final Label defaultLabel, final Label... table) {
1565 debug("tableswitch", peekType());
1566 adjustStackForSwitch(defaultLabel, table);
1567 method.visitTableSwitchInsn(lo, hi, defaultLabel.getLabel(), getLabels(table));
1568 doesNotContinueSequentially();
1569 }
1571 private void adjustStackForSwitch(final Label defaultLabel, final Label... table) {
1572 popType(Type.INT);
1573 joinTo(defaultLabel);
1574 for(final Label label: table) {
1575 joinTo(label);
1576 }
1577 }
1579 /**
1580 * Abstraction for performing a conditional jump of any type
1581 *
1582 * @see Condition
1583 *
1584 * @param cond the condition to test
1585 * @param trueLabel the destination label is condition is true
1586 */
1587 void conditionalJump(final Condition cond, final Label trueLabel) {
1588 conditionalJump(cond, cond != Condition.GT && cond != Condition.GE, trueLabel);
1589 }
1591 /**
1592 * Abstraction for performing a conditional jump of any type,
1593 * including a dcmpg/dcmpl semantic for doubles.
1594 *
1595 * @param cond the condition to test
1596 * @param isCmpG is this a dcmpg for numbers, false if it's a dcmpl
1597 * @param trueLabel the destination label if condition is true
1598 */
1599 void conditionalJump(final Condition cond, final boolean isCmpG, final Label trueLabel) {
1600 if (peekType().isCategory2()) {
1601 debug("[ld]cmp isCmpG=", isCmpG);
1602 pushType(get2n().cmp(method, isCmpG));
1603 jump(Condition.toUnary(cond), trueLabel, 1);
1604 } else {
1605 debug("if", cond);
1606 jump(Condition.toBinary(cond, peekType().isObject()), trueLabel, 2);
1607 }
1608 }
1610 /**
1611 * Perform a non void return, popping the type from the stack
1612 *
1613 * @param type the type for the return
1614 */
1615 void _return(final Type type) {
1616 debug("return", type);
1617 assert stack.size() == 1 : "Only return value on stack allowed at return point - depth=" + stack.size() + " stack = " + stack;
1618 final Type stackType = peekType();
1619 if (!Type.areEquivalent(type, stackType)) {
1620 convert(type);
1621 }
1622 popType(type)._return(method);
1623 doesNotContinueSequentially();
1624 }
1626 /**
1627 * Perform a return using the stack top value as the guide for the type
1628 */
1629 void _return() {
1630 _return(peekType());
1631 }
1633 /**
1634 * Perform a void return.
1635 */
1636 void returnVoid() {
1637 debug("return [void]");
1638 assert stack.isEmpty() : stack;
1639 method.visitInsn(RETURN);
1640 doesNotContinueSequentially();
1641 }
1643 /**
1644 * Perform a comparison of two number types that are popped from the stack
1645 *
1646 * @param isCmpG is this a dcmpg semantic, false if it's a dcmpl semantic
1647 *
1648 * @return the method emitter
1649 */
1650 MethodEmitter cmp(final boolean isCmpG) {
1651 pushType(get2n().cmp(method, isCmpG));
1652 return this;
1653 }
1655 /**
1656 * Helper function for jumps, conditional or not
1657 * @param opcode opcode for jump
1658 * @param label destination
1659 * @param n elements on stack to compare, 0-2
1660 */
1661 private void jump(final int opcode, final Label label, final int n) {
1662 for (int i = 0; i < n; i++) {
1663 assert peekType().isInteger() || peekType().isBoolean() || peekType().isObject() : "expecting integer type or object for jump, but found " + peekType();
1664 popType();
1665 }
1666 joinTo(label);
1667 method.visitJumpInsn(opcode, label.getLabel());
1668 }
1670 /**
1671 * Generate an if_acmpeq
1672 *
1673 * @param label label to true case
1674 */
1675 void if_acmpeq(final Label label) {
1676 debug("if_acmpeq", label);
1677 jump(IF_ACMPEQ, label, 2);
1678 }
1680 /**
1681 * Generate an if_acmpne
1682 *
1683 * @param label label to true case
1684 */
1685 void if_acmpne(final Label label) {
1686 debug("if_acmpne", label);
1687 jump(IF_ACMPNE, label, 2);
1688 }
1690 /**
1691 * Generate an ifnull
1692 *
1693 * @param label label to true case
1694 */
1695 void ifnull(final Label label) {
1696 debug("ifnull", label);
1697 jump(IFNULL, label, 1);
1698 }
1700 /**
1701 * Generate an ifnonnull
1702 *
1703 * @param label label to true case
1704 */
1705 void ifnonnull(final Label label) {
1706 debug("ifnonnull", label);
1707 jump(IFNONNULL, label, 1);
1708 }
1710 /**
1711 * Generate an ifeq
1712 *
1713 * @param label label to true case
1714 */
1715 void ifeq(final Label label) {
1716 debug("ifeq ", label);
1717 jump(IFEQ, label, 1);
1718 }
1720 /**
1721 * Generate an if_icmpeq
1722 *
1723 * @param label label to true case
1724 */
1725 void if_icmpeq(final Label label) {
1726 debug("if_icmpeq", label);
1727 jump(IF_ICMPEQ, label, 2);
1728 }
1730 /**
1731 * Generate an if_ne
1732 *
1733 * @param label label to true case
1734 */
1735 void ifne(final Label label) {
1736 debug("ifne", label);
1737 jump(IFNE, label, 1);
1738 }
1740 /**
1741 * Generate an if_icmpne
1742 *
1743 * @param label label to true case
1744 */
1745 void if_icmpne(final Label label) {
1746 debug("if_icmpne", label);
1747 jump(IF_ICMPNE, label, 2);
1748 }
1750 /**
1751 * Generate an iflt
1752 *
1753 * @param label label to true case
1754 */
1755 void iflt(final Label label) {
1756 debug("iflt", label);
1757 jump(IFLT, label, 1);
1758 }
1760 /**
1761 * Generate an if_icmplt
1762 *
1763 * @param label label to true case
1764 */
1765 void if_icmplt(final Label label) {
1766 debug("if_icmplt", label);
1767 jump(IF_ICMPLT, label, 2);
1768 }
1770 /**
1771 * Generate an ifle
1772 *
1773 * @param label label to true case
1774 */
1775 void ifle(final Label label) {
1776 debug("ifle", label);
1777 jump(IFLE, label, 1);
1778 }
1780 /**
1781 * Generate an if_icmple
1782 *
1783 * @param label label to true case
1784 */
1785 void if_icmple(final Label label) {
1786 debug("if_icmple", label);
1787 jump(IF_ICMPLE, label, 2);
1788 }
1790 /**
1791 * Generate an ifgt
1792 *
1793 * @param label label to true case
1794 */
1795 void ifgt(final Label label) {
1796 debug("ifgt", label);
1797 jump(IFGT, label, 1);
1798 }
1800 /**
1801 * Generate an if_icmpgt
1802 *
1803 * @param label label to true case
1804 */
1805 void if_icmpgt(final Label label) {
1806 debug("if_icmpgt", label);
1807 jump(IF_ICMPGT, label, 2);
1808 }
1810 /**
1811 * Generate an ifge
1812 *
1813 * @param label label to true case
1814 */
1815 void ifge(final Label label) {
1816 debug("ifge", label);
1817 jump(IFGE, label, 1);
1818 }
1820 /**
1821 * Generate an if_icmpge
1822 *
1823 * @param label label to true case
1824 */
1825 void if_icmpge(final Label label) {
1826 debug("if_icmpge", label);
1827 jump(IF_ICMPGE, label, 2);
1828 }
1830 /**
1831 * Unconditional jump to a label
1832 *
1833 * @param label destination label
1834 */
1835 void _goto(final Label label) {
1836 debug("goto", label);
1837 jump(GOTO, label, 0);
1838 doesNotContinueSequentially(); //whoever reaches the point after us provides the stack, because we don't
1839 }
1841 /**
1842 * Unconditional jump to the start label of a loop. It differs from ordinary {@link #_goto(Label)} in that it will
1843 * preserve the current label stack, as the next instruction after the goto is loop body that the loop will come
1844 * back to. Also used to jump at the start label of the continuation handler, as it behaves much like a loop test in
1845 * the sense that after it is evaluated, it also jumps backwards.
1846 *
1847 * @param loopStart start label of a loop
1848 */
1849 void gotoLoopStart(final Label loopStart) {
1850 debug("goto (loop)", loopStart);
1851 jump(GOTO, loopStart, 0);
1852 }
1854 /**
1855 * Unconditional jump without any control flow and data flow testing. You should not normally use this method when
1856 * generating code, except if you're very sure that you know what you're doing. Normally only used for the
1857 * admittedly torturous control flow of continuation handler plumbing.
1858 * @param target the target of the jump
1859 */
1860 void uncheckedGoto(final Label target) {
1861 method.visitJumpInsn(GOTO, target.getLabel());
1862 }
1864 /**
1865 * Potential transfer of control to a catch block.
1866 *
1867 * @param catchLabel destination catch label
1868 */
1869 void canThrow(final Label catchLabel) {
1870 catchLabel.joinFromTry(stack, false);
1871 }
1873 /**
1874 * A join in control flow - helper function that makes sure all entry stacks
1875 * discovered for the join point so far are equivalent
1876 *
1877 * MergeStack: we are about to enter a label. If its stack, label.getStack() is null
1878 * we have never been here before. Then we are expected to carry a stack with us.
1879 *
1880 * @param label label
1881 */
1882 private void joinTo(final Label label) {
1883 assert isReachable();
1884 label.joinFrom(stack);
1885 }
1887 /**
1888 * Register a new label, enter it here.
1889 * @param label
1890 */
1891 void label(final Label label) {
1892 breakLabel(label, -1);
1893 }
1895 /**
1896 * Register a new break target label, enter it here.
1897 *
1898 * @param label the label
1899 * @param liveLocals the number of live locals at this label
1900 */
1901 void breakLabel(final Label label, final int liveLocals) {
1902 if (!isReachable()) {
1903 // If we emit a label, and the label's stack is null, it must not be reachable.
1904 assert (label.getStack() == null) != label.isReachable();
1905 } else {
1906 joinTo(label);
1907 }
1908 // Use label's stack as we might have no stack.
1909 final Label.Stack labelStack = label.getStack();
1910 stack = labelStack == null ? null : labelStack.clone();
1911 if(stack != null && label.isBreakTarget() && liveLocals != -1) {
1912 // This has to be done because we might not have another frame to provide us with its firstTemp if the label
1913 // is only reachable through a break or continue statement; also in this case, the frame can actually
1914 // give us a higher number of live locals, e.g. if it comes from a catch. Typical example:
1915 // for(;;) { try{ throw 0; } catch(e) { break; } }.
1916 // Since the for loop can only be exited through the break in the catch block, it'll bring with it the
1917 // "e" as a live local, and we need to trim it off here.
1918 assert stack.firstTemp >= liveLocals;
1919 stack.firstTemp = liveLocals;
1920 }
1921 debug_label(label);
1922 method.visitLabel(label.getLabel());
1923 }
1925 /**
1926 * Pop element from stack, convert to given type
1927 *
1928 * @param to type to convert to
1929 *
1930 * @return the method emitter
1931 */
1932 MethodEmitter convert(final Type to) {
1933 final Type from = peekType();
1934 final Type type = from.convert(method, to);
1935 if (type != null) {
1936 if (!from.isEquivalentTo(to)) {
1937 debug("convert", from, "->", to);
1938 }
1939 if (type != from) {
1940 final int l0 = stack.getTopLocalLoad();
1941 popType();
1942 pushType(type);
1943 // NOTE: conversions from a primitive type are considered to preserve the "load" property of the value
1944 // on the stack. Otherwise we could introduce temporary locals in a deoptimized rest-of (e.g. doing an
1945 // "i < x.length" where "i" is int and ".length" gets deoptimized to long would end up converting i to
1946 // long with "ILOAD i; I2L; LSTORE tmp; LLOAD tmp;"). Such additional temporary would cause an error
1947 // when restoring the state of the function for rest-of execution, as the not-yet deoptimized variant
1948 // would have the (now invalidated) assumption that "x.length" is an int, so it wouldn't have the I2L,
1949 // and therefore neither the subsequent LSTORE tmp; LLOAD tmp;. By making sure conversions from a
1950 // primitive type don't erase the "load" information, we don't introduce temporaries in the deoptimized
1951 // rest-of that didn't exist in the more optimistic version that triggered the deoptimization.
1952 // NOTE: as a more general observation, we could theoretically track the operations required to
1953 // reproduce any stack value as long as they are all local loads, constant loads, and stack operations.
1954 // We won't go there in the current system
1955 if(!from.isObject()) {
1956 stack.markLocalLoad(l0);
1957 }
1958 }
1959 }
1960 return this;
1961 }
1963 /**
1964 * Helper function - expect two types that are equivalent
1965 *
1966 * @return common type
1967 */
1968 private Type get2() {
1969 final Type p0 = popType();
1970 final Type p1 = popType();
1971 assert p0.isEquivalentTo(p1) : "expecting equivalent types on stack but got " + p0 + " and " + p1;
1972 return p0;
1973 }
1975 /**
1976 * Helper function - expect two types that are integer types and equivalent
1977 *
1978 * @return common type
1979 */
1980 private BitwiseType get2i() {
1981 final BitwiseType p0 = popBitwise();
1982 final BitwiseType p1 = popBitwise();
1983 assert p0.isEquivalentTo(p1) : "expecting equivalent types on stack but got " + p0 + " and " + p1;
1984 return p0;
1985 }
1987 /**
1988 * Helper function - expect two types that are numbers and equivalent
1989 *
1990 * @return common type
1991 */
1992 private NumericType get2n() {
1993 final NumericType p0 = popNumeric();
1994 final NumericType p1 = popNumeric();
1995 assert p0.isEquivalentTo(p1) : "expecting equivalent types on stack but got " + p0 + " and " + p1;
1996 return p0;
1997 }
1999 /**
2000 * Pop two numbers, perform addition and push result
2001 *
2002 * @return the method emitter
2003 */
2004 MethodEmitter add(final int programPoint) {
2005 debug("add");
2006 pushType(get2().add(method, programPoint));
2007 return this;
2008 }
2010 /**
2011 * Pop two numbers, perform subtraction and push result
2012 *
2013 * @return the method emitter
2014 */
2015 MethodEmitter sub(final int programPoint) {
2016 debug("sub");
2017 pushType(get2n().sub(method, programPoint));
2018 return this;
2019 }
2021 /**
2022 * Pop two numbers, perform multiplication and push result
2023 *
2024 * @return the method emitter
2025 */
2026 MethodEmitter mul(final int programPoint) {
2027 debug("mul ");
2028 pushType(get2n().mul(method, programPoint));
2029 return this;
2030 }
2032 /**
2033 * Pop two numbers, perform division and push result
2034 *
2035 * @return the method emitter
2036 */
2037 MethodEmitter div(final int programPoint) {
2038 debug("div");
2039 pushType(get2n().div(method, programPoint));
2040 return this;
2041 }
2043 /**
2044 * Pop two numbers, calculate remainder and push result
2045 *
2046 * @return the method emitter
2047 */
2048 MethodEmitter rem(final int programPoint) {
2049 debug("rem");
2050 pushType(get2n().rem(method, programPoint));
2051 return this;
2052 }
2054 /**
2055 * Retrieve the top <tt>count</tt> types on the stack without modifying it.
2056 *
2057 * @param count number of types to return
2058 * @return array of Types
2059 */
2060 protected Type[] getTypesFromStack(final int count) {
2061 return stack.getTopTypes(count);
2062 }
2064 int[] getLocalLoadsOnStack(final int from, final int to) {
2065 return stack.getLocalLoads(from, to);
2066 }
2068 int getStackSize() {
2069 return stack.size();
2070 }
2072 int getFirstTemp() {
2073 return stack.firstTemp;
2074 }
2076 int getUsedSlotsWithLiveTemporaries() {
2077 return stack.getUsedSlotsWithLiveTemporaries();
2078 }
2080 /**
2081 * Helper function to generate a function signature based on stack contents
2082 * and argument count and return type
2083 *
2084 * @param returnType return type
2085 * @param argCount argument count
2086 *
2087 * @return function signature for stack contents
2088 */
2089 private String getDynamicSignature(final Type returnType, final int argCount) {
2090 final Type[] paramTypes = new Type[argCount];
2092 int pos = 0;
2093 for (int i = argCount - 1; i >= 0; i--) {
2094 Type pt = stack.peek(pos++);
2095 // "erase" specific ScriptObject subtype info - except for NativeArray.
2096 // NativeArray is used for array/List/Deque conversion for Java calls.
2097 if (ScriptObject.class.isAssignableFrom(pt.getTypeClass()) &&
2098 !NativeArray.class.isAssignableFrom(pt.getTypeClass())) {
2099 pt = Type.SCRIPT_OBJECT;
2100 }
2101 paramTypes[i] = pt;
2102 }
2103 final String descriptor = Type.getMethodDescriptor(returnType, paramTypes);
2104 for (int i = 0; i < argCount; i++) {
2105 popType(paramTypes[argCount - i - 1]);
2106 }
2108 return descriptor;
2109 }
2111 MethodEmitter invalidateSpecialName(final String name) {
2112 switch (name) {
2113 case "apply":
2114 case "call":
2115 debug("invalidate_name", "name=", name);
2116 load("Function");
2117 invoke(ScriptRuntime.INVALIDATE_RESERVED_BUILTIN_NAME);
2118 break;
2119 default:
2120 break;
2121 }
2122 return this;
2123 }
2125 /**
2126 * Generate a dynamic new
2127 *
2128 * @param argCount number of arguments
2129 * @param flags callsite flags
2130 *
2131 * @return the method emitter
2132 */
2133 MethodEmitter dynamicNew(final int argCount, final int flags) {
2134 return dynamicNew(argCount, flags, null);
2135 }
2137 /**
2138 * Generate a dynamic new
2139 *
2140 * @param argCount number of arguments
2141 * @param flags callsite flags
2142 * @param msg additional message to be used when reporting error
2143 *
2144 * @return the method emitter
2145 */
2146 MethodEmitter dynamicNew(final int argCount, final int flags, final String msg) {
2147 assert !isOptimistic(flags);
2148 debug("dynamic_new", "argcount=", argCount);
2149 final String signature = getDynamicSignature(Type.OBJECT, argCount);
2150 method.visitInvokeDynamicInsn(
2151 msg != null && msg.length() < LARGE_STRING_THRESHOLD? "dyn:new:" + NameCodec.encode(msg) : "dyn:new",
2152 signature, LINKERBOOTSTRAP, flags);
2153 pushType(Type.OBJECT); //TODO fix result type
2154 return this;
2155 }
2157 /**
2158 * Generate a dynamic call
2159 *
2160 * @param returnType return type
2161 * @param argCount number of arguments
2162 * @param flags callsite flags
2163 *
2164 * @return the method emitter
2165 */
2166 MethodEmitter dynamicCall(final Type returnType, final int argCount, final int flags) {
2167 return dynamicCall(returnType, argCount, flags, null);
2168 }
2170 /**
2171 * Generate a dynamic call
2172 *
2173 * @param returnType return type
2174 * @param argCount number of arguments
2175 * @param flags callsite flags
2176 * @param msg additional message to be used when reporting error
2177 *
2178 * @return the method emitter
2179 */
2180 MethodEmitter dynamicCall(final Type returnType, final int argCount, final int flags, final String msg) {
2181 debug("dynamic_call", "args=", argCount, "returnType=", returnType);
2182 final String signature = getDynamicSignature(returnType, argCount); // +1 because the function itself is the 1st parameter for dynamic calls (what you call - call target)
2183 debug(" signature", signature);
2184 method.visitInvokeDynamicInsn(
2185 msg != null && msg.length() < LARGE_STRING_THRESHOLD? "dyn:call:" + NameCodec.encode(msg) : "dyn:call",
2186 signature, LINKERBOOTSTRAP, flags);
2187 pushType(returnType);
2189 return this;
2190 }
2192 MethodEmitter dynamicArrayPopulatorCall(final int argCount, final int startIndex) {
2193 debug("populate_array", "args=", argCount, "startIndex=", startIndex);
2194 final String signature = getDynamicSignature(Type.OBJECT_ARRAY, argCount);
2195 method.visitInvokeDynamicInsn("populateArray", signature, POPULATE_ARRAY_BOOTSTRAP, startIndex);
2196 pushType(Type.OBJECT_ARRAY);
2197 return this;
2198 }
2200 /**
2201 * Generate dynamic getter. Pop scope from stack. Push result
2202 *
2203 * @param valueType type of the value to set
2204 * @param name name of property
2205 * @param flags call site flags
2206 * @param isMethod should it prefer retrieving methods
2207 * @param isIndex is this an index operation?
2208 * @return the method emitter
2209 */
2210 MethodEmitter dynamicGet(final Type valueType, final String name, final int flags, final boolean isMethod, final boolean isIndex) {
2211 if (name.length() > LARGE_STRING_THRESHOLD) { // use getIndex for extremely long names
2212 return load(name).dynamicGetIndex(valueType, flags, isMethod);
2213 }
2215 debug("dynamic_get", name, valueType, getProgramPoint(flags));
2217 Type type = valueType;
2218 if (type.isObject() || type.isBoolean()) {
2219 type = Type.OBJECT; //promote e.g strings to object generic setter
2220 }
2222 popType(Type.SCOPE);
2223 method.visitInvokeDynamicInsn(dynGetOperation(isMethod, isIndex) + ':' + NameCodec.encode(name),
2224 Type.getMethodDescriptor(type, Type.OBJECT), LINKERBOOTSTRAP, flags);
2226 pushType(type);
2227 convert(valueType); //most probably a nop
2229 return this;
2230 }
2232 /**
2233 * Generate dynamic setter. Pop receiver and property from stack.
2234 *
2235 * @param name name of property
2236 * @param flags call site flags
2237 * @param isIndex is this an index operation?
2238 */
2239 void dynamicSet(final String name, final int flags, final boolean isIndex) {
2240 if (name.length() > LARGE_STRING_THRESHOLD) { // use setIndex for extremely long names
2241 load(name).swap().dynamicSetIndex(flags);
2242 return;
2243 }
2245 assert !isOptimistic(flags);
2246 debug("dynamic_set", name, peekType());
2248 Type type = peekType();
2249 if (type.isObject() || type.isBoolean()) { //promote strings to objects etc
2250 type = Type.OBJECT;
2251 convert(Type.OBJECT); //TODO bad- until we specialize boolean setters,
2252 }
2253 popType(type);
2254 popType(Type.SCOPE);
2256 method.visitInvokeDynamicInsn(dynSetOperation(isIndex) + ':' + NameCodec.encode(name),
2257 methodDescriptor(void.class, Object.class, type.getTypeClass()), LINKERBOOTSTRAP, flags);
2258 }
2260 /**
2261 * Dynamic getter for indexed structures. Pop index and receiver from stack,
2262 * generate appropriate signatures based on types
2263 *
2264 * @param result result type for getter
2265 * @param flags call site flags for getter
2266 * @param isMethod should it prefer retrieving methods
2267 *
2268 * @return the method emitter
2269 */
2270 MethodEmitter dynamicGetIndex(final Type result, final int flags, final boolean isMethod) {
2271 assert result.getTypeClass().isPrimitive() || result.getTypeClass() == Object.class;
2272 debug("dynamic_get_index", peekType(1), "[", peekType(), "]", getProgramPoint(flags));
2274 Type resultType = result;
2275 if (result.isBoolean()) {
2276 resultType = Type.OBJECT; // INT->OBJECT to avoid another dimension of cross products in the getters. TODO
2277 }
2279 Type index = peekType();
2280 if (index.isObject() || index.isBoolean()) {
2281 index = Type.OBJECT; //e.g. string->object
2282 convert(Type.OBJECT);
2283 }
2284 popType();
2286 popType(Type.OBJECT);
2288 final String signature = Type.getMethodDescriptor(resultType, Type.OBJECT /*e.g STRING->OBJECT*/, index);
2290 method.visitInvokeDynamicInsn(dynGetOperation(isMethod, true), signature, LINKERBOOTSTRAP, flags);
2291 pushType(resultType);
2293 if (result.isBoolean()) {
2294 convert(Type.BOOLEAN);
2295 }
2297 return this;
2298 }
2300 private static String getProgramPoint(final int flags) {
2301 if((flags & CALLSITE_OPTIMISTIC) == 0) {
2302 return "";
2303 }
2304 return "pp=" + String.valueOf((flags & (-1 << CALLSITE_PROGRAM_POINT_SHIFT)) >> CALLSITE_PROGRAM_POINT_SHIFT);
2305 }
2307 /**
2308 * Dynamic setter for indexed structures. Pop value, index and receiver from
2309 * stack, generate appropriate signature based on types
2310 *
2311 * @param flags call site flags for setter
2312 */
2313 void dynamicSetIndex(final int flags) {
2314 assert !isOptimistic(flags);
2315 debug("dynamic_set_index", peekType(2), "[", peekType(1), "] =", peekType());
2317 Type value = peekType();
2318 if (value.isObject() || value.isBoolean()) {
2319 value = Type.OBJECT; //e.g. STRING->OBJECT - one descriptor for all object types
2320 convert(Type.OBJECT);
2321 }
2322 popType();
2324 Type index = peekType();
2325 if (index.isObject() || index.isBoolean()) {
2326 index = Type.OBJECT; //e.g. string->object
2327 convert(Type.OBJECT);
2328 }
2329 popType(index);
2331 final Type receiver = popType(Type.OBJECT);
2332 assert receiver.isObject();
2334 method.visitInvokeDynamicInsn("dyn:setElem|setProp", methodDescriptor(void.class, receiver.getTypeClass(), index.getTypeClass(), value.getTypeClass()), LINKERBOOTSTRAP, flags);
2335 }
2337 /**
2338 * Load a key value in the proper form.
2339 *
2340 * @param key
2341 */
2342 //TODO move this and break it apart
2343 MethodEmitter loadKey(final Object key) {
2344 if (key instanceof IdentNode) {
2345 method.visitLdcInsn(((IdentNode) key).getName());
2346 } else if (key instanceof LiteralNode) {
2347 method.visitLdcInsn(((LiteralNode<?>)key).getString());
2348 } else {
2349 method.visitLdcInsn(JSType.toString(key));
2350 }
2351 pushType(Type.OBJECT); //STRING
2352 return this;
2353 }
2355 @SuppressWarnings("fallthrough")
2356 private static Type fieldType(final String desc) {
2357 switch (desc) {
2358 case "Z":
2359 case "B":
2360 case "C":
2361 case "S":
2362 case "I":
2363 return Type.INT;
2364 case "F":
2365 assert false;
2366 case "D":
2367 return Type.NUMBER;
2368 case "J":
2369 return Type.LONG;
2370 default:
2371 assert desc.startsWith("[") || desc.startsWith("L") : desc + " is not an object type";
2372 switch (desc.charAt(0)) {
2373 case 'L':
2374 return Type.OBJECT;
2375 case '[':
2376 return Type.typeFor(Array.newInstance(fieldType(desc.substring(1)).getTypeClass(), 0).getClass());
2377 default:
2378 assert false;
2379 }
2380 return Type.OBJECT;
2381 }
2382 }
2384 /**
2385 * Generate get for a field access
2386 *
2387 * @param fa the field access
2388 *
2389 * @return the method emitter
2390 */
2391 MethodEmitter getField(final FieldAccess fa) {
2392 return fa.get(this);
2393 }
2395 /**
2396 * Generate set for a field access
2397 *
2398 * @param fa the field access
2399 */
2400 void putField(final FieldAccess fa) {
2401 fa.put(this);
2402 }
2404 /**
2405 * Get the value of a non-static field, pop the receiver from the stack,
2406 * push value to the stack
2407 *
2408 * @param className class
2409 * @param fieldName field name
2410 * @param fieldDescriptor field descriptor
2411 *
2412 * @return the method emitter
2413 */
2414 MethodEmitter getField(final String className, final String fieldName, final String fieldDescriptor) {
2415 debug("getfield", "receiver=", peekType(), className, ".", fieldName, fieldDescriptor);
2416 final Type receiver = popType();
2417 assert receiver.isObject();
2418 method.visitFieldInsn(GETFIELD, className, fieldName, fieldDescriptor);
2419 pushType(fieldType(fieldDescriptor));
2420 return this;
2421 }
2423 /**
2424 * Get the value of a static field, push it to the stack
2425 *
2426 * @param className class
2427 * @param fieldName field name
2428 * @param fieldDescriptor field descriptor
2429 *
2430 * @return the method emitter
2431 */
2432 MethodEmitter getStatic(final String className, final String fieldName, final String fieldDescriptor) {
2433 debug("getstatic", className, ".", fieldName, ".", fieldDescriptor);
2434 method.visitFieldInsn(GETSTATIC, className, fieldName, fieldDescriptor);
2435 pushType(fieldType(fieldDescriptor));
2436 return this;
2437 }
2439 /**
2440 * Pop value and field from stack and write to a non-static field
2441 *
2442 * @param className class
2443 * @param fieldName field name
2444 * @param fieldDescriptor field descriptor
2445 */
2446 void putField(final String className, final String fieldName, final String fieldDescriptor) {
2447 debug("putfield", "receiver=", peekType(1), "value=", peekType());
2448 popType(fieldType(fieldDescriptor));
2449 popType(Type.OBJECT);
2450 method.visitFieldInsn(PUTFIELD, className, fieldName, fieldDescriptor);
2451 }
2453 /**
2454 * Pop value from stack and write to a static field
2455 *
2456 * @param className class
2457 * @param fieldName field name
2458 * @param fieldDescriptor field descriptor
2459 */
2460 void putStatic(final String className, final String fieldName, final String fieldDescriptor) {
2461 debug("putfield", "value=", peekType());
2462 popType(fieldType(fieldDescriptor));
2463 method.visitFieldInsn(PUTSTATIC, className, fieldName, fieldDescriptor);
2464 }
2466 /**
2467 * Register line number at a label
2468 *
2469 * @param line line number
2470 */
2471 void lineNumber(final int line) {
2472 if (context.getEnv()._debug_lines) {
2473 debug_label("[LINE]", line);
2474 final jdk.internal.org.objectweb.asm.Label l = new jdk.internal.org.objectweb.asm.Label();
2475 method.visitLabel(l);
2476 method.visitLineNumber(line, l);
2477 }
2478 }
2480 void beforeJoinPoint(final JoinPredecessor joinPredecessor) {
2481 LocalVariableConversion next = joinPredecessor.getLocalVariableConversion();
2482 while(next != null) {
2483 final Symbol symbol = next.getSymbol();
2484 if(next.isLive()) {
2485 emitLocalVariableConversion(next, true);
2486 } else {
2487 markDeadLocalVariable(symbol);
2488 }
2489 next = next.getNext();
2490 }
2491 }
2493 void beforeTry(final TryNode tryNode, final Label recovery) {
2494 LocalVariableConversion next = tryNode.getLocalVariableConversion();
2495 while(next != null) {
2496 if(next.isLive()) {
2497 final Type to = emitLocalVariableConversion(next, false);
2498 recovery.getStack().onLocalStore(to, next.getSymbol().getSlot(to), true);
2499 }
2500 next = next.getNext();
2501 }
2502 }
2504 private static String dynGetOperation(final boolean isMethod, final boolean isIndex) {
2505 if (isMethod) {
2506 return isIndex ? "dyn:getMethod|getElem|getProp" : "dyn:getMethod|getProp|getElem";
2507 } else {
2508 return isIndex ? "dyn:getElem|getProp|getMethod" : "dyn:getProp|getElem|getMethod";
2509 }
2510 }
2512 private static String dynSetOperation(final boolean isIndex) {
2513 return isIndex ? "dyn:setElem|setProp" : "dyn:setProp|setElem";
2514 }
2516 private Type emitLocalVariableConversion(final LocalVariableConversion conversion, final boolean onlySymbolLiveValue) {
2517 final Type from = conversion.getFrom();
2518 final Type to = conversion.getTo();
2519 final Symbol symbol = conversion.getSymbol();
2520 assert symbol.isBytecodeLocal();
2521 if(from == Type.UNDEFINED) {
2522 loadUndefined(to);
2523 } else {
2524 load(symbol, from).convert(to);
2525 }
2526 store(symbol, to, onlySymbolLiveValue);
2527 return to;
2528 }
2530 /*
2531 * Debugging below
2532 */
2534 private final FieldAccess ERR_STREAM = staticField(System.class, "err", PrintStream.class);
2535 private final Call PRINT = virtualCallNoLookup(PrintStream.class, "print", void.class, Object.class);
2536 private final Call PRINTLN = virtualCallNoLookup(PrintStream.class, "println", void.class, Object.class);
2537 private final Call PRINT_STACKTRACE = virtualCallNoLookup(Throwable.class, "printStackTrace", void.class);
2539 /**
2540 * Emit a System.err.print statement of whatever is on top of the bytecode stack
2541 */
2542 void print() {
2543 getField(ERR_STREAM);
2544 swap();
2545 convert(Type.OBJECT);
2546 invoke(PRINT);
2547 }
2549 /**
2550 * Emit a System.err.println statement of whatever is on top of the bytecode stack
2551 */
2552 void println() {
2553 getField(ERR_STREAM);
2554 swap();
2555 convert(Type.OBJECT);
2556 invoke(PRINTLN);
2557 }
2559 /**
2560 * Emit a System.err.print statement
2561 * @param string string to print
2562 */
2563 void print(final String string) {
2564 getField(ERR_STREAM);
2565 load(string);
2566 invoke(PRINT);
2567 }
2569 /**
2570 * Emit a System.err.println statement
2571 * @param string string to print
2572 */
2573 void println(final String string) {
2574 getField(ERR_STREAM);
2575 load(string);
2576 invoke(PRINTLN);
2577 }
2579 /**
2580 * Print a stacktrace to S
2581 */
2582 void stacktrace() {
2583 _new(Throwable.class);
2584 dup();
2585 invoke(constructorNoLookup(Throwable.class));
2586 invoke(PRINT_STACKTRACE);
2587 }
2589 private static int linePrefix = 0;
2591 /**
2592 * Debug function that outputs generated bytecode and stack contents
2593 *
2594 * @param args debug information to print
2595 */
2596 @SuppressWarnings("unused")
2597 private void debug(final Object... args) {
2598 if (debug) {
2599 debug(30, args);
2600 }
2601 }
2603 private void debug(final String arg) {
2604 if (debug) {
2605 debug(30, arg);
2606 }
2607 }
2609 private void debug(final Object arg0, final Object arg1) {
2610 if (debug) {
2611 debug(30, new Object[] { arg0, arg1 });
2612 }
2613 }
2615 private void debug(final Object arg0, final Object arg1, final Object arg2) {
2616 if (debug) {
2617 debug(30, new Object[] { arg0, arg1, arg2 });
2618 }
2619 }
2621 private void debug(final Object arg0, final Object arg1, final Object arg2, final Object arg3) {
2622 if (debug) {
2623 debug(30, new Object[] { arg0, arg1, arg2, arg3 });
2624 }
2625 }
2627 private void debug(final Object arg0, final Object arg1, final Object arg2, final Object arg3, final Object arg4) {
2628 if (debug) {
2629 debug(30, new Object[] { arg0, arg1, arg2, arg3, arg4 });
2630 }
2631 }
2633 private void debug(final Object arg0, final Object arg1, final Object arg2, final Object arg3, final Object arg4, final Object arg5) {
2634 if (debug) {
2635 debug(30, new Object[] { arg0, arg1, arg2, arg3, arg4, arg5 });
2636 }
2637 }
2639 private void debug(final Object arg0, final Object arg1, final Object arg2, final Object arg3, final Object arg4, final Object arg5, final Object arg6) {
2640 if (debug) {
2641 debug(30, new Object[] { arg0, arg1, arg2, arg3, arg4, arg5, arg6 });
2642 }
2643 }
2645 /**
2646 * Debug function that outputs generated bytecode and stack contents
2647 * for a label - indentation is currently the only thing that differs
2648 *
2649 * @param args debug information to print
2650 */
2651 private void debug_label(final Object... args) {
2652 if (debug) {
2653 debug(22, args);
2654 }
2655 }
2657 private void debug(final int padConstant, final Object... args) {
2658 if (debug) {
2659 final StringBuilder sb = new StringBuilder();
2660 int pad;
2662 sb.append('#');
2663 sb.append(++linePrefix);
2665 pad = 5 - sb.length();
2666 while (pad > 0) {
2667 sb.append(' ');
2668 pad--;
2669 }
2671 if (isReachable() && !stack.isEmpty()) {
2672 sb.append("{");
2673 sb.append(stack.size());
2674 sb.append(":");
2675 for (int pos = 0; pos < stack.size(); pos++) {
2676 final Type t = stack.peek(pos);
2678 if (t == Type.SCOPE) {
2679 sb.append("scope");
2680 } else if (t == Type.THIS) {
2681 sb.append("this");
2682 } else if (t.isObject()) {
2683 String desc = t.getDescriptor();
2684 int i;
2685 for (i = 0; desc.charAt(i) == '[' && i < desc.length(); i++) {
2686 sb.append('[');
2687 }
2688 desc = desc.substring(i);
2689 final int slash = desc.lastIndexOf('/');
2690 if (slash != -1) {
2691 desc = desc.substring(slash + 1, desc.length() - 1);
2692 }
2693 if ("Object".equals(desc)) {
2694 sb.append('O');
2695 } else {
2696 sb.append(desc);
2697 }
2698 } else {
2699 sb.append(t.getDescriptor());
2700 }
2701 final int loadIndex = stack.localLoads[stack.sp - 1 - pos];
2702 if(loadIndex != Label.Stack.NON_LOAD) {
2703 sb.append('(').append(loadIndex).append(')');
2704 }
2705 if (pos + 1 < stack.size()) {
2706 sb.append(' ');
2707 }
2708 }
2709 sb.append('}');
2710 sb.append(' ');
2711 }
2713 pad = padConstant - sb.length();
2714 while (pad > 0) {
2715 sb.append(' ');
2716 pad--;
2717 }
2719 for (final Object arg : args) {
2720 sb.append(arg);
2721 sb.append(' ');
2722 }
2724 if (context.getEnv() != null) { //early bootstrap code doesn't have inited context yet
2725 log.info(sb);
2726 if (DEBUG_TRACE_LINE == linePrefix) {
2727 new Throwable().printStackTrace(log.getOutputStream());
2728 }
2729 }
2730 }
2731 }
2733 /**
2734 * Set the current function node being emitted
2735 * @param functionNode the function node
2736 */
2737 void setFunctionNode(final FunctionNode functionNode) {
2738 this.functionNode = functionNode;
2739 }
2741 /**
2742 * Invoke to enforce assertions preventing load from a local variable slot that's known to not have been written to.
2743 * Used by CodeGenerator, as it strictly enforces tracking of stores. Simpler uses of MethodEmitter, e.g. those
2744 * for creating initializers for structure classes, array getters, etc. don't have strict tracking of stores,
2745 * therefore they would fail if they had this assertion turned on.
2746 */
2747 void setPreventUndefinedLoad() {
2748 this.preventUndefinedLoad = true;
2749 }
2751 private static boolean isOptimistic(final int flags) {
2752 return (flags & CALLSITE_OPTIMISTIC) != 0;
2753 }
2754 }