# HG changeset patch # User lana # Date 1414006197 25200 # Node ID 81483abb4ff9ba2658f1ce0f897ebabe6ef439e8 # Parent f01c0455ff0353bec7577a07efdca480d57ff79c# Parent 7fad0ce8134431c0dfc3dc7fbebc4e76a82790ab Merge diff -r f01c0455ff03 -r 81483abb4ff9 make/build-nasgen.xml --- a/make/build-nasgen.xml Wed Oct 22 11:18:29 2014 -0700 +++ b/make/build-nasgen.xml Wed Oct 22 12:29:57 2014 -0700 @@ -25,7 +25,7 @@ Builds and runs nasgen. - + diff -r f01c0455ff03 -r 81483abb4ff9 make/build.xml --- a/make/build.xml Wed Oct 22 11:18:29 2014 -0700 +++ b/make/build.xml Wed Oct 22 12:29:57 2014 -0700 @@ -49,8 +49,6 @@ - - @@ -78,8 +76,31 @@ + + + + + + + + + + + + + + + + + + + + + + + - + @@ -107,19 +128,7 @@ - - - - - - + - @@ -430,7 +439,7 @@ - @@ -456,9 +465,11 @@ + + @@ -466,9 +477,11 @@ + + diff -r f01c0455ff03 -r 81483abb4ff9 src/jdk/nashorn/api/scripting/NashornScriptEngine.java --- a/src/jdk/nashorn/api/scripting/NashornScriptEngine.java Wed Oct 22 11:18:29 2014 -0700 +++ b/src/jdk/nashorn/api/scripting/NashornScriptEngine.java Wed Oct 22 12:29:57 2014 -0700 @@ -229,6 +229,8 @@ } private T getInterfaceInner(final Object thiz, final Class clazz) { + assert !(thiz instanceof ScriptObject) : "raw ScriptObject not expected here"; + if (clazz == null || !clazz.isInterface()) { throw new IllegalArgumentException(getMessage("interface.class.expected")); } @@ -254,17 +256,6 @@ if (! isOfContext(realGlobal, nashornContext)) { throw new IllegalArgumentException(getMessage("script.object.from.another.engine")); } - } else if (thiz instanceof ScriptObject) { - // called from script code. - realSelf = (ScriptObject)thiz; - realGlobal = Context.getGlobal(); - if (realGlobal == null) { - throw new IllegalArgumentException(getMessage("no.current.nashorn.global")); - } - - if (! isOfContext(realGlobal, nashornContext)) { - throw new IllegalArgumentException(getMessage("script.object.from.another.engine")); - } } if (realSelf == null) { @@ -368,6 +359,7 @@ private Object invokeImpl(final Object selfObject, final String name, final Object... args) throws ScriptException, NoSuchMethodException { name.getClass(); // null check + assert !(selfObject instanceof ScriptObject) : "raw ScriptObject not expected here"; Global invokeGlobal = null; ScriptObjectMirror selfMirror = null; @@ -377,20 +369,6 @@ throw new IllegalArgumentException(getMessage("script.object.from.another.engine")); } invokeGlobal = selfMirror.getHomeGlobal(); - } else if (selfObject instanceof ScriptObject) { - // invokeMethod called from script code - in which case we may get 'naked' ScriptObject - // Wrap it with oldGlobal to make a ScriptObjectMirror for the same. - final Global oldGlobal = Context.getGlobal(); - invokeGlobal = oldGlobal; - if (oldGlobal == null) { - throw new IllegalArgumentException(getMessage("no.current.nashorn.global")); - } - - if (! isOfContext(oldGlobal, nashornContext)) { - throw new IllegalArgumentException(getMessage("script.object.from.another.engine")); - } - - selfMirror = (ScriptObjectMirror)ScriptObjectMirror.wrap(selfObject, oldGlobal); } else if (selfObject == null) { // selfObject is null => global function call final Global ctxtGlobal = getNashornGlobalFrom(context); diff -r f01c0455ff03 -r 81483abb4ff9 src/jdk/nashorn/api/scripting/ScriptUtils.java --- a/src/jdk/nashorn/api/scripting/ScriptUtils.java Wed Oct 22 11:18:29 2014 -0700 +++ b/src/jdk/nashorn/api/scripting/ScriptUtils.java Wed Oct 22 12:29:57 2014 -0700 @@ -75,11 +75,8 @@ * @param sync the object to synchronize on * @return a synchronizing wrapper function */ - public static Object makeSynchronizedFunction(final Object func, final Object sync) { - if (func instanceof ScriptFunction) { - return ((ScriptFunction)func).makeSynchronizedFunction(sync); - } - throw typeError("not.a.function", ScriptRuntime.safeToString(func)); + public static Object makeSynchronizedFunction(final ScriptFunction func, final Object sync) { + return func.makeSynchronizedFunction(unwrap(sync)); } /** @@ -88,12 +85,8 @@ * @param obj object to be wrapped * @return wrapped object */ - public static Object wrap(final Object obj) { - if (obj instanceof ScriptObject) { - return ScriptObjectMirror.wrap(obj, Context.getGlobal()); - } - - return obj; + public static ScriptObjectMirror wrap(final ScriptObject obj) { + return (ScriptObjectMirror) ScriptObjectMirror.wrap(obj, Context.getGlobal()); } /** @@ -160,14 +153,15 @@ } final LinkerServices linker = Bootstrap.getLinkerServices(); - final MethodHandle converter = linker.getTypeConverter(obj.getClass(), clazz); + final Object objToConvert = unwrap(obj); + final MethodHandle converter = linker.getTypeConverter(objToConvert.getClass(), clazz); if (converter == null) { // no supported conversion! throw new UnsupportedOperationException("conversion not supported"); } try { - return converter.invoke(obj); + return converter.invoke(objToConvert); } catch (final RuntimeException | Error e) { throw e; } catch (final Throwable t) { diff -r f01c0455ff03 -r 81483abb4ff9 src/jdk/nashorn/internal/codegen/ApplySpecialization.java --- a/src/jdk/nashorn/internal/codegen/ApplySpecialization.java Wed Oct 22 11:18:29 2014 -0700 +++ b/src/jdk/nashorn/internal/codegen/ApplySpecialization.java Wed Oct 22 12:29:57 2014 -0700 @@ -27,6 +27,7 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS_VAR; import static jdk.nashorn.internal.codegen.CompilerConstants.EXPLODED_ARGUMENT_PREFIX; + import java.lang.invoke.MethodType; import java.util.ArrayDeque; import java.util.ArrayList; @@ -38,6 +39,7 @@ import jdk.nashorn.internal.ir.CallNode; import jdk.nashorn.internal.ir.Expression; import jdk.nashorn.internal.ir.FunctionNode; +import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.IdentNode; import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.Node; @@ -321,7 +323,7 @@ explodedArguments.pop(); - return newFunctionNode; + return newFunctionNode.setState(lc, CompilationState.BUILTINS_TRANSFORMED); } private static boolean isApply(final CallNode callNode) { diff -r f01c0455ff03 -r 81483abb4ff9 src/jdk/nashorn/internal/codegen/AssignSymbols.java --- a/src/jdk/nashorn/internal/codegen/AssignSymbols.java Wed Oct 22 11:18:29 2014 -0700 +++ b/src/jdk/nashorn/internal/codegen/AssignSymbols.java Wed Oct 22 12:29:57 2014 -0700 @@ -76,7 +76,6 @@ import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.RuntimeNode; import jdk.nashorn.internal.ir.RuntimeNode.Request; -import jdk.nashorn.internal.ir.SplitNode; import jdk.nashorn.internal.ir.Statement; import jdk.nashorn.internal.ir.SwitchNode; import jdk.nashorn.internal.ir.Symbol; @@ -135,9 +134,6 @@ if (!(functionNode.hasScopeBlock() || functionNode.needsParentScope())) { functionNode.compilerConstant(SCOPE).setNeedsSlot(false); } - if (!functionNode.usesReturnSymbol()) { - functionNode.compilerConstant(RETURN).setNeedsSlot(false); - } // Named function expressions that end up not referencing themselves won't need a local slot for the self symbol. if(!functionNode.isDeclared() && !functionNode.usesSelfSymbol() && !functionNode.isAnonymous()) { final Symbol selfSymbol = functionNode.getBody().getExistingSymbol(functionNode.getIdent().getName()); @@ -511,16 +507,6 @@ thisProperties.push(new HashSet()); - if (functionNode.isDeclared()) { - // Can't use lc.getCurrentBlock() as we can have an outermost function in our lexical context that - // is not a program - it is a function being compiled on-demand. - final Iterator blocks = lc.getBlocks(); - if (blocks.hasNext()) { - final IdentNode ident = functionNode.getIdent(); - defineSymbol(blocks.next(), ident.getName(), ident, IS_VAR | (functionNode.isAnonymous()? IS_INTERNAL : 0)); - } - } - // Every function has a body, even the ones skipped on reparse (they have an empty one). We're // asserting this as even for those, enterBlock() must be invoked to correctly process symbols that // are used in them. @@ -532,14 +518,34 @@ @Override public boolean enterVarNode(final VarNode varNode) { start(varNode); + // Normally, a symbol assigned in a var statement is not live for its RHS. Since we also represent function + // declarations as VarNodes, they are exception to the rule, as they need to have the symbol visible to the + // body of the declared function for self-reference. + if (varNode.isFunctionDeclaration()) { + defineVarIdent(varNode); + } return true; } @Override public Node leaveVarNode(final VarNode varNode) { + if (!varNode.isFunctionDeclaration()) { + defineVarIdent(varNode); + } + return super.leaveVarNode(varNode); + } + + private void defineVarIdent(final VarNode varNode) { final IdentNode ident = varNode.getName(); - defineSymbol(lc.getCurrentBlock(), ident.getName(), ident, varNode.getSymbolFlags() | (lc.getCurrentFunction().isProgram() ? IS_SCOPE : 0)); - return super.leaveVarNode(varNode); + final int flags; + if (varNode.isAnonymousFunctionDeclaration()) { + flags = IS_INTERNAL; + } else if (lc.getCurrentFunction().isProgram()) { + flags = IS_SCOPE; + } else { + flags = 0; + } + defineSymbol(lc.getCurrentBlock(), ident.getName(), ident, varNode.getSymbolFlags() | flags); } private Symbol exceptionSymbol() { @@ -1004,7 +1010,7 @@ boolean previousWasBlock = false; for (final Iterator it = lc.getAllNodes(); it.hasNext();) { final LexicalContextNode node = it.next(); - if (node instanceof FunctionNode || node instanceof SplitNode || isSplitArray(node)) { + if (node instanceof FunctionNode || isSplitArray(node)) { // We reached the function boundary or a splitting boundary without seeing a definition for the symbol. // It needs to be in scope. return true; diff -r f01c0455ff03 -r 81483abb4ff9 src/jdk/nashorn/internal/codegen/AstSerializer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk/nashorn/internal/codegen/AstSerializer.java Wed Oct 22 12:29:57 2014 -0700 @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2010, 2014, 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 java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.util.Collections; +import java.util.zip.Deflater; +import java.util.zip.DeflaterOutputStream; +import jdk.nashorn.internal.ir.Block; +import jdk.nashorn.internal.ir.FunctionNode; +import jdk.nashorn.internal.ir.LexicalContext; +import jdk.nashorn.internal.ir.Node; +import jdk.nashorn.internal.ir.Statement; +import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.runtime.options.Options; + +/** + * This static utility class performs serialization of FunctionNode ASTs to a byte array. + * The format is a standard Java serialization stream, deflated. + */ +final class AstSerializer { + // Experimentally, we concluded that compression level 4 gives a good tradeoff between serialization speed + // and size. + private static final int COMPRESSION_LEVEL = Options.getIntProperty("nashorn.serialize.compression", 4); + static byte[] serialize(final FunctionNode fn) { + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + try (final ObjectOutputStream oout = new ObjectOutputStream(new DeflaterOutputStream(out, + new Deflater(COMPRESSION_LEVEL)))) { + oout.writeObject(removeInnerFunctionBodies(fn)); + } catch (final IOException e) { + throw new AssertionError("Unexpected exception serializing function", e); + } + return out.toByteArray(); + } + + private static FunctionNode removeInnerFunctionBodies(final FunctionNode fn) { + return (FunctionNode)fn.accept(new NodeVisitor(new LexicalContext()) { + @Override + public Node leaveBlock(final Block block) { + if (lc.isFunctionBody() && lc.getFunction(block) != lc.getOutermostFunction()) { + return block.setStatements(lc, Collections.emptyList()); + } + return super.leaveBlock(block); + } + }); + } +} diff -r f01c0455ff03 -r 81483abb4ff9 src/jdk/nashorn/internal/codegen/ClassEmitter.java --- a/src/jdk/nashorn/internal/codegen/ClassEmitter.java Wed Oct 22 11:18:29 2014 -0700 +++ b/src/jdk/nashorn/internal/codegen/ClassEmitter.java Wed Oct 22 12:29:57 2014 -0700 @@ -51,6 +51,7 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor; import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor; import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup; + import java.io.ByteArrayOutputStream; import java.io.PrintWriter; import java.security.AccessController; @@ -64,7 +65,6 @@ import jdk.internal.org.objectweb.asm.util.TraceClassVisitor; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.FunctionNode; -import jdk.nashorn.internal.ir.SplitNode; import jdk.nashorn.internal.ir.debug.NashornClassReader; import jdk.nashorn.internal.ir.debug.NashornTextifier; import jdk.nashorn.internal.runtime.Context; @@ -476,12 +476,6 @@ methodsStarted.remove(method); } - SplitMethodEmitter method(final SplitNode splitNode, final String methodName, final Class rtype, final Class... ptypes) { - methodCount++; - methodNames.add(methodName); - return new SplitMethodEmitter(this, methodVisitor(EnumSet.of(Flag.PUBLIC, Flag.STATIC), methodName, rtype, ptypes), splitNode); - } - /** * Add a new method to the class - defaults to public method * diff -r f01c0455ff03 -r 81483abb4ff9 src/jdk/nashorn/internal/codegen/CodeGenerator.java --- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java Wed Oct 22 11:18:29 2014 -0700 +++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java Wed Oct 22 12:29:57 2014 -0700 @@ -34,9 +34,7 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.GET_STRING; import static jdk.nashorn.internal.codegen.CompilerConstants.QUICK_PREFIX; import static jdk.nashorn.internal.codegen.CompilerConstants.REGEX_PREFIX; -import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN; import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE; -import static jdk.nashorn.internal.codegen.CompilerConstants.SPLIT_ARRAY_ARG; import static jdk.nashorn.internal.codegen.CompilerConstants.SPLIT_PREFIX; import static jdk.nashorn.internal.codegen.CompilerConstants.THIS; import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS; @@ -99,10 +97,10 @@ import jdk.nashorn.internal.ir.ForNode; import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.FunctionNode.CompilationState; +import jdk.nashorn.internal.ir.GetSplitState; import jdk.nashorn.internal.ir.IdentNode; import jdk.nashorn.internal.ir.IfNode; import jdk.nashorn.internal.ir.IndexNode; -import jdk.nashorn.internal.ir.JoinPredecessor; import jdk.nashorn.internal.ir.JoinPredecessorExpression; import jdk.nashorn.internal.ir.JumpStatement; import jdk.nashorn.internal.ir.LabelNode; @@ -121,7 +119,8 @@ import jdk.nashorn.internal.ir.ReturnNode; import jdk.nashorn.internal.ir.RuntimeNode; import jdk.nashorn.internal.ir.RuntimeNode.Request; -import jdk.nashorn.internal.ir.SplitNode; +import jdk.nashorn.internal.ir.SetSplitState; +import jdk.nashorn.internal.ir.SplitReturn; import jdk.nashorn.internal.ir.Statement; import jdk.nashorn.internal.ir.SwitchNode; import jdk.nashorn.internal.ir.Symbol; @@ -493,8 +492,7 @@ //walk up the chain from starting block and when we bump into the current function boundary, add the external //information. final FunctionNode fn = lc.getCurrentFunction(); - final int fnId = fn.getId(); - final int externalDepth = compiler.getScriptFunctionData(fnId).getExternalSymbolDepth(symbol.getName()); + final int externalDepth = compiler.getScriptFunctionData(fn.getId()).getExternalSymbolDepth(symbol.getName()); //count the number of scopes from this place to the start of the function @@ -554,10 +552,10 @@ } MethodEmitter loadBinaryOperands(final BinaryNode binaryNode) { - return loadBinaryOperands(binaryNode.lhs(), binaryNode.rhs(), TypeBounds.UNBOUNDED.notWiderThan(binaryNode.getWidestOperandType()), false); + return loadBinaryOperands(binaryNode.lhs(), binaryNode.rhs(), TypeBounds.UNBOUNDED.notWiderThan(binaryNode.getWidestOperandType()), false, false); } - private MethodEmitter loadBinaryOperands(final Expression lhs, final Expression rhs, final TypeBounds explicitOperandBounds, final boolean baseAlreadyOnStack) { + private MethodEmitter loadBinaryOperands(final Expression lhs, final Expression rhs, final TypeBounds explicitOperandBounds, final boolean baseAlreadyOnStack, final boolean forceConversionSeparation) { // ECMAScript 5.1 specification (sections 11.5-11.11 and 11.13) prescribes that when evaluating a binary // expression "LEFT op RIGHT", the order of operations must be: LOAD LEFT, LOAD RIGHT, CONVERT LEFT, CONVERT // RIGHT, EXECUTE OP. Unfortunately, doing it in this order defeats potential optimizations that arise when we @@ -574,15 +572,34 @@ final Type narrowestOperandType = Type.narrowest(Type.widest(lhs.getType(), rhs.getType()), explicitOperandBounds.widest); final TypeBounds operandBounds = explicitOperandBounds.notNarrowerThan(narrowestOperandType); if (noToPrimitiveConversion(lhs.getType(), explicitOperandBounds.widest) || rhs.isLocal()) { - // Can reorder. Combine load and convert into single operations. - loadExpression(lhs, operandBounds, baseAlreadyOnStack); - loadExpression(rhs, operandBounds, false); + // Can reorder. We might still need to separate conversion, but at least we can do it with reordering + if (forceConversionSeparation) { + // Can reorder, but can't move conversion into the operand as the operation depends on operands + // exact types for its overflow guarantees. E.g. with {L}{%I}expr1 {L}* {L}{%I}expr2 we are not allowed + // to merge {L}{%I} into {%L}, as that can cause subsequent overflows; test for JDK-8058610 contains + // concrete cases where this could happen. + final TypeBounds safeConvertBounds = TypeBounds.UNBOUNDED.notNarrowerThan(narrowestOperandType); + loadExpression(lhs, safeConvertBounds, baseAlreadyOnStack); + method.convert(operandBounds.within(method.peekType())); + loadExpression(rhs, safeConvertBounds, false); + method.convert(operandBounds.within(method.peekType())); + } else { + // Can reorder and move conversion into the operand. Combine load and convert into single operations. + loadExpression(lhs, operandBounds, baseAlreadyOnStack); + loadExpression(rhs, operandBounds, false); + } } else { // Can't reorder. Load and convert separately. final TypeBounds safeConvertBounds = TypeBounds.UNBOUNDED.notNarrowerThan(narrowestOperandType); loadExpression(lhs, safeConvertBounds, baseAlreadyOnStack); + final Type lhsType = method.peekType(); loadExpression(rhs, safeConvertBounds, false); - method.swap().convert(operandBounds.within(method.peekType())).swap().convert(operandBounds.within(method.peekType())); + final Type convertedLhsType = operandBounds.within(method.peekType()); + if (convertedLhsType != lhsType) { + // Do it conditionally, so that if conversion is a no-op we don't introduce a SWAP, SWAP. + method.swap().convert(convertedLhsType).swap(); + } + method.convert(operandBounds.within(method.peekType())); } assert Type.generic(method.peekType()) == operandBounds.narrowest; assert Type.generic(method.peekType(1)) == operandBounds.narrowest; @@ -633,19 +650,11 @@ } TypeBounds booleanToInt() { - return maybeNew(booleanToInt(narrowest), booleanToInt(widest)); + return maybeNew(CodeGenerator.booleanToInt(narrowest), CodeGenerator.booleanToInt(widest)); } TypeBounds objectToNumber() { - return maybeNew(objectToNumber(narrowest), objectToNumber(widest)); - } - - private static Type booleanToInt(final Type t) { - return t == Type.BOOLEAN ? Type.INT : t; - } - - private static Type objectToNumber(final Type t) { - return t.isObject() ? Type.NUMBER : t; + return maybeNew(CodeGenerator.objectToNumber(narrowest), CodeGenerator.objectToNumber(widest)); } Type within(final Type type) { @@ -664,6 +673,14 @@ } } + private static Type booleanToInt(final Type t) { + return t == Type.BOOLEAN ? Type.INT : t; + } + + private static Type objectToNumber(final Type t) { + return t.isObject() ? Type.NUMBER : t; + } + MethodEmitter loadExpressionAsType(final Expression expr, final Type type) { if(type == Type.BOOLEAN) { return loadExpressionAsBoolean(expr); @@ -1048,6 +1065,13 @@ } @Override + public boolean enterGetSplitState(final GetSplitState getSplitState) { + method.loadScope(); + method.invoke(Scope.GET_SPLIT_STATE); + return false; + } + + @Override public boolean enterDefault(final Node otherNode) { // Must have handled all expressions that can legally be encountered. throw new AssertionError(otherNode.getClass().getName()); @@ -1219,7 +1243,7 @@ popScopesUntil(target); final Label targetLabel = jump.getTargetLabel(target); targetLabel.markAsBreakTarget(); - method.splitAwareGoto(lc, targetLabel, target); + method._goto(targetLabel); return false; } @@ -2029,10 +2053,10 @@ } private void lineNumber(final int lineNumber) { - if (lineNumber != lastLineNumber) { + if (lineNumber != lastLineNumber && lineNumber != Node.NO_LINE_NUMBER) { method.lineNumber(lineNumber); - } - lastLineNumber = lineNumber; + lastLineNumber = lineNumber; + } } int getLastLineNumber() { @@ -2079,13 +2103,14 @@ method.begin(); defineCommonSplitMethodParameters(); - defineSplitMethodParameter(3, arrayType); - - fixScopeSlot(currentFunction); + defineSplitMethodParameter(CompilerConstants.SPLIT_ARRAY_ARG.slot(), arrayType); + + // NOTE: when this is no longer needed, SplitIntoFunctions will no longer have to add IS_SPLIT + // to synthetic functions, and FunctionNode.needsCallee() will no longer need to test for isSplit(). + final int arraySlot = fixScopeSlot(currentFunction, 3); lc.enterSplitNode(); - final int arraySlot = SPLIT_ARRAY_ARG.slot(); for (int i = arrayUnit.getLo(); i < arrayUnit.getHi(); i++) { method.load(arrayType, arraySlot); storeElement(nodes, elementType, postsets[i]); @@ -2700,73 +2725,6 @@ method.convert(newRuntimeNode.getType()); } - @Override - public boolean enterSplitNode(final SplitNode splitNode) { - if(!method.isReachable()) { - return false; - } - - final CompileUnit splitCompileUnit = splitNode.getCompileUnit(); - - final FunctionNode fn = lc.getCurrentFunction(); - final String className = splitCompileUnit.getUnitClassName(); - final String name = splitNode.getName(); - - final Type returnType = fn.getReturnType(); - - final Class rtype = fn.getReturnType().getTypeClass(); - final boolean needsArguments = fn.needsArguments(); - final Class[] ptypes = needsArguments ? - new Class[] {ScriptFunction.class, Object.class, ScriptObject.class, ScriptObject.class} : - new Class[] {ScriptFunction.class, Object.class, ScriptObject.class}; - - final MethodEmitter caller = method; - unit = lc.pushCompileUnit(splitCompileUnit); - - final Call splitCall = staticCallNoLookup( - className, - name, - methodDescriptor(rtype, ptypes)); - - final MethodEmitter splitEmitter = - splitCompileUnit.getClassEmitter().method( - splitNode, - name, - rtype, - ptypes); - - pushMethodEmitter(splitEmitter); - method.setFunctionNode(fn); - - assert fn.needsCallee() : "split function should require callee"; - caller.loadCompilerConstant(CALLEE); - caller.loadCompilerConstant(THIS); - caller.loadCompilerConstant(SCOPE); - if (needsArguments) { - caller.loadCompilerConstant(ARGUMENTS); - } - caller.invoke(splitCall); - caller.storeCompilerConstant(RETURN, returnType); - - method.begin(); - - defineCommonSplitMethodParameters(); - if(needsArguments) { - defineSplitMethodParameter(3, ARGUMENTS); - } - - // Copy scope to its target slot as first thing because the original slot could be used by return symbol. - fixScopeSlot(fn); - - final int returnSlot = fn.compilerConstant(RETURN).getSlot(returnType); - method.defineBlockLocalVariable(returnSlot, returnSlot + returnType.getSlots()); - method.loadUndefined(returnType); - method.storeCompilerConstant(RETURN, returnType); - - lc.enterSplitNode(); - return true; - } - private void defineCommonSplitMethodParameters() { defineSplitMethodParameter(0, CALLEE); defineSplitMethodParameter(1, THIS); @@ -2782,114 +2740,40 @@ method.onLocalStore(type, slot); } - private void fixScopeSlot(final FunctionNode functionNode) { + private int fixScopeSlot(final FunctionNode functionNode, final int extraSlot) { // TODO hack to move the scope to the expected slot (needed because split methods reuse the same slots as the root method) final int actualScopeSlot = functionNode.compilerConstant(SCOPE).getSlot(SCOPE_TYPE); final int defaultScopeSlot = SCOPE.slot(); + int newExtraSlot = extraSlot; if (actualScopeSlot != defaultScopeSlot) { - method.defineBlockLocalVariable(actualScopeSlot, actualScopeSlot + 1); + if (actualScopeSlot == extraSlot) { + newExtraSlot = extraSlot + 1; + method.defineBlockLocalVariable(newExtraSlot, newExtraSlot + 1); + method.load(Type.OBJECT, extraSlot); + method.storeHidden(Type.OBJECT, newExtraSlot); + } else { + method.defineBlockLocalVariable(actualScopeSlot, actualScopeSlot + 1); + } method.load(SCOPE_TYPE, defaultScopeSlot); method.storeCompilerConstant(SCOPE); } + return newExtraSlot; } @Override - public Node leaveSplitNode(final SplitNode splitNode) { - assert method instanceof SplitMethodEmitter; - lc.exitSplitNode(); - final boolean hasReturn = method.hasReturn(); - final SplitMethodEmitter splitMethod = ((SplitMethodEmitter)method); - final List