src/jdk/nashorn/internal/codegen/MethodEmitter.java

Thu, 24 May 2018 16:39:31 +0800

author
aoqi
date
Thu, 24 May 2018 16:39:31 +0800
changeset 1959
61ffdd1b89f2
parent 1720
c09b105e7be5
parent 1490
d85f981c8cf8
permissions
-rw-r--r--

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
  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;
  1013     private Symbol getCompilerConstantSymbol(final CompilerConstants cc) {
  1014         return functionNode.getBody().getExistingSymbol(cc.symbolName());
  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();
  1026     MethodEmitter loadCompilerConstant(final CompilerConstants cc) {
  1027         return loadCompilerConstant(cc, null);
  1030     MethodEmitter loadCompilerConstant(final CompilerConstants cc, final Type type) {
  1031         if (cc == SCOPE && peekType() == Type.SCOPE) {
  1032             dup();
  1033             return this;
  1035         return load(getCompilerConstantSymbol(cc), type != null ? type : getCompilerConstantType(cc));
  1038     MethodEmitter loadScope() {
  1039         return loadCompilerConstant(SCOPE).checkcast(Scope.class);
  1042     MethodEmitter setSplitState(final int state) {
  1043         return loadScope().load(state).invoke(Scope.SET_SPLIT_STATE);
  1046     void storeCompilerConstant(final CompilerConstants cc) {
  1047         storeCompilerConstant(cc, null);
  1050     void storeCompilerConstant(final CompilerConstants cc, final Type type) {
  1051         final Symbol symbol = getCompilerConstantSymbol(cc);
  1052         if(!symbol.hasSlot()) {
  1053             return;
  1055         debug("store compiler constant ", symbol);
  1056         store(symbol, type != null ? type : getCompilerConstantType(cc));
  1059     private static Type getCompilerConstantType(final CompilerConstants cc) {
  1060         final Class<?> constantType = cc.type();
  1061         assert constantType != null;
  1062         return Type.typeFor(constantType);
  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;
  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);
  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
  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);
  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;
  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());
  1130         if(isReachable()) {
  1131             markDeadLocalVariable(symbol);
  1135     void markDeadLocalVariable(final Symbol symbol) {
  1136         if(!symbol.isDead()) {
  1137             markDeadSlots(symbol.getFirstSlot(), symbol.slotCount());
  1141     void markDeadSlots(final int firstSlot, final int slotCount) {
  1142         stack.markDeadLocalVariables(firstSlot, slotCount);
  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();
  1150         method.visitLocalVariable(name, def.type.getDescriptor(), null, def.label, label, symbol.getSlot(def.type));
  1153     void store(final Symbol symbol, final Type type) {
  1154         store(symbol, type, true);
  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)}.
  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);
  1179                 method.visitLabel(here);
  1180                 if(existingDef != null) {
  1181                     endLocalValueDef(symbol, existingDef, here);
  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);
  1192                 debug("dead store symbol ", symbol.getName(), " type=", type);
  1193                 pop();
  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);
  1213         } else {
  1214             debug("dead store symbol ", symbol.getName(), " type=", type);
  1215             pop();
  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.
  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);
  1233     void storeHidden(final Type type, final int slot, final boolean onlyLiveSymbolValue) {
  1234         explicitStore(type, slot);
  1235         stack.onLocalStore(type, slot, onlyLiveSymbolValue);
  1238     void storeTemp(final Type type, final int slot) {
  1239         explicitStore(type, slot);
  1240         defineTemporaryLocalVariable(slot, slot + type.getSlots());
  1241         onLocalStore(type, slot);
  1244     void onLocalStore(final Type type, final int slot) {
  1245         stack.onLocalStore(type, slot, true);
  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);
  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);
  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);
  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);
  1284     void undefineLocalVariables(final int fromSlot, final boolean canTruncateSymbol) {
  1285         if(isReachable()) {
  1286             stack.undefineLocalVariables(fromSlot, canTruncateSymbol);
  1290     List<Type> getLocalVariableTypes() {
  1291         return stack.localVariableTypes;
  1294     List<Type> getWidestLiveLocals(final List<Type> localTypes) {
  1295         return stack.getWidestLiveLocals(localTypes);
  1298     String markSymbolBoundariesInLvarTypesDescriptor(final String lvarDescriptor) {
  1299         return stack.markSymbolBoundariesInLvarTypesDescriptor(lvarDescriptor);
  1302     /**
  1303      * Increment/Decrement a local integer by the given value.
  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);
  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();
  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
  1330      * @param classDescriptor descriptor of the class to type check against
  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;
  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
  1347      * @param clazz the type to check instanceof against
  1349      * @return the method emitter
  1350      */
  1351     MethodEmitter _instanceof(final Class<?> clazz) {
  1352         return _instanceof(CompilerConstants.className(clazz));
  1355     /**
  1356      * Perform a checkcast operation on the object at the top of the
  1357      * stack.
  1359      * @param classDescriptor descriptor of the class to type check against
  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;
  1370     /**
  1371      * Perform a checkcast operation on the object at the top of the
  1372      * stack.
  1374      * @param clazz class to checkcast against
  1376      * @return the method emitter
  1377      */
  1378     MethodEmitter checkcast(final Class<?> clazz) {
  1379         return checkcast(CompilerConstants.className(clazz));
  1382     /**
  1383      * Instantiate a new array given a length that is popped
  1384      * from the stack and the array type
  1386      * @param arrayType the type of the array
  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;
  1397     /**
  1398      * Instantiate a multidimensional array with a given number of dimensions.
  1399      * On the stack are dim lengths of the sub arrays.
  1401      * @param arrayType type of the array
  1402      * @param dims      number of dimensions
  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
  1411         pushType(arrayType.newarray(method, dims));
  1412         return this;
  1415     /**
  1416      * Helper function to pop and type check the appropriate arguments
  1417      * from the stack given a method signature
  1419      * @param signature method signature
  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]);
  1428         final Type returnType = Type.getMethodReturnType(signature);
  1429         return returnType;
  1432     /**
  1433      * Generate an invocation to a Call structure
  1434      * @see CompilerConstants
  1436      * @param call the call object
  1438      * @return the method emitter
  1439      */
  1440     MethodEmitter invoke(final Call call) {
  1441         return call.invoke(this);
  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);
  1451         method.visitMethodInsn(opcode, className, methodName, methodDescriptor, opcode == INVOKEINTERFACE);
  1453         if (returnType != null) {
  1454             pushType(returnType);
  1457         return this;
  1460     /**
  1461      * Pop receiver from stack, perform an invoke special
  1463      * @param className        class name
  1464      * @param methodName       method name
  1465      * @param methodDescriptor descriptor
  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);
  1474     /**
  1475      * Pop receiver from stack, perform an invoke virtual, push return value if any
  1477      * @param className        class name
  1478      * @param methodName       method name
  1479      * @param methodDescriptor descriptor
  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);
  1488     /**
  1489      * Perform an invoke static and push the return value if any
  1491      * @param className        class name
  1492      * @param methodName       method name
  1493      * @param methodDescriptor descriptor
  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;
  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
  1507      * @param className        class name
  1508      * @param methodName       method name
  1509      * @param methodDescriptor descriptor
  1510      * @param returnType       return type override
  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;
  1521     /**
  1522      * Pop receiver from stack, perform an invoke interface and push return value if any
  1524      * @param className        class name
  1525      * @param methodName       method name
  1526      * @param methodDescriptor descriptor
  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);
  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();
  1540         return internalLabels;
  1543     /**
  1544      * Generate a lookup switch, popping the switch value from the stack
  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();
  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();
  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);
  1579     /**
  1580      * Abstraction for performing a conditional jump of any type
  1582      * @see Condition
  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);
  1591     /**
  1592      * Abstraction for performing a conditional jump of any type,
  1593      * including a dcmpg/dcmpl semantic for doubles.
  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);
  1610     /**
  1611      * Perform a non void return, popping the type from the stack
  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);
  1622         popType(type)._return(method);
  1623         doesNotContinueSequentially();
  1626     /**
  1627      * Perform a return using the stack top value as the guide for the type
  1628      */
  1629     void _return() {
  1630         _return(peekType());
  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();
  1643     /**
  1644      * Perform a comparison of two number types that are popped from the stack
  1646      * @param isCmpG is this a dcmpg semantic, false if it's a dcmpl semantic
  1648      * @return the method emitter
  1649      */
  1650     MethodEmitter cmp(final boolean isCmpG) {
  1651         pushType(get2n().cmp(method, isCmpG));
  1652         return this;
  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();
  1666         joinTo(label);
  1667         method.visitJumpInsn(opcode, label.getLabel());
  1670     /**
  1671      * Generate an if_acmpeq
  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);
  1680     /**
  1681      * Generate an if_acmpne
  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);
  1690     /**
  1691      * Generate an ifnull
  1693      * @param label label to true case
  1694      */
  1695     void ifnull(final Label label) {
  1696         debug("ifnull", label);
  1697         jump(IFNULL, label, 1);
  1700     /**
  1701      * Generate an ifnonnull
  1703      * @param label label to true case
  1704      */
  1705     void ifnonnull(final Label label) {
  1706         debug("ifnonnull", label);
  1707         jump(IFNONNULL, label, 1);
  1710     /**
  1711      * Generate an ifeq
  1713      * @param label label to true case
  1714      */
  1715     void ifeq(final Label label) {
  1716         debug("ifeq ", label);
  1717         jump(IFEQ, label, 1);
  1720     /**
  1721      * Generate an if_icmpeq
  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);
  1730     /**
  1731      * Generate an if_ne
  1733      * @param label label to true case
  1734      */
  1735     void ifne(final Label label) {
  1736         debug("ifne", label);
  1737         jump(IFNE, label, 1);
  1740     /**
  1741      * Generate an if_icmpne
  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);
  1750     /**
  1751      * Generate an iflt
  1753      * @param label label to true case
  1754      */
  1755     void iflt(final Label label) {
  1756         debug("iflt", label);
  1757         jump(IFLT, label, 1);
  1760     /**
  1761      * Generate an if_icmplt
  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);
  1770     /**
  1771      * Generate an ifle
  1773      * @param label label to true case
  1774      */
  1775     void ifle(final Label label) {
  1776         debug("ifle", label);
  1777         jump(IFLE, label, 1);
  1780     /**
  1781      * Generate an if_icmple
  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);
  1790     /**
  1791      * Generate an ifgt
  1793      * @param label label to true case
  1794      */
  1795     void ifgt(final Label label) {
  1796         debug("ifgt", label);
  1797         jump(IFGT, label, 1);
  1800     /**
  1801      * Generate an if_icmpgt
  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);
  1810     /**
  1811      * Generate an ifge
  1813      * @param label label to true case
  1814      */
  1815     void ifge(final Label label) {
  1816         debug("ifge", label);
  1817         jump(IFGE, label, 1);
  1820     /**
  1821      * Generate an if_icmpge
  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);
  1830     /**
  1831      * Unconditional jump to a label
  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
  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.
  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);
  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());
  1864     /**
  1865      * Potential transfer of control to a catch block.
  1867      * @param catchLabel destination catch label
  1868      */
  1869     void canThrow(final Label catchLabel) {
  1870         catchLabel.joinFromTry(stack, false);
  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
  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.
  1880      * @param label label
  1881      */
  1882     private void joinTo(final Label label) {
  1883         assert isReachable();
  1884         label.joinFrom(stack);
  1887     /**
  1888      * Register a new label, enter it here.
  1889      * @param label
  1890      */
  1891     void label(final Label label) {
  1892         breakLabel(label, -1);
  1895     /**
  1896      * Register a new break target label, enter it here.
  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);
  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;
  1921         debug_label(label);
  1922         method.visitLabel(label.getLabel());
  1925     /**
  1926      * Pop element from stack, convert to given type
  1928      * @param to type to convert to
  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);
  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);
  1960         return this;
  1963     /**
  1964      * Helper function - expect two types that are equivalent
  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;
  1975     /**
  1976      * Helper function - expect two types that are integer types and equivalent
  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;
  1987     /**
  1988      * Helper function - expect two types that are numbers and equivalent
  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;
  1999     /**
  2000      * Pop two numbers, perform addition and push result
  2002      * @return the method emitter
  2003      */
  2004     MethodEmitter add(final int programPoint) {
  2005         debug("add");
  2006         pushType(get2().add(method, programPoint));
  2007         return this;
  2010     /**
  2011      * Pop two numbers, perform subtraction and push result
  2013      * @return the method emitter
  2014      */
  2015     MethodEmitter sub(final int programPoint) {
  2016         debug("sub");
  2017         pushType(get2n().sub(method, programPoint));
  2018         return this;
  2021     /**
  2022      * Pop two numbers, perform multiplication and push result
  2024      * @return the method emitter
  2025      */
  2026     MethodEmitter mul(final int programPoint) {
  2027         debug("mul ");
  2028         pushType(get2n().mul(method, programPoint));
  2029         return this;
  2032     /**
  2033      * Pop two numbers, perform division and push result
  2035      * @return the method emitter
  2036      */
  2037     MethodEmitter div(final int programPoint) {
  2038         debug("div");
  2039         pushType(get2n().div(method, programPoint));
  2040         return this;
  2043     /**
  2044      * Pop two numbers, calculate remainder and push result
  2046      * @return the method emitter
  2047      */
  2048     MethodEmitter rem(final int programPoint) {
  2049         debug("rem");
  2050         pushType(get2n().rem(method, programPoint));
  2051         return this;
  2054     /**
  2055      * Retrieve the top <tt>count</tt> types on the stack without modifying it.
  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);
  2064     int[] getLocalLoadsOnStack(final int from, final int to) {
  2065         return stack.getLocalLoads(from, to);
  2068     int getStackSize() {
  2069         return stack.size();
  2072     int getFirstTemp() {
  2073         return stack.firstTemp;
  2076     int getUsedSlotsWithLiveTemporaries() {
  2077         return stack.getUsedSlotsWithLiveTemporaries();
  2080     /**
  2081      * Helper function to generate a function signature based on stack contents
  2082      * and argument count and return type
  2084      * @param returnType return type
  2085      * @param argCount   argument count
  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;
  2101             paramTypes[i] = pt;
  2103         final String descriptor = Type.getMethodDescriptor(returnType, paramTypes);
  2104         for (int i = 0; i < argCount; i++) {
  2105             popType(paramTypes[argCount - i - 1]);
  2108         return descriptor;
  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;
  2122         return this;
  2125     /**
  2126      * Generate a dynamic new
  2128      * @param argCount  number of arguments
  2129      * @param flags     callsite flags
  2131      * @return the method emitter
  2132      */
  2133     MethodEmitter dynamicNew(final int argCount, final int flags) {
  2134         return dynamicNew(argCount, flags, null);
  2137     /**
  2138      * Generate a dynamic new
  2140      * @param argCount  number of arguments
  2141      * @param flags     callsite flags
  2142      * @param msg        additional message to be used when reporting error
  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;
  2157     /**
  2158      * Generate a dynamic call
  2160      * @param returnType return type
  2161      * @param argCount   number of arguments
  2162      * @param flags      callsite flags
  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);
  2170     /**
  2171      * Generate a dynamic call
  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
  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;
  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;
  2200     /**
  2201      * Generate dynamic getter. Pop scope from stack. Push result
  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);
  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
  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;
  2232     /**
  2233      * Generate dynamic setter. Pop receiver and property from stack.
  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;
  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,
  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);
  2260      /**
  2261      * Dynamic getter for indexed structures. Pop index and receiver from stack,
  2262      * generate appropriate signatures based on types
  2264      * @param result result type for getter
  2265      * @param flags call site flags for getter
  2266      * @param isMethod should it prefer retrieving methods
  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
  2279         Type index = peekType();
  2280         if (index.isObject() || index.isBoolean()) {
  2281             index = Type.OBJECT; //e.g. string->object
  2282             convert(Type.OBJECT);
  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);
  2297         return this;
  2300     private static String getProgramPoint(final int flags) {
  2301         if((flags & CALLSITE_OPTIMISTIC) == 0) {
  2302             return "";
  2304         return "pp=" + String.valueOf((flags & (-1 << CALLSITE_PROGRAM_POINT_SHIFT)) >> CALLSITE_PROGRAM_POINT_SHIFT);
  2307     /**
  2308      * Dynamic setter for indexed structures. Pop value, index and receiver from
  2309      * stack, generate appropriate signature based on types
  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);
  2322         popType();
  2324         Type index = peekType();
  2325         if (index.isObject() || index.isBoolean()) {
  2326             index = Type.OBJECT; //e.g. string->object
  2327             convert(Type.OBJECT);
  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);
  2337     /**
  2338      * Load a key value in the proper form.
  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));
  2351         pushType(Type.OBJECT); //STRING
  2352         return this;
  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;
  2380              return Type.OBJECT;
  2384      /**
  2385       * Generate get for a field access
  2387       * @param fa the field access
  2389       * @return the method emitter
  2390       */
  2391     MethodEmitter getField(final FieldAccess fa) {
  2392         return fa.get(this);
  2395      /**
  2396       * Generate set for a field access
  2398       * @param fa the field access
  2399       */
  2400     void putField(final FieldAccess fa) {
  2401         fa.put(this);
  2404     /**
  2405      * Get the value of a non-static field, pop the receiver from the stack,
  2406      * push value to the stack
  2408      * @param className        class
  2409      * @param fieldName        field name
  2410      * @param fieldDescriptor  field descriptor
  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;
  2423     /**
  2424      * Get the value of a static field, push it to the stack
  2426      * @param className        class
  2427      * @param fieldName        field name
  2428      * @param fieldDescriptor  field descriptor
  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;
  2439     /**
  2440      * Pop value and field from stack and write to a non-static field
  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);
  2453     /**
  2454      * Pop value from stack and write to a static field
  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);
  2466     /**
  2467      * Register line number at a label
  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);
  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);
  2489             next = next.getNext();
  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);
  2500             next = next.getNext();
  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";
  2512     private static String dynSetOperation(final boolean isIndex) {
  2513         return isIndex ? "dyn:setElem|setProp" : "dyn:setProp|setElem";
  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);
  2526         store(symbol, to, onlySymbolLiveValue);
  2527         return to;
  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);
  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);
  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);
  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);
  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);
  2589     private static int linePrefix = 0;
  2591     /**
  2592      * Debug function that outputs generated bytecode and stack contents
  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);
  2603     private void debug(final String arg) {
  2604         if (debug) {
  2605             debug(30, arg);
  2609     private void debug(final Object arg0, final Object arg1) {
  2610         if (debug) {
  2611             debug(30, new Object[] { arg0, arg1 });
  2615     private void debug(final Object arg0, final Object arg1, final Object arg2) {
  2616         if (debug) {
  2617             debug(30, new Object[] { arg0, arg1, arg2 });
  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 });
  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 });
  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 });
  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 });
  2645     /**
  2646      * Debug function that outputs generated bytecode and stack contents
  2647      * for a label - indentation is currently the only thing that differs
  2649      * @param args debug information to print
  2650      */
  2651     private void debug_label(final Object... args) {
  2652         if (debug) {
  2653             debug(22, args);
  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--;
  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('[');
  2688                         desc = desc.substring(i);
  2689                         final int slash = desc.lastIndexOf('/');
  2690                         if (slash != -1) {
  2691                             desc = desc.substring(slash + 1, desc.length() - 1);
  2693                         if ("Object".equals(desc)) {
  2694                             sb.append('O');
  2695                         } else {
  2696                             sb.append(desc);
  2698                     } else {
  2699                         sb.append(t.getDescriptor());
  2701                     final int loadIndex = stack.localLoads[stack.sp - 1 - pos];
  2702                     if(loadIndex != Label.Stack.NON_LOAD) {
  2703                         sb.append('(').append(loadIndex).append(')');
  2705                     if (pos + 1 < stack.size()) {
  2706                         sb.append(' ');
  2709                 sb.append('}');
  2710                 sb.append(' ');
  2713             pad = padConstant - sb.length();
  2714             while (pad > 0) {
  2715                 sb.append(' ');
  2716                 pad--;
  2719             for (final Object arg : args) {
  2720                 sb.append(arg);
  2721                 sb.append(' ');
  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());
  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;
  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;
  2751     private static boolean isOptimistic(final int flags) {
  2752         return (flags & CALLSITE_OPTIMISTIC) != 0;

mercurial