--- old/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/MethodEmitter.java 2020-04-15 18:47:12.000000000 +0530 +++ /dev/null 2020-04-15 18:47:12.000000000 +0530 @@ -1,2819 +0,0 @@ -/* - * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.nashorn.internal.codegen; - -import static jdk.internal.org.objectweb.asm.Opcodes.ATHROW; -import static jdk.internal.org.objectweb.asm.Opcodes.CHECKCAST; -import static jdk.internal.org.objectweb.asm.Opcodes.DUP2; -import static jdk.internal.org.objectweb.asm.Opcodes.GETFIELD; -import static jdk.internal.org.objectweb.asm.Opcodes.GETSTATIC; -import static jdk.internal.org.objectweb.asm.Opcodes.GOTO; -import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKESTATIC; -import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKEINTERFACE; -import static jdk.internal.org.objectweb.asm.Opcodes.IFEQ; -import static jdk.internal.org.objectweb.asm.Opcodes.IFGE; -import static jdk.internal.org.objectweb.asm.Opcodes.IFGT; -import static jdk.internal.org.objectweb.asm.Opcodes.IFLE; -import static jdk.internal.org.objectweb.asm.Opcodes.IFLT; -import static jdk.internal.org.objectweb.asm.Opcodes.IFNE; -import static jdk.internal.org.objectweb.asm.Opcodes.IFNONNULL; -import static jdk.internal.org.objectweb.asm.Opcodes.IFNULL; -import static jdk.internal.org.objectweb.asm.Opcodes.IF_ACMPEQ; -import static jdk.internal.org.objectweb.asm.Opcodes.IF_ACMPNE; -import static jdk.internal.org.objectweb.asm.Opcodes.IF_ICMPEQ; -import static jdk.internal.org.objectweb.asm.Opcodes.IF_ICMPGE; -import static jdk.internal.org.objectweb.asm.Opcodes.IF_ICMPGT; -import static jdk.internal.org.objectweb.asm.Opcodes.IF_ICMPLE; -import static jdk.internal.org.objectweb.asm.Opcodes.IF_ICMPLT; -import static jdk.internal.org.objectweb.asm.Opcodes.IF_ICMPNE; -import static jdk.internal.org.objectweb.asm.Opcodes.INSTANCEOF; -import static jdk.internal.org.objectweb.asm.Opcodes.INVOKEINTERFACE; -import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESPECIAL; -import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESTATIC; -import static jdk.internal.org.objectweb.asm.Opcodes.INVOKEVIRTUAL; -import static jdk.internal.org.objectweb.asm.Opcodes.NEW; -import static jdk.internal.org.objectweb.asm.Opcodes.PUTFIELD; -import static jdk.internal.org.objectweb.asm.Opcodes.PUTSTATIC; -import static jdk.internal.org.objectweb.asm.Opcodes.RETURN; -import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS; -import static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS; -import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE; -import static jdk.nashorn.internal.codegen.CompilerConstants.THIS; -import static jdk.nashorn.internal.codegen.CompilerConstants.THIS_DEBUGGER; -import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS; -import static jdk.nashorn.internal.codegen.CompilerConstants.className; -import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup; -import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor; -import static jdk.nashorn.internal.codegen.CompilerConstants.staticField; -import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup; -import static jdk.nashorn.internal.codegen.ObjectClassGenerator.PRIMITIVE_FIELD_TYPE; -import static jdk.nashorn.internal.runtime.linker.NameCodec.EMPTY_NAME; -import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_OPTIMISTIC; -import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_PROGRAM_POINT_SHIFT; - -import java.io.PrintStream; -import java.lang.reflect.Array; -import java.util.Collection; -import java.util.EnumSet; -import java.util.IdentityHashMap; -import java.util.List; -import java.util.Map; -import jdk.internal.org.objectweb.asm.Handle; -import jdk.internal.org.objectweb.asm.MethodVisitor; -import jdk.nashorn.internal.codegen.ClassEmitter.Flag; -import jdk.nashorn.internal.codegen.CompilerConstants.Call; -import jdk.nashorn.internal.codegen.CompilerConstants.FieldAccess; -import jdk.nashorn.internal.codegen.types.ArrayType; -import jdk.nashorn.internal.codegen.types.BitwiseType; -import jdk.nashorn.internal.codegen.types.NumericType; -import jdk.nashorn.internal.codegen.types.Type; -import jdk.nashorn.internal.ir.FunctionNode; -import jdk.nashorn.internal.ir.IdentNode; -import jdk.nashorn.internal.ir.JoinPredecessor; -import jdk.nashorn.internal.ir.LiteralNode; -import jdk.nashorn.internal.ir.LocalVariableConversion; -import jdk.nashorn.internal.ir.Symbol; -import jdk.nashorn.internal.ir.TryNode; -import jdk.nashorn.internal.objects.NativeArray; -import jdk.nashorn.internal.runtime.ArgumentSetter; -import jdk.nashorn.internal.runtime.Context; -import jdk.nashorn.internal.runtime.Debug; -import jdk.nashorn.internal.runtime.JSType; -import jdk.nashorn.internal.runtime.RewriteException; -import jdk.nashorn.internal.runtime.Scope; -import jdk.nashorn.internal.runtime.ScriptObject; -import jdk.nashorn.internal.runtime.ScriptRuntime; -import jdk.nashorn.internal.runtime.UnwarrantedOptimismException; -import jdk.nashorn.internal.runtime.linker.Bootstrap; -import jdk.nashorn.internal.runtime.linker.NameCodec; -import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor; -import jdk.nashorn.internal.runtime.logging.DebugLogger; -import jdk.nashorn.internal.runtime.options.Options; - -/** - * This is the main function responsible for emitting method code - * in a class. It maintains a type stack and keeps track of control - * flow to make sure that the registered instructions don't violate - * byte code verification. - * - * Running Nashorn with -ea will assert as soon as a type stack - * becomes corrupt, for easier debugging - * - * Running Nashorn with -Dnashorn.codegen.debug=true will print - * all generated bytecode and labels to stderr, for easier debugging, - * including bytecode stack contents - */ -public class MethodEmitter { - /** The ASM MethodVisitor we are plugged into */ - private final MethodVisitor method; - - /** Parent classEmitter representing the class of this method */ - private final ClassEmitter classEmitter; - - /** FunctionNode representing this method, or null if none exists */ - protected FunctionNode functionNode; - - /** Current type stack for current evaluation */ - private Label.Stack stack; - - private boolean preventUndefinedLoad; - - /** - * Map of live local variable definitions. - */ - private final Map localVariableDefs = new IdentityHashMap<>(); - - /** The context */ - private final Context context; - - /** Threshold in chars for when string constants should be split */ - static final int LARGE_STRING_THRESHOLD = 32 * 1024; - - /** Debug flag, should we dump all generated bytecode along with stacks? */ - private final DebugLogger log; - private final boolean debug; - - /** dump stack on a particular line, or -1 if disabled */ - private static final int DEBUG_TRACE_LINE; - - static { - final String tl = Options.getStringProperty("nashorn.codegen.debug.trace", "-1"); - int line = -1; - try { - line = Integer.parseInt(tl); - } catch (final NumberFormatException e) { - //fallthru - } - DEBUG_TRACE_LINE = line; - } - - /** Bootstrap for normal indy:s */ - private static final Handle LINKERBOOTSTRAP = new Handle(H_INVOKESTATIC, Bootstrap.BOOTSTRAP.className(), Bootstrap.BOOTSTRAP.name(), Bootstrap.BOOTSTRAP.descriptor(), false); - - /** Bootstrap for array populators */ - private static final Handle POPULATE_ARRAY_BOOTSTRAP = new Handle(H_INVOKESTATIC, RewriteException.BOOTSTRAP.className(), RewriteException.BOOTSTRAP.name(), RewriteException.BOOTSTRAP.descriptor(), false); - - /** - * Constructor - internal use from ClassEmitter only - * @see ClassEmitter#method - * - * @param classEmitter the class emitter weaving the class this method is in - * @param method a method visitor - */ - MethodEmitter(final ClassEmitter classEmitter, final MethodVisitor method) { - this(classEmitter, method, null); - } - - /** - * Constructor - internal use from ClassEmitter only - * @see ClassEmitter#method - * - * @param classEmitter the class emitter weaving the class this method is in - * @param method a method visitor - * @param functionNode a function node representing this method - */ - MethodEmitter(final ClassEmitter classEmitter, final MethodVisitor method, final FunctionNode functionNode) { - this.context = classEmitter.getContext(); - this.classEmitter = classEmitter; - this.method = method; - this.functionNode = functionNode; - this.stack = null; - this.log = context.getLogger(CodeGenerator.class); - this.debug = log.isEnabled(); - } - - /** - * Begin a method - */ - public void begin() { - classEmitter.beginMethod(this); - newStack(); - method.visitCode(); - } - - /** - * End a method - */ - public void end() { - method.visitMaxs(0, 0); - method.visitEnd(); - - classEmitter.endMethod(this); - } - - boolean isReachable() { - return stack != null; - } - - private void doesNotContinueSequentially() { - stack = null; - } - - private void newStack() { - stack = new Label.Stack(); - } - - @Override - public String toString() { - return "methodEmitter: " + (functionNode == null ? method : functionNode.getName()).toString() + ' ' + Debug.id(this); - } - - /** - * Push a type to the existing stack - * @param type the type - */ - void pushType(final Type type) { - if (type != null) { - stack.push(type); - } - } - - /** - * Pop a type from the existing stack - * - * @param expected expected type - will assert if wrong - * - * @return the type that was retrieved - */ - private Type popType(final Type expected) { - final Type type = popType(); - assert type.isEquivalentTo(expected) : type + " is not compatible with " + expected; - return type; - } - - /** - * Pop a type from the existing stack, no matter what it is. - * - * @return the type - */ - private Type popType() { - return stack.pop(); - } - - /** - * Pop a type from the existing stack, ensuring that it is numeric. Boolean type is popped as int type. - * - * @return the type - */ - private NumericType popNumeric() { - final Type type = popType(); - if(type.isBoolean()) { - // Booleans are treated as int for purposes of arithmetic operations - return Type.INT; - } - assert type.isNumeric(); - return (NumericType)type; - } - - /** - * Pop a type from the existing stack, ensuring that it is an integer type - * (integer or long). Boolean type is popped as int type. - * - * @return the type - */ - private BitwiseType popBitwise() { - final Type type = popType(); - if(type == Type.BOOLEAN) { - return Type.INT; - } - return (BitwiseType)type; - } - - private BitwiseType popInteger() { - final Type type = popType(); - if(type == Type.BOOLEAN) { - return Type.INT; - } - assert type == Type.INT; - return (BitwiseType)type; - } - - /** - * Pop a type from the existing stack, ensuring that it is an array type, - * assert if not - * - * @return the type - */ - private ArrayType popArray() { - final Type type = popType(); - assert type.isArray() : type; - return (ArrayType)type; - } - - /** - * Peek a given number of slots from the top of the stack and return the - * type in that slot - * - * @param pos the number of positions from the top, 0 is the top element - * - * @return the type at position "pos" on the stack - */ - final Type peekType(final int pos) { - return stack.peek(pos); - } - - /** - * Peek at the type at the top of the stack - * - * @return the type at the top of the stack - */ - final Type peekType() { - return stack.peek(); - } - - /** - * Generate code a for instantiating a new object and push the - * object type on the stack - * - * @param classDescriptor class descriptor for the object type - * @param type the type of the new object - * - * @return the method emitter - */ - MethodEmitter _new(final String classDescriptor, final Type type) { - debug("new", classDescriptor); - method.visitTypeInsn(NEW, classDescriptor); - pushType(type); - return this; - } - - /** - * Generate code a for instantiating a new object and push the - * object type on the stack - * - * @param clazz class type to instatiate - * - * @return the method emitter - */ - MethodEmitter _new(final Class clazz) { - return _new(className(clazz), Type.typeFor(clazz)); - } - - /** - * Generate code to call the empty constructor for a class - * - * @param clazz class type to instatiate - * - * @return the method emitter - */ - MethodEmitter newInstance(final Class clazz) { - return invoke(constructorNoLookup(clazz)); - } - - /** - * Perform a dup, that is, duplicate the top element and - * push the duplicate down a given number of positions - * on the stack. This is totally type agnostic. - * - * @param depth the depth on which to put the copy - * - * @return the method emitter, or null if depth is illegal and - * has no instruction equivalent. - */ - MethodEmitter dup(final int depth) { - if (peekType().dup(method, depth) == null) { - return null; - } - - debug("dup", depth); - - switch (depth) { - case 0: { - final int l0 = stack.getTopLocalLoad(); - pushType(peekType()); - stack.markLocalLoad(l0); - break; - } - case 1: { - final int l0 = stack.getTopLocalLoad(); - final Type p0 = popType(); - final int l1 = stack.getTopLocalLoad(); - final Type p1 = popType(); - pushType(p0); - stack.markLocalLoad(l0); - pushType(p1); - stack.markLocalLoad(l1); - pushType(p0); - stack.markLocalLoad(l0); - break; - } - case 2: { - final int l0 = stack.getTopLocalLoad(); - final Type p0 = popType(); - final int l1 = stack.getTopLocalLoad(); - final Type p1 = popType(); - final int l2 = stack.getTopLocalLoad(); - final Type p2 = popType(); - pushType(p0); - stack.markLocalLoad(l0); - pushType(p2); - stack.markLocalLoad(l2); - pushType(p1); - stack.markLocalLoad(l1); - pushType(p0); - stack.markLocalLoad(l0); - break; - } - default: - assert false : "illegal dup depth = " + depth; - return null; - } - - return this; - } - - /** - * Perform a dup2, that is, duplicate the top element if it - * is a category 2 type, or two top elements if they are category - * 1 types, and push them on top of the stack - * - * @return the method emitter - */ - MethodEmitter dup2() { - debug("dup2"); - - if (peekType().isCategory2()) { - final int l0 = stack.getTopLocalLoad(); - pushType(peekType()); - stack.markLocalLoad(l0); - } else { - final int l0 = stack.getTopLocalLoad(); - final Type p0 = popType(); - final int l1 = stack.getTopLocalLoad(); - final Type p1 = popType(); - pushType(p0); - stack.markLocalLoad(l0); - pushType(p1); - stack.markLocalLoad(l1); - pushType(p0); - stack.markLocalLoad(l0); - pushType(p1); - stack.markLocalLoad(l1); - } - method.visitInsn(DUP2); - return this; - } - - /** - * Duplicate the top element on the stack and push it - * - * @return the method emitter - */ - MethodEmitter dup() { - return dup(0); - } - - /** - * Pop the top element of the stack and throw it away - * - * @return the method emitter - */ - MethodEmitter pop() { - debug("pop", peekType()); - popType().pop(method); - return this; - } - - /** - * Pop the top element of the stack if category 2 type, or the two - * top elements of the stack if category 1 types - * - * @return the method emitter - */ - MethodEmitter pop2() { - if (peekType().isCategory2()) { - popType(); - } else { - get2n(); - } - return this; - } - - /** - * Swap the top two elements of the stack. This is totally - * type agnostic and works for all types - * - * @return the method emitter - */ - MethodEmitter swap() { - debug("swap"); - - final int l0 = stack.getTopLocalLoad(); - final Type p0 = popType(); - final int l1 = stack.getTopLocalLoad(); - final Type p1 = popType(); - p0.swap(method, p1); - - pushType(p0); - stack.markLocalLoad(l0); - pushType(p1); - stack.markLocalLoad(l1); - return this; - } - - void pack() { - final Type type = peekType(); - if (type.isInteger()) { - convert(PRIMITIVE_FIELD_TYPE); - } else if (type.isLong()) { - //nop - } else if (type.isNumber()) { - invokestatic("java/lang/Double", "doubleToRawLongBits", "(D)J"); - } else { - assert false : type + " cannot be packed!"; - } - } - - /** - * Initializes a bytecode method parameter - * @param symbol the symbol for the parameter - * @param type the type of the parameter - * @param start the label for the start of the method - */ - void initializeMethodParameter(final Symbol symbol, final Type type, final Label start) { - assert symbol.isBytecodeLocal(); - localVariableDefs.put(symbol, new LocalVariableDef(start.getLabel(), type)); - } - - /** - * Create a new string builder, call the constructor and push the instance to the stack. - * - * @return the method emitter - */ - MethodEmitter newStringBuilder() { - return invoke(constructorNoLookup(StringBuilder.class)).dup(); - } - - /** - * Pop a string and a StringBuilder from the top of the stack and call the append - * function of the StringBuilder, appending the string. Pushes the StringBuilder to - * the stack when finished. - * - * @return the method emitter - */ - MethodEmitter stringBuilderAppend() { - convert(Type.STRING); - return invoke(virtualCallNoLookup(StringBuilder.class, "append", StringBuilder.class, String.class)); - } - - /** - * Pops two integer types from the stack, performs a bitwise and and pushes - * the result - * - * @return the method emitter - */ - MethodEmitter and() { - debug("and"); - pushType(get2i().and(method)); - return this; - } - - /** - * Pops two integer types from the stack, performs a bitwise or and pushes - * the result - * - * @return the method emitter - */ - MethodEmitter or() { - debug("or"); - pushType(get2i().or(method)); - return this; - } - - /** - * Pops two integer types from the stack, performs a bitwise xor and pushes - * the result - * - * @return the method emitter - */ - MethodEmitter xor() { - debug("xor"); - pushType(get2i().xor(method)); - return this; - } - - /** - * Pops two integer types from the stack, performs a bitwise logic shift right and pushes - * the result. The shift count, the first element, must be INT. - * - * @return the method emitter - */ - MethodEmitter shr() { - debug("shr"); - popInteger(); - pushType(popBitwise().shr(method)); - return this; - } - - /** - * Pops two integer types from the stack, performs a bitwise shift left and and pushes - * the result. The shift count, the first element, must be INT. - * - * @return the method emitter - */ - MethodEmitter shl() { - debug("shl"); - popInteger(); - pushType(popBitwise().shl(method)); - return this; - } - - /** - * Pops two integer types from the stack, performs a bitwise arithmetic shift right and pushes - * the result. The shift count, the first element, must be INT. - * - * @return the method emitter - */ - MethodEmitter sar() { - debug("sar"); - popInteger(); - pushType(popBitwise().sar(method)); - return this; - } - - /** - * Pops a numeric type from the stack, negates it and pushes the result - * - * @return the method emitter - */ - MethodEmitter neg(final int programPoint) { - debug("neg"); - pushType(popNumeric().neg(method, programPoint)); - return this; - } - - /** - * Add label for the start of a catch block and push the exception to the - * stack - * - * @param recovery label pointing to start of catch block - */ - void _catch(final Label recovery) { - // While in JVM a catch block can be reached through normal control flow, our code generator never does this, - // so we might as well presume there's no stack on entry. - assert stack == null; - recovery.onCatch(); - label(recovery); - beginCatchBlock(); - } - - /** - * Add any number of labels for the start of a catch block and push the exception to the - * stack - * - * @param recoveries labels pointing to start of catch block - */ - void _catch(final Collection