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

Wed, 27 Apr 2016 01:36:41 +0800

author
aoqi
date
Wed, 27 Apr 2016 01:36:41 +0800
changeset 0
b1a7da25b547
child 952
6d5471a497fb
permissions
-rw-r--r--

Initial load
http://hg.openjdk.java.net/jdk8u/jdk8u/nashorn/
changeset: 1034:4b9cc65dd24d
tag: jdk8u25-b17

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

mercurial