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