Mon, 20 Oct 2014 12:06:36 +0200
8059844: Implement optimistic splitter
Reviewed-by: hannesw, lagergren
1.1 --- a/src/jdk/nashorn/internal/codegen/ApplySpecialization.java Fri Oct 17 14:24:26 2014 +0200 1.2 +++ b/src/jdk/nashorn/internal/codegen/ApplySpecialization.java Mon Oct 20 12:06:36 2014 +0200 1.3 @@ -27,6 +27,7 @@ 1.4 1.5 import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS_VAR; 1.6 import static jdk.nashorn.internal.codegen.CompilerConstants.EXPLODED_ARGUMENT_PREFIX; 1.7 + 1.8 import java.lang.invoke.MethodType; 1.9 import java.util.ArrayDeque; 1.10 import java.util.ArrayList; 1.11 @@ -38,6 +39,7 @@ 1.12 import jdk.nashorn.internal.ir.CallNode; 1.13 import jdk.nashorn.internal.ir.Expression; 1.14 import jdk.nashorn.internal.ir.FunctionNode; 1.15 +import jdk.nashorn.internal.ir.FunctionNode.CompilationState; 1.16 import jdk.nashorn.internal.ir.IdentNode; 1.17 import jdk.nashorn.internal.ir.LexicalContext; 1.18 import jdk.nashorn.internal.ir.Node; 1.19 @@ -321,7 +323,7 @@ 1.20 1.21 explodedArguments.pop(); 1.22 1.23 - return newFunctionNode; 1.24 + return newFunctionNode.setState(lc, CompilationState.BUILTINS_TRANSFORMED); 1.25 } 1.26 1.27 private static boolean isApply(final CallNode callNode) {
2.1 --- a/src/jdk/nashorn/internal/codegen/AssignSymbols.java Fri Oct 17 14:24:26 2014 +0200 2.2 +++ b/src/jdk/nashorn/internal/codegen/AssignSymbols.java Mon Oct 20 12:06:36 2014 +0200 2.3 @@ -76,7 +76,6 @@ 2.4 import jdk.nashorn.internal.ir.Node; 2.5 import jdk.nashorn.internal.ir.RuntimeNode; 2.6 import jdk.nashorn.internal.ir.RuntimeNode.Request; 2.7 -import jdk.nashorn.internal.ir.SplitNode; 2.8 import jdk.nashorn.internal.ir.Statement; 2.9 import jdk.nashorn.internal.ir.SwitchNode; 2.10 import jdk.nashorn.internal.ir.Symbol; 2.11 @@ -135,9 +134,6 @@ 2.12 if (!(functionNode.hasScopeBlock() || functionNode.needsParentScope())) { 2.13 functionNode.compilerConstant(SCOPE).setNeedsSlot(false); 2.14 } 2.15 - if (!functionNode.usesReturnSymbol()) { 2.16 - functionNode.compilerConstant(RETURN).setNeedsSlot(false); 2.17 - } 2.18 // Named function expressions that end up not referencing themselves won't need a local slot for the self symbol. 2.19 if(!functionNode.isDeclared() && !functionNode.usesSelfSymbol() && !functionNode.isAnonymous()) { 2.20 final Symbol selfSymbol = functionNode.getBody().getExistingSymbol(functionNode.getIdent().getName()); 2.21 @@ -1014,7 +1010,7 @@ 2.22 boolean previousWasBlock = false; 2.23 for (final Iterator<LexicalContextNode> it = lc.getAllNodes(); it.hasNext();) { 2.24 final LexicalContextNode node = it.next(); 2.25 - if (node instanceof FunctionNode || node instanceof SplitNode || isSplitArray(node)) { 2.26 + if (node instanceof FunctionNode || isSplitArray(node)) { 2.27 // We reached the function boundary or a splitting boundary without seeing a definition for the symbol. 2.28 // It needs to be in scope. 2.29 return true;
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 3.2 +++ b/src/jdk/nashorn/internal/codegen/AstSerializer.java Mon Oct 20 12:06:36 2014 +0200 3.3 @@ -0,0 +1,71 @@ 3.4 +/* 3.5 + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. 3.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 3.7 + * 3.8 + * This code is free software; you can redistribute it and/or modify it 3.9 + * under the terms of the GNU General Public License version 2 only, as 3.10 + * published by the Free Software Foundation. Oracle designates this 3.11 + * particular file as subject to the "Classpath" exception as provided 3.12 + * by Oracle in the LICENSE file that accompanied this code. 3.13 + * 3.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 3.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 3.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 3.17 + * version 2 for more details (a copy is included in the LICENSE file that 3.18 + * accompanied this code). 3.19 + * 3.20 + * You should have received a copy of the GNU General Public License version 3.21 + * 2 along with this work; if not, write to the Free Software Foundation, 3.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 3.23 + * 3.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 3.25 + * or visit www.oracle.com if you need additional information or have any 3.26 + * questions. 3.27 + */ 3.28 +package jdk.nashorn.internal.codegen; 3.29 + 3.30 +import java.io.ByteArrayOutputStream; 3.31 +import java.io.IOException; 3.32 +import java.io.ObjectOutputStream; 3.33 +import java.util.Collections; 3.34 +import java.util.zip.Deflater; 3.35 +import java.util.zip.DeflaterOutputStream; 3.36 +import jdk.nashorn.internal.ir.Block; 3.37 +import jdk.nashorn.internal.ir.FunctionNode; 3.38 +import jdk.nashorn.internal.ir.LexicalContext; 3.39 +import jdk.nashorn.internal.ir.Node; 3.40 +import jdk.nashorn.internal.ir.Statement; 3.41 +import jdk.nashorn.internal.ir.visitor.NodeVisitor; 3.42 +import jdk.nashorn.internal.runtime.options.Options; 3.43 + 3.44 +/** 3.45 + * This static utility class performs serialization of FunctionNode ASTs to a byte array. 3.46 + * The format is a standard Java serialization stream, deflated. 3.47 + */ 3.48 +final class AstSerializer { 3.49 + // Experimentally, we concluded that compression level 4 gives a good tradeoff between serialization speed 3.50 + // and size. 3.51 + private static final int COMPRESSION_LEVEL = Options.getIntProperty("nashorn.serialize.compression", 4); 3.52 + static byte[] serialize(final FunctionNode fn) { 3.53 + final ByteArrayOutputStream out = new ByteArrayOutputStream(); 3.54 + try (final ObjectOutputStream oout = new ObjectOutputStream(new DeflaterOutputStream(out, 3.55 + new Deflater(COMPRESSION_LEVEL)))) { 3.56 + oout.writeObject(removeInnerFunctionBodies(fn)); 3.57 + } catch (final IOException e) { 3.58 + throw new AssertionError("Unexpected exception serializing function", e); 3.59 + } 3.60 + return out.toByteArray(); 3.61 + } 3.62 + 3.63 + private static FunctionNode removeInnerFunctionBodies(final FunctionNode fn) { 3.64 + return (FunctionNode)fn.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { 3.65 + @Override 3.66 + public Node leaveBlock(final Block block) { 3.67 + if (lc.isFunctionBody() && lc.getFunction(block) != lc.getOutermostFunction()) { 3.68 + return block.setStatements(lc, Collections.<Statement>emptyList()); 3.69 + } 3.70 + return super.leaveBlock(block); 3.71 + } 3.72 + }); 3.73 + } 3.74 +}
4.1 --- a/src/jdk/nashorn/internal/codegen/ClassEmitter.java Fri Oct 17 14:24:26 2014 +0200 4.2 +++ b/src/jdk/nashorn/internal/codegen/ClassEmitter.java Mon Oct 20 12:06:36 2014 +0200 4.3 @@ -51,6 +51,7 @@ 4.4 import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor; 4.5 import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor; 4.6 import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup; 4.7 + 4.8 import java.io.ByteArrayOutputStream; 4.9 import java.io.PrintWriter; 4.10 import java.security.AccessController; 4.11 @@ -64,7 +65,6 @@ 4.12 import jdk.internal.org.objectweb.asm.util.TraceClassVisitor; 4.13 import jdk.nashorn.internal.codegen.types.Type; 4.14 import jdk.nashorn.internal.ir.FunctionNode; 4.15 -import jdk.nashorn.internal.ir.SplitNode; 4.16 import jdk.nashorn.internal.ir.debug.NashornClassReader; 4.17 import jdk.nashorn.internal.ir.debug.NashornTextifier; 4.18 import jdk.nashorn.internal.runtime.Context; 4.19 @@ -476,12 +476,6 @@ 4.20 methodsStarted.remove(method); 4.21 } 4.22 4.23 - SplitMethodEmitter method(final SplitNode splitNode, final String methodName, final Class<?> rtype, final Class<?>... ptypes) { 4.24 - methodCount++; 4.25 - methodNames.add(methodName); 4.26 - return new SplitMethodEmitter(this, methodVisitor(EnumSet.of(Flag.PUBLIC, Flag.STATIC), methodName, rtype, ptypes), splitNode); 4.27 - } 4.28 - 4.29 /** 4.30 * Add a new method to the class - defaults to public method 4.31 *
5.1 --- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java Fri Oct 17 14:24:26 2014 +0200 5.2 +++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java Mon Oct 20 12:06:36 2014 +0200 5.3 @@ -34,9 +34,7 @@ 5.4 import static jdk.nashorn.internal.codegen.CompilerConstants.GET_STRING; 5.5 import static jdk.nashorn.internal.codegen.CompilerConstants.QUICK_PREFIX; 5.6 import static jdk.nashorn.internal.codegen.CompilerConstants.REGEX_PREFIX; 5.7 -import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN; 5.8 import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE; 5.9 -import static jdk.nashorn.internal.codegen.CompilerConstants.SPLIT_ARRAY_ARG; 5.10 import static jdk.nashorn.internal.codegen.CompilerConstants.SPLIT_PREFIX; 5.11 import static jdk.nashorn.internal.codegen.CompilerConstants.THIS; 5.12 import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS; 5.13 @@ -99,10 +97,10 @@ 5.14 import jdk.nashorn.internal.ir.ForNode; 5.15 import jdk.nashorn.internal.ir.FunctionNode; 5.16 import jdk.nashorn.internal.ir.FunctionNode.CompilationState; 5.17 +import jdk.nashorn.internal.ir.GetSplitState; 5.18 import jdk.nashorn.internal.ir.IdentNode; 5.19 import jdk.nashorn.internal.ir.IfNode; 5.20 import jdk.nashorn.internal.ir.IndexNode; 5.21 -import jdk.nashorn.internal.ir.JoinPredecessor; 5.22 import jdk.nashorn.internal.ir.JoinPredecessorExpression; 5.23 import jdk.nashorn.internal.ir.JumpStatement; 5.24 import jdk.nashorn.internal.ir.LabelNode; 5.25 @@ -121,7 +119,8 @@ 5.26 import jdk.nashorn.internal.ir.ReturnNode; 5.27 import jdk.nashorn.internal.ir.RuntimeNode; 5.28 import jdk.nashorn.internal.ir.RuntimeNode.Request; 5.29 -import jdk.nashorn.internal.ir.SplitNode; 5.30 +import jdk.nashorn.internal.ir.SetSplitState; 5.31 +import jdk.nashorn.internal.ir.SplitReturn; 5.32 import jdk.nashorn.internal.ir.Statement; 5.33 import jdk.nashorn.internal.ir.SwitchNode; 5.34 import jdk.nashorn.internal.ir.Symbol; 5.35 @@ -493,8 +492,7 @@ 5.36 //walk up the chain from starting block and when we bump into the current function boundary, add the external 5.37 //information. 5.38 final FunctionNode fn = lc.getCurrentFunction(); 5.39 - final int fnId = fn.getId(); 5.40 - final int externalDepth = compiler.getScriptFunctionData(fnId).getExternalSymbolDepth(symbol.getName()); 5.41 + final int externalDepth = compiler.getScriptFunctionData(fn.getId()).getExternalSymbolDepth(symbol.getName()); 5.42 5.43 //count the number of scopes from this place to the start of the function 5.44 5.45 @@ -1048,6 +1046,13 @@ 5.46 } 5.47 5.48 @Override 5.49 + public boolean enterGetSplitState(final GetSplitState getSplitState) { 5.50 + method.loadScope(); 5.51 + method.invoke(Scope.GET_SPLIT_STATE); 5.52 + return false; 5.53 + } 5.54 + 5.55 + @Override 5.56 public boolean enterDefault(final Node otherNode) { 5.57 // Must have handled all expressions that can legally be encountered. 5.58 throw new AssertionError(otherNode.getClass().getName()); 5.59 @@ -1219,7 +1224,7 @@ 5.60 popScopesUntil(target); 5.61 final Label targetLabel = jump.getTargetLabel(target); 5.62 targetLabel.markAsBreakTarget(); 5.63 - method.splitAwareGoto(lc, targetLabel, target); 5.64 + method._goto(targetLabel); 5.65 5.66 return false; 5.67 } 5.68 @@ -2029,10 +2034,10 @@ 5.69 } 5.70 5.71 private void lineNumber(final int lineNumber) { 5.72 - if (lineNumber != lastLineNumber) { 5.73 + if (lineNumber != lastLineNumber && lineNumber != Node.NO_LINE_NUMBER) { 5.74 method.lineNumber(lineNumber); 5.75 - } 5.76 - lastLineNumber = lineNumber; 5.77 + lastLineNumber = lineNumber; 5.78 + } 5.79 } 5.80 5.81 int getLastLineNumber() { 5.82 @@ -2079,13 +2084,14 @@ 5.83 method.begin(); 5.84 5.85 defineCommonSplitMethodParameters(); 5.86 - defineSplitMethodParameter(3, arrayType); 5.87 - 5.88 - fixScopeSlot(currentFunction); 5.89 + defineSplitMethodParameter(CompilerConstants.SPLIT_ARRAY_ARG.slot(), arrayType); 5.90 + 5.91 + // NOTE: when this is no longer needed, SplitIntoFunctions will no longer have to add IS_SPLIT 5.92 + // to synthetic functions, and FunctionNode.needsCallee() will no longer need to test for isSplit(). 5.93 + final int arraySlot = fixScopeSlot(currentFunction, 3); 5.94 5.95 lc.enterSplitNode(); 5.96 5.97 - final int arraySlot = SPLIT_ARRAY_ARG.slot(); 5.98 for (int i = arrayUnit.getLo(); i < arrayUnit.getHi(); i++) { 5.99 method.load(arrayType, arraySlot); 5.100 storeElement(nodes, elementType, postsets[i]); 5.101 @@ -2700,73 +2706,6 @@ 5.102 method.convert(newRuntimeNode.getType()); 5.103 } 5.104 5.105 - @Override 5.106 - public boolean enterSplitNode(final SplitNode splitNode) { 5.107 - if(!method.isReachable()) { 5.108 - return false; 5.109 - } 5.110 - 5.111 - final CompileUnit splitCompileUnit = splitNode.getCompileUnit(); 5.112 - 5.113 - final FunctionNode fn = lc.getCurrentFunction(); 5.114 - final String className = splitCompileUnit.getUnitClassName(); 5.115 - final String name = splitNode.getName(); 5.116 - 5.117 - final Type returnType = fn.getReturnType(); 5.118 - 5.119 - final Class<?> rtype = fn.getReturnType().getTypeClass(); 5.120 - final boolean needsArguments = fn.needsArguments(); 5.121 - final Class<?>[] ptypes = needsArguments ? 5.122 - new Class<?>[] {ScriptFunction.class, Object.class, ScriptObject.class, ScriptObject.class} : 5.123 - new Class<?>[] {ScriptFunction.class, Object.class, ScriptObject.class}; 5.124 - 5.125 - final MethodEmitter caller = method; 5.126 - unit = lc.pushCompileUnit(splitCompileUnit); 5.127 - 5.128 - final Call splitCall = staticCallNoLookup( 5.129 - className, 5.130 - name, 5.131 - methodDescriptor(rtype, ptypes)); 5.132 - 5.133 - final MethodEmitter splitEmitter = 5.134 - splitCompileUnit.getClassEmitter().method( 5.135 - splitNode, 5.136 - name, 5.137 - rtype, 5.138 - ptypes); 5.139 - 5.140 - pushMethodEmitter(splitEmitter); 5.141 - method.setFunctionNode(fn); 5.142 - 5.143 - assert fn.needsCallee() : "split function should require callee"; 5.144 - caller.loadCompilerConstant(CALLEE); 5.145 - caller.loadCompilerConstant(THIS); 5.146 - caller.loadCompilerConstant(SCOPE); 5.147 - if (needsArguments) { 5.148 - caller.loadCompilerConstant(ARGUMENTS); 5.149 - } 5.150 - caller.invoke(splitCall); 5.151 - caller.storeCompilerConstant(RETURN, returnType); 5.152 - 5.153 - method.begin(); 5.154 - 5.155 - defineCommonSplitMethodParameters(); 5.156 - if(needsArguments) { 5.157 - defineSplitMethodParameter(3, ARGUMENTS); 5.158 - } 5.159 - 5.160 - // Copy scope to its target slot as first thing because the original slot could be used by return symbol. 5.161 - fixScopeSlot(fn); 5.162 - 5.163 - final int returnSlot = fn.compilerConstant(RETURN).getSlot(returnType); 5.164 - method.defineBlockLocalVariable(returnSlot, returnSlot + returnType.getSlots()); 5.165 - method.loadUndefined(returnType); 5.166 - method.storeCompilerConstant(RETURN, returnType); 5.167 - 5.168 - lc.enterSplitNode(); 5.169 - return true; 5.170 - } 5.171 - 5.172 private void defineCommonSplitMethodParameters() { 5.173 defineSplitMethodParameter(0, CALLEE); 5.174 defineSplitMethodParameter(1, THIS); 5.175 @@ -2782,114 +2721,40 @@ 5.176 method.onLocalStore(type, slot); 5.177 } 5.178 5.179 - private void fixScopeSlot(final FunctionNode functionNode) { 5.180 + private int fixScopeSlot(final FunctionNode functionNode, final int extraSlot) { 5.181 // TODO hack to move the scope to the expected slot (needed because split methods reuse the same slots as the root method) 5.182 final int actualScopeSlot = functionNode.compilerConstant(SCOPE).getSlot(SCOPE_TYPE); 5.183 final int defaultScopeSlot = SCOPE.slot(); 5.184 + int newExtraSlot = extraSlot; 5.185 if (actualScopeSlot != defaultScopeSlot) { 5.186 - method.defineBlockLocalVariable(actualScopeSlot, actualScopeSlot + 1); 5.187 + if (actualScopeSlot == extraSlot) { 5.188 + newExtraSlot = extraSlot + 1; 5.189 + method.defineBlockLocalVariable(newExtraSlot, newExtraSlot + 1); 5.190 + method.load(Type.OBJECT, extraSlot); 5.191 + method.storeHidden(Type.OBJECT, newExtraSlot); 5.192 + } else { 5.193 + method.defineBlockLocalVariable(actualScopeSlot, actualScopeSlot + 1); 5.194 + } 5.195 method.load(SCOPE_TYPE, defaultScopeSlot); 5.196 method.storeCompilerConstant(SCOPE); 5.197 } 5.198 + return newExtraSlot; 5.199 } 5.200 5.201 @Override 5.202 - public Node leaveSplitNode(final SplitNode splitNode) { 5.203 - assert method instanceof SplitMethodEmitter; 5.204 - lc.exitSplitNode(); 5.205 - final boolean hasReturn = method.hasReturn(); 5.206 - final SplitMethodEmitter splitMethod = ((SplitMethodEmitter)method); 5.207 - final List<Label> targets = splitMethod.getExternalTargets(); 5.208 - final boolean hasControlFlow = hasReturn || !targets.isEmpty(); 5.209 - final List<BreakableNode> targetNodes = splitMethod.getExternalTargetNodes(); 5.210 - final Type returnType = lc.getCurrentFunction().getReturnType(); 5.211 - 5.212 - try { 5.213 - // Wrap up this method. 5.214 - 5.215 - if(method.isReachable()) { 5.216 - if (hasControlFlow) { 5.217 - method.setSplitState(-1); 5.218 - } 5.219 - method.loadCompilerConstant(RETURN, returnType); 5.220 - method._return(returnType); 5.221 - } 5.222 - method.end(); 5.223 - 5.224 - lc.releaseSlots(); 5.225 - 5.226 - unit = lc.popCompileUnit(splitNode.getCompileUnit()); 5.227 - popMethodEmitter(); 5.228 - 5.229 - } catch (final Throwable t) { 5.230 - Context.printStackTrace(t); 5.231 - final VerifyError e = new VerifyError("Code generation bug in \"" + splitNode.getName() + "\": likely stack misaligned: " + t + " " + getCurrentSource().getName()); 5.232 - e.initCause(t); 5.233 - throw e; 5.234 - } 5.235 - 5.236 - //no external jump targets or return in switch node 5.237 - if (!hasControlFlow) { 5.238 - return splitNode; 5.239 - } 5.240 - 5.241 - // Handle return from split method if there was one. 5.242 - final MethodEmitter caller = method; 5.243 - final int targetCount = targets.size(); 5.244 - 5.245 - caller.loadScope(); 5.246 - caller.invoke(Scope.GET_SPLIT_STATE); 5.247 - 5.248 - final Label breakLabel = new Label("no_split_state"); 5.249 - // Split state is -1 for no split state, 0 for return, 1..n+1 for break/continue 5.250 - 5.251 - //the common case is that we don't need a switch 5.252 - if (targetCount == 0) { 5.253 - assert hasReturn; 5.254 - caller.ifne(breakLabel); 5.255 - //has to be zero 5.256 - caller.label(new Label("split_return")); 5.257 - caller.loadCompilerConstant(RETURN, returnType); 5.258 - caller._return(returnType); 5.259 - caller.label(breakLabel); 5.260 - } else { 5.261 - assert !targets.isEmpty(); 5.262 - 5.263 - final int low = hasReturn ? 0 : 1; 5.264 - final int labelCount = targetCount + 1 - low; 5.265 - final Label[] labels = new Label[labelCount]; 5.266 - 5.267 - for (int i = 0; i < labelCount; i++) { 5.268 - labels[i] = new Label(i == 0 ? "split_return" : "split_" + targets.get(i - 1)); 5.269 - } 5.270 - caller.tableswitch(low, targetCount, breakLabel, labels); 5.271 - for (int i = low; i <= targetCount; i++) { 5.272 - caller.label(labels[i - low]); 5.273 - if (i == 0) { 5.274 - caller.loadCompilerConstant(RETURN, returnType); 5.275 - caller._return(returnType); 5.276 - } else { 5.277 - final BreakableNode targetNode = targetNodes.get(i - 1); 5.278 - final Label label = targets.get(i - 1); 5.279 - if (!lc.isExternalTarget(splitNode, targetNode)) { 5.280 - final JoinPredecessor jumpOrigin = splitNode.getJumpOrigin(label); 5.281 - if(jumpOrigin != null) { 5.282 - method.beforeJoinPoint(jumpOrigin); 5.283 - } 5.284 - popScopesUntil(targetNode); 5.285 - } 5.286 - caller.splitAwareGoto(lc, label, targetNode); 5.287 - } 5.288 - } 5.289 - caller.label(breakLabel); 5.290 - } 5.291 - 5.292 - // If split has a return and caller is itself a split method it needs to propagate the return. 5.293 - if (hasReturn) { 5.294 - caller.setHasReturn(); 5.295 - } 5.296 - 5.297 - return splitNode; 5.298 + public boolean enterSplitReturn(final SplitReturn splitReturn) { 5.299 + if (method.isReachable()) { 5.300 + method.loadUndefined(lc.getCurrentFunction().getReturnType())._return(); 5.301 + } 5.302 + return false; 5.303 + } 5.304 + 5.305 + @Override 5.306 + public boolean enterSetSplitState(final SetSplitState setSplitState) { 5.307 + if (method.isReachable()) { 5.308 + method.setSplitState(setSplitState.getState()); 5.309 + } 5.310 + return false; 5.311 } 5.312 5.313 @Override 5.314 @@ -4379,11 +4244,7 @@ 5.315 private void newFunctionObject(final FunctionNode functionNode, final boolean addInitializer) { 5.316 assert lc.peek() == functionNode; 5.317 5.318 - final int fnId = functionNode.getId(); 5.319 - 5.320 - final RecompilableScriptFunctionData data = compiler.getScriptFunctionData(fnId); 5.321 - 5.322 - assert data != null : functionNode.getName() + " has no data"; 5.323 + final RecompilableScriptFunctionData data = compiler.getScriptFunctionData(functionNode.getId()); 5.324 5.325 if (functionNode.isProgram() && !compiler.isOnDemandCompilation()) { 5.326 final CompileUnit fnUnit = functionNode.getCompileUnit();
6.1 --- a/src/jdk/nashorn/internal/codegen/CompilationPhase.java Fri Oct 17 14:24:26 2014 +0200 6.2 +++ b/src/jdk/nashorn/internal/codegen/CompilationPhase.java Mon Oct 20 12:06:36 2014 +0200 6.3 @@ -38,12 +38,11 @@ 6.4 import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SPLIT; 6.5 import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SYMBOLS_ASSIGNED; 6.6 import static jdk.nashorn.internal.runtime.logging.DebugLogger.quote; 6.7 + 6.8 import java.io.PrintWriter; 6.9 -import java.util.ArrayList; 6.10 import java.util.EnumSet; 6.11 import java.util.HashMap; 6.12 import java.util.LinkedHashMap; 6.13 -import java.util.List; 6.14 import java.util.Map; 6.15 import java.util.Map.Entry; 6.16 import java.util.Set; 6.17 @@ -53,10 +52,7 @@ 6.18 import jdk.nashorn.internal.ir.FunctionNode.CompilationState; 6.19 import jdk.nashorn.internal.ir.LexicalContext; 6.20 import jdk.nashorn.internal.ir.LiteralNode; 6.21 -import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; 6.22 -import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit; 6.23 import jdk.nashorn.internal.ir.Node; 6.24 -import jdk.nashorn.internal.ir.SplitNode; 6.25 import jdk.nashorn.internal.ir.debug.ASTWriter; 6.26 import jdk.nashorn.internal.ir.debug.PrintVisitor; 6.27 import jdk.nashorn.internal.ir.visitor.NodeVisitor; 6.28 @@ -81,7 +77,7 @@ 6.29 PARSED)) { 6.30 @Override 6.31 FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 6.32 - return (FunctionNode)fn.accept(new FoldConstants(compiler)); 6.33 + return transformFunction(fn, new FoldConstants(compiler)); 6.34 } 6.35 6.36 @Override 6.37 @@ -104,7 +100,7 @@ 6.38 CONSTANT_FOLDED)) { 6.39 @Override 6.40 FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 6.41 - return (FunctionNode)fn.accept(new Lower(compiler)); 6.42 + return transformFunction(fn, new Lower(compiler)); 6.43 } 6.44 6.45 @Override 6.46 @@ -118,23 +114,6 @@ 6.47 * optimistic ops a program point so that an UnwarrantedException knows from where 6.48 * a guess went wrong when creating the continuation to roll back this execution 6.49 */ 6.50 - PROGRAM_POINT_PHASE( 6.51 - EnumSet.of( 6.52 - INITIALIZED, 6.53 - PARSED, 6.54 - CONSTANT_FOLDED, 6.55 - LOWERED)) { 6.56 - @Override 6.57 - FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 6.58 - return (FunctionNode)fn.accept(new ProgramPoints()); 6.59 - } 6.60 - 6.61 - @Override 6.62 - public String toString() { 6.63 - return "'Program Point Calculation'"; 6.64 - } 6.65 - }, 6.66 - 6.67 TRANSFORM_BUILTINS_PHASE( 6.68 EnumSet.of( 6.69 INITIALIZED, 6.70 @@ -144,13 +123,7 @@ 6.71 //we only do this if we have a param type map, otherwise this is not a specialized recompile 6.72 @Override 6.73 FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 6.74 - final FunctionNode newFunctionNode = (FunctionNode)fn.accept(new ApplySpecialization(compiler)); 6.75 - return (FunctionNode)newFunctionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { 6.76 - @Override 6.77 - public Node leaveFunctionNode(final FunctionNode node) { 6.78 - return node.setState(lc, BUILTINS_TRANSFORMED); 6.79 - } 6.80 - }); 6.81 + return setStates(transformFunction(fn, new ApplySpecialization(compiler)), BUILTINS_TRANSFORMED); 6.82 } 6.83 6.84 @Override 6.85 @@ -177,7 +150,7 @@ 6.86 FunctionNode newFunctionNode; 6.87 6.88 //ensure elementTypes, postsets and presets exist for splitter and arraynodes 6.89 - newFunctionNode = (FunctionNode)fn.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { 6.90 + newFunctionNode = transformFunction(fn, new NodeVisitor<LexicalContext>(new LexicalContext()) { 6.91 @Override 6.92 public LiteralNode<?> leaveLiteralNode(final LiteralNode<?> literalNode) { 6.93 return literalNode.initialize(lc); 6.94 @@ -185,7 +158,7 @@ 6.95 }); 6.96 6.97 newFunctionNode = new Splitter(compiler, newFunctionNode, outermostCompileUnit).split(newFunctionNode, true); 6.98 - 6.99 + newFunctionNode = transformFunction(newFunctionNode, new SplitIntoFunctions(compiler)); 6.100 assert newFunctionNode.getCompileUnit() == outermostCompileUnit : "fn=" + fn.getName() + ", fn.compileUnit (" + newFunctionNode.getCompileUnit() + ") != " + outermostCompileUnit; 6.101 assert newFunctionNode.isStrict() == compiler.isStrict() : "functionNode.isStrict() != compiler.isStrict() for " + quote(newFunctionNode.getName()); 6.102 6.103 @@ -198,6 +171,52 @@ 6.104 } 6.105 }, 6.106 6.107 + PROGRAM_POINT_PHASE( 6.108 + EnumSet.of( 6.109 + INITIALIZED, 6.110 + PARSED, 6.111 + CONSTANT_FOLDED, 6.112 + LOWERED, 6.113 + BUILTINS_TRANSFORMED, 6.114 + SPLIT)) { 6.115 + @Override 6.116 + FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 6.117 + return transformFunction(fn, new ProgramPoints()); 6.118 + } 6.119 + 6.120 + @Override 6.121 + public String toString() { 6.122 + return "'Program Point Calculation'"; 6.123 + } 6.124 + }, 6.125 + 6.126 + SERIALIZE_SPLIT_PHASE( 6.127 + EnumSet.of( 6.128 + INITIALIZED, 6.129 + PARSED, 6.130 + CONSTANT_FOLDED, 6.131 + LOWERED, 6.132 + BUILTINS_TRANSFORMED, 6.133 + SPLIT)) { 6.134 + @Override 6.135 + FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 6.136 + return transformFunction(fn, new NodeVisitor<LexicalContext>(new LexicalContext()) { 6.137 + @Override 6.138 + public boolean enterFunctionNode(final FunctionNode functionNode) { 6.139 + if (functionNode.isSplit()) { 6.140 + compiler.serializeAst(functionNode); 6.141 + } 6.142 + return true; 6.143 + } 6.144 + }); 6.145 + } 6.146 + 6.147 + @Override 6.148 + public String toString() { 6.149 + return "'Serialize Split Functions'"; 6.150 + } 6.151 + }, 6.152 + 6.153 SYMBOL_ASSIGNMENT_PHASE( 6.154 EnumSet.of( 6.155 INITIALIZED, 6.156 @@ -208,7 +227,7 @@ 6.157 SPLIT)) { 6.158 @Override 6.159 FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 6.160 - return (FunctionNode)fn.accept(new AssignSymbols(compiler)); 6.161 + return transformFunction(fn, new AssignSymbols(compiler)); 6.162 } 6.163 6.164 @Override 6.165 @@ -228,7 +247,7 @@ 6.166 SYMBOLS_ASSIGNED)) { 6.167 @Override 6.168 FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 6.169 - return (FunctionNode)fn.accept(new FindScopeDepths(compiler)); 6.170 + return transformFunction(fn, new FindScopeDepths(compiler)); 6.171 } 6.172 6.173 @Override 6.174 @@ -250,7 +269,7 @@ 6.175 @Override 6.176 FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 6.177 if (compiler.useOptimisticTypes()) { 6.178 - return (FunctionNode)fn.accept(new OptimisticTypesCalculator(compiler)); 6.179 + return transformFunction(fn, new OptimisticTypesCalculator(compiler)); 6.180 } 6.181 return setStates(fn, OPTIMISTIC_TYPES_ASSIGNED); 6.182 } 6.183 @@ -274,8 +293,7 @@ 6.184 OPTIMISTIC_TYPES_ASSIGNED)) { 6.185 @Override 6.186 FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 6.187 - final FunctionNode newFunctionNode = (FunctionNode)fn.accept(new LocalVariableTypesCalculator(compiler)); 6.188 - 6.189 + final FunctionNode newFunctionNode = transformFunction(fn, new LocalVariableTypesCalculator(compiler)); 6.190 final ScriptEnvironment senv = compiler.getScriptEnvironment(); 6.191 final PrintWriter err = senv.getErr(); 6.192 6.193 @@ -330,13 +348,7 @@ 6.194 6.195 for (final CompileUnit oldUnit : compiler.getCompileUnits()) { 6.196 assert map.get(oldUnit) == null; 6.197 - final StringBuilder sb = new StringBuilder(compiler.nextCompileUnitName()); 6.198 - if (phases.isRestOfCompilation()) { 6.199 - sb.append("$restOf"); 6.200 - } 6.201 - //it's ok to not copy the initCount, methodCount and clinitCount here, as codegen is what 6.202 - //fills those out anyway. Thus no need for a copy constructor 6.203 - final CompileUnit newUnit = compiler.createCompileUnit(sb.toString(), oldUnit.getWeight()); 6.204 + final CompileUnit newUnit = createNewCompileUnit(compiler, phases); 6.205 log.fine("Creating new compile unit ", oldUnit, " => ", newUnit); 6.206 map.put(oldUnit, newUnit); 6.207 assert newUnit != null; 6.208 @@ -350,47 +362,10 @@ 6.209 //replace old compile units in function nodes, if any are assigned, 6.210 //for example by running the splitter on this function node in a previous 6.211 //partial code generation 6.212 - final FunctionNode newFunctionNode = (FunctionNode)fn.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { 6.213 + final FunctionNode newFunctionNode = transformFunction(fn, new ReplaceCompileUnits() { 6.214 @Override 6.215 - public Node leaveFunctionNode(final FunctionNode node) { 6.216 - final CompileUnit oldUnit = node.getCompileUnit(); 6.217 - assert oldUnit != null : "no compile unit in function node"; 6.218 - 6.219 - final CompileUnit newUnit = map.get(oldUnit); 6.220 - assert newUnit != null : "old unit has no mapping to new unit " + oldUnit; 6.221 - 6.222 - log.fine("Replacing compile unit: ", oldUnit, " => ", newUnit, " in ", quote(node.getName())); 6.223 - return node.setCompileUnit(lc, newUnit).setState(lc, CompilationState.COMPILE_UNITS_REUSED); 6.224 - } 6.225 - 6.226 - @Override 6.227 - public Node leaveSplitNode(final SplitNode node) { 6.228 - final CompileUnit oldUnit = node.getCompileUnit(); 6.229 - assert oldUnit != null : "no compile unit in function node"; 6.230 - 6.231 - final CompileUnit newUnit = map.get(oldUnit); 6.232 - assert newUnit != null : "old unit has no mapping to new unit " + oldUnit; 6.233 - 6.234 - log.fine("Replacing compile unit: ", oldUnit, " => ", newUnit, " in ", quote(node.getName())); 6.235 - return node.setCompileUnit(lc, newUnit); 6.236 - } 6.237 - 6.238 - @Override 6.239 - public Node leaveLiteralNode(final LiteralNode<?> node) { 6.240 - if (node instanceof ArrayLiteralNode) { 6.241 - final ArrayLiteralNode aln = (ArrayLiteralNode)node; 6.242 - if (aln.getUnits() == null) { 6.243 - return node; 6.244 - } 6.245 - final List<ArrayUnit> newArrayUnits = new ArrayList<>(); 6.246 - for (final ArrayUnit au : aln.getUnits()) { 6.247 - final CompileUnit newUnit = map.get(au.getCompileUnit()); 6.248 - assert newUnit != null; 6.249 - newArrayUnits.add(new ArrayUnit(newUnit, au.getLo(), au.getHi())); 6.250 - } 6.251 - return aln.setUnits(lc, newArrayUnits); 6.252 - } 6.253 - return node; 6.254 + CompileUnit getReplacement(CompileUnit original) { 6.255 + return map.get(original); 6.256 } 6.257 6.258 @Override 6.259 @@ -408,7 +383,59 @@ 6.260 } 6.261 }, 6.262 6.263 - /** 6.264 + REINITIALIZE_SERIALIZED( 6.265 + EnumSet.of( 6.266 + INITIALIZED, 6.267 + PARSED, 6.268 + CONSTANT_FOLDED, 6.269 + LOWERED, 6.270 + BUILTINS_TRANSFORMED, 6.271 + SPLIT)) { 6.272 + @Override 6.273 + FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { 6.274 + final Set<CompileUnit> unitSet = CompileUnit.createCompileUnitSet(); 6.275 + final Map<CompileUnit, CompileUnit> unitMap = new HashMap<>(); 6.276 + 6.277 + // Ensure that the FunctionNode's compile unit is the first in the list of new units. Install phase 6.278 + // will use that as the root class. 6.279 + createCompileUnit(fn.getCompileUnit(), unitSet, unitMap, compiler, phases); 6.280 + 6.281 + final FunctionNode newFn = transformFunction(fn, new ReplaceCompileUnits() { 6.282 + @Override 6.283 + CompileUnit getReplacement(final CompileUnit oldUnit) { 6.284 + final CompileUnit existing = unitMap.get(oldUnit); 6.285 + if (existing != null) { 6.286 + return existing; 6.287 + } 6.288 + return createCompileUnit(oldUnit, unitSet, unitMap, compiler, phases); 6.289 + } 6.290 + 6.291 + @Override 6.292 + public Node leaveFunctionNode(final FunctionNode fn2) { 6.293 + return super.leaveFunctionNode( 6.294 + // restore flags for deserialized nested function nodes 6.295 + compiler.getScriptFunctionData(fn2.getId()).restoreFlags(lc, fn2)); 6.296 + }; 6.297 + }); 6.298 + compiler.replaceCompileUnits(unitSet); 6.299 + return newFn; 6.300 + } 6.301 + 6.302 + private CompileUnit createCompileUnit(final CompileUnit oldUnit, final Set<CompileUnit> unitSet, 6.303 + final Map<CompileUnit, CompileUnit> unitMap, final Compiler compiler, final CompilationPhases phases) { 6.304 + final CompileUnit newUnit = createNewCompileUnit(compiler, phases); 6.305 + unitMap.put(oldUnit, newUnit); 6.306 + unitSet.add(newUnit); 6.307 + return newUnit; 6.308 + } 6.309 + 6.310 + @Override 6.311 + public String toString() { 6.312 + return "'Deserialize'"; 6.313 + } 6.314 + }, 6.315 + 6.316 + /** 6.317 * Bytecode generation: 6.318 * 6.319 * Generate the byte code class(es) resulting from the compiled FunctionNode 6.320 @@ -443,7 +470,7 @@ 6.321 try { 6.322 // Explicitly set BYTECODE_GENERATED here; it can not be set in case of skipping codegen for :program 6.323 // in the lazy + optimistic world. See CodeGenerator.skipFunction(). 6.324 - newFunctionNode = ((FunctionNode)newFunctionNode.accept(codegen)).setState(null, BYTECODE_GENERATED); 6.325 + newFunctionNode = transformFunction(newFunctionNode, codegen).setState(null, BYTECODE_GENERATED); 6.326 codegen.generateScopeCalls(); 6.327 } catch (final VerifyError e) { 6.328 if (senv._verify_code || senv._print_code) { 6.329 @@ -615,7 +642,7 @@ 6.330 if (!AssertsEnabled.assertsEnabled()) { 6.331 return functionNode; 6.332 } 6.333 - return (FunctionNode)functionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { 6.334 + return transformFunction(functionNode, new NodeVisitor<LexicalContext>(new LexicalContext()) { 6.335 @Override 6.336 public Node leaveFunctionNode(final FunctionNode fn) { 6.337 return fn.setState(lc, state); 6.338 @@ -701,4 +728,17 @@ 6.339 return end(compiler, transform(compiler, phases, begin(compiler, functionNode))); 6.340 } 6.341 6.342 + private static FunctionNode transformFunction(final FunctionNode fn, final NodeVisitor<?> visitor) { 6.343 + return (FunctionNode) fn.accept(visitor); 6.344 + } 6.345 + 6.346 + private static CompileUnit createNewCompileUnit(final Compiler compiler, final CompilationPhases phases) { 6.347 + final StringBuilder sb = new StringBuilder(compiler.nextCompileUnitName()); 6.348 + if (phases.isRestOfCompilation()) { 6.349 + sb.append("$restOf"); 6.350 + } 6.351 + //it's ok to not copy the initCount, methodCount and clinitCount here, as codegen is what 6.352 + //fills those out anyway. Thus no need for a copy constructor 6.353 + return compiler.createCompileUnit(sb.toString(), 0); 6.354 + } 6.355 }
7.1 --- a/src/jdk/nashorn/internal/codegen/CompileUnit.java Fri Oct 17 14:24:26 2014 +0200 7.2 +++ b/src/jdk/nashorn/internal/codegen/CompileUnit.java Mon Oct 20 12:06:36 2014 +0200 7.3 @@ -28,11 +28,13 @@ 7.4 import java.io.Serializable; 7.5 import java.util.Set; 7.6 import java.util.TreeSet; 7.7 +import jdk.nashorn.internal.ir.CompileUnitHolder; 7.8 7.9 /** 7.10 * Used to track split class compilation. Note that instances of the class are serializable, but all fields are 7.11 * transient, making the serialized version of the class only useful for tracking the referential topology of other 7.12 - * AST nodes referencing the same or different compile units. 7.13 + * AST nodes referencing the same or different compile units. We do want to preserve this topology though as 7.14 + * {@link CompileUnitHolder}s in a deserialized AST will undergo reinitialization. 7.15 */ 7.16 public final class CompileUnit implements Comparable<CompileUnit>, Serializable { 7.17 private static final long serialVersionUID = 1L; 7.18 @@ -127,14 +129,6 @@ 7.19 } 7.20 7.21 /** 7.22 - * Get the current weight of the compile unit. 7.23 - * @return the unit's weight 7.24 - */ 7.25 - long getWeight() { 7.26 - return weight; 7.27 - } 7.28 - 7.29 - /** 7.30 * Check if this compile unit can hold {@code weight} more units of weight 7.31 * @param w weight to check if can be added 7.32 * @return true if weight fits in this compile unit 7.33 @@ -160,7 +154,7 @@ 7.34 } 7.35 7.36 private static String shortName(final String name) { 7.37 - return name.lastIndexOf('/') == -1 ? name : name.substring(name.lastIndexOf('/') + 1); 7.38 + return name == null ? null : name.lastIndexOf('/') == -1 ? name : name.substring(name.lastIndexOf('/') + 1); 7.39 } 7.40 7.41 @Override
8.1 --- a/src/jdk/nashorn/internal/codegen/Compiler.java Fri Oct 17 14:24:26 2014 +0200 8.2 +++ b/src/jdk/nashorn/internal/codegen/Compiler.java Mon Oct 20 12:06:36 2014 +0200 8.3 @@ -32,15 +32,16 @@ 8.4 import static jdk.nashorn.internal.codegen.CompilerConstants.THIS; 8.5 import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS; 8.6 import static jdk.nashorn.internal.runtime.logging.DebugLogger.quote; 8.7 + 8.8 import java.io.File; 8.9 import java.lang.invoke.MethodType; 8.10 +import java.util.ArrayList; 8.11 import java.util.Arrays; 8.12 import java.util.Collections; 8.13 import java.util.Comparator; 8.14 import java.util.HashMap; 8.15 import java.util.Iterator; 8.16 import java.util.LinkedHashMap; 8.17 -import java.util.LinkedList; 8.18 import java.util.List; 8.19 import java.util.Map; 8.20 import java.util.Set; 8.21 @@ -159,75 +160,142 @@ 8.22 */ 8.23 private static final int COMPILE_UNIT_NAME_BUFFER_SIZE = 32; 8.24 8.25 + private final Map<Integer, byte[]> serializedAsts = new HashMap<>(); 8.26 + 8.27 /** 8.28 * Compilation phases that a compilation goes through 8.29 */ 8.30 public static class CompilationPhases implements Iterable<CompilationPhase> { 8.31 8.32 - /** Singleton that describes a standard eager compilation - this includes code installation */ 8.33 - public final static CompilationPhases COMPILE_ALL = new CompilationPhases( 8.34 - "Compile all", 8.35 - new CompilationPhase[] { 8.36 - CompilationPhase.CONSTANT_FOLDING_PHASE, 8.37 - CompilationPhase.LOWERING_PHASE, 8.38 - CompilationPhase.PROGRAM_POINT_PHASE, 8.39 - CompilationPhase.TRANSFORM_BUILTINS_PHASE, 8.40 - CompilationPhase.SPLITTING_PHASE, 8.41 - CompilationPhase.SYMBOL_ASSIGNMENT_PHASE, 8.42 - CompilationPhase.SCOPE_DEPTH_COMPUTATION_PHASE, 8.43 - CompilationPhase.OPTIMISTIC_TYPE_ASSIGNMENT_PHASE, 8.44 - CompilationPhase.LOCAL_VARIABLE_TYPE_CALCULATION_PHASE, 8.45 - CompilationPhase.BYTECODE_GENERATION_PHASE, 8.46 - CompilationPhase.INSTALL_PHASE 8.47 - }); 8.48 + /** 8.49 + * Singleton that describes compilation up to the phase where a function can be serialized. 8.50 + */ 8.51 + private final static CompilationPhases COMPILE_UPTO_SERIALIZABLE = new CompilationPhases( 8.52 + "Common initial phases", 8.53 + CompilationPhase.CONSTANT_FOLDING_PHASE, 8.54 + CompilationPhase.LOWERING_PHASE, 8.55 + CompilationPhase.TRANSFORM_BUILTINS_PHASE, 8.56 + CompilationPhase.SPLITTING_PHASE, 8.57 + CompilationPhase.PROGRAM_POINT_PHASE, 8.58 + CompilationPhase.SERIALIZE_SPLIT_PHASE 8.59 + ); 8.60 8.61 - /** Compile all for a rest of method */ 8.62 - public final static CompilationPhases COMPILE_ALL_RESTOF = 8.63 - COMPILE_ALL.setDescription("Compile all, rest of").addAfter(CompilationPhase.LOCAL_VARIABLE_TYPE_CALCULATION_PHASE, CompilationPhase.REUSE_COMPILE_UNITS_PHASE); 8.64 + private final static CompilationPhases COMPILE_SERIALIZABLE_UPTO_BYTECODE = new CompilationPhases( 8.65 + "After common phases, before bytecode generator", 8.66 + CompilationPhase.SYMBOL_ASSIGNMENT_PHASE, 8.67 + CompilationPhase.SCOPE_DEPTH_COMPUTATION_PHASE, 8.68 + CompilationPhase.OPTIMISTIC_TYPE_ASSIGNMENT_PHASE, 8.69 + CompilationPhase.LOCAL_VARIABLE_TYPE_CALCULATION_PHASE 8.70 + ); 8.71 8.72 - /** Singleton that describes a standard eager compilation, but no installation, for example used by --compile-only */ 8.73 - public final static CompilationPhases COMPILE_ALL_NO_INSTALL = 8.74 - COMPILE_ALL. 8.75 - removeLast(). 8.76 - setDescription("Compile without install"); 8.77 - 8.78 - /** Singleton that describes compilation up to the CodeGenerator, but not actually generating code */ 8.79 - public final static CompilationPhases COMPILE_UPTO_BYTECODE = 8.80 - COMPILE_ALL. 8.81 - removeLast(). 8.82 - removeLast(). 8.83 - setDescription("Compile upto bytecode"); 8.84 + /** 8.85 + * Singleton that describes additional steps to be taken after deserializing, all the way up to (but not 8.86 + * including) generating and installing code. 8.87 + */ 8.88 + public final static CompilationPhases RECOMPILE_SERIALIZED_UPTO_BYTECODE = new CompilationPhases( 8.89 + "Recompile serialized function up to bytecode", 8.90 + CompilationPhase.REINITIALIZE_SERIALIZED, 8.91 + COMPILE_SERIALIZABLE_UPTO_BYTECODE 8.92 + ); 8.93 8.94 /** 8.95 * Singleton that describes back end of method generation, given that we have generated the normal 8.96 * method up to CodeGenerator as in {@link CompilationPhases#COMPILE_UPTO_BYTECODE} 8.97 */ 8.98 - public final static CompilationPhases COMPILE_FROM_BYTECODE = new CompilationPhases( 8.99 + public final static CompilationPhases GENERATE_BYTECODE_AND_INSTALL = new CompilationPhases( 8.100 "Generate bytecode and install", 8.101 - new CompilationPhase[] { 8.102 - CompilationPhase.BYTECODE_GENERATION_PHASE, 8.103 - CompilationPhase.INSTALL_PHASE 8.104 - }); 8.105 + CompilationPhase.BYTECODE_GENERATION_PHASE, 8.106 + CompilationPhase.INSTALL_PHASE 8.107 + ); 8.108 + 8.109 + /** Singleton that describes compilation up to the CodeGenerator, but not actually generating code */ 8.110 + public final static CompilationPhases COMPILE_UPTO_BYTECODE = new CompilationPhases( 8.111 + "Compile upto bytecode", 8.112 + COMPILE_UPTO_SERIALIZABLE, 8.113 + COMPILE_SERIALIZABLE_UPTO_BYTECODE); 8.114 + 8.115 + /** Singleton that describes a standard eager compilation, but no installation, for example used by --compile-only */ 8.116 + public final static CompilationPhases COMPILE_ALL_NO_INSTALL = new CompilationPhases( 8.117 + "Compile without install", 8.118 + COMPILE_UPTO_BYTECODE, 8.119 + CompilationPhase.BYTECODE_GENERATION_PHASE); 8.120 + 8.121 + /** Singleton that describes a standard eager compilation - this includes code installation */ 8.122 + public final static CompilationPhases COMPILE_ALL = new CompilationPhases( 8.123 + "Full eager compilation", 8.124 + COMPILE_UPTO_BYTECODE, 8.125 + GENERATE_BYTECODE_AND_INSTALL); 8.126 + 8.127 + /** Singleton that describes a full compilation - this includes code installation - from serialized state*/ 8.128 + public final static CompilationPhases COMPILE_ALL_SERIALIZED = new CompilationPhases( 8.129 + "Eager compilation from serializaed state", 8.130 + RECOMPILE_SERIALIZED_UPTO_BYTECODE, 8.131 + GENERATE_BYTECODE_AND_INSTALL); 8.132 8.133 /** 8.134 * Singleton that describes restOf method generation, given that we have generated the normal 8.135 * method up to CodeGenerator as in {@link CompilationPhases#COMPILE_UPTO_BYTECODE} 8.136 */ 8.137 - public final static CompilationPhases COMPILE_FROM_BYTECODE_RESTOF = 8.138 - COMPILE_FROM_BYTECODE. 8.139 - addFirst(CompilationPhase.REUSE_COMPILE_UNITS_PHASE). 8.140 - setDescription("Generate bytecode and install - RestOf method"); 8.141 + public final static CompilationPhases GENERATE_BYTECODE_AND_INSTALL_RESTOF = new CompilationPhases( 8.142 + "Generate bytecode and install - RestOf method", 8.143 + CompilationPhase.REUSE_COMPILE_UNITS_PHASE, 8.144 + GENERATE_BYTECODE_AND_INSTALL); 8.145 + 8.146 + /** Compile all for a rest of method */ 8.147 + public final static CompilationPhases COMPILE_ALL_RESTOF = new CompilationPhases( 8.148 + "Compile all, rest of", 8.149 + COMPILE_UPTO_BYTECODE, 8.150 + GENERATE_BYTECODE_AND_INSTALL_RESTOF); 8.151 + 8.152 + /** Compile from serialized for a rest of method */ 8.153 + public final static CompilationPhases COMPILE_SERIALIZED_RESTOF = new CompilationPhases( 8.154 + "Compile serialized, rest of", 8.155 + RECOMPILE_SERIALIZED_UPTO_BYTECODE, 8.156 + GENERATE_BYTECODE_AND_INSTALL_RESTOF); 8.157 8.158 private final List<CompilationPhase> phases; 8.159 8.160 private final String desc; 8.161 8.162 private CompilationPhases(final String desc, final CompilationPhase... phases) { 8.163 + this(desc, Arrays.asList(phases)); 8.164 + } 8.165 + 8.166 + private CompilationPhases(final String desc, final CompilationPhases base, final CompilationPhase... phases) { 8.167 + this(desc, concat(base.phases, Arrays.asList(phases))); 8.168 + } 8.169 + 8.170 + private CompilationPhases(final String desc, final CompilationPhase first, final CompilationPhases rest) { 8.171 + this(desc, concat(Collections.singletonList(first), rest.phases)); 8.172 + } 8.173 + 8.174 + private CompilationPhases(final String desc, final CompilationPhases base) { 8.175 + this(desc, base.phases); 8.176 + } 8.177 + 8.178 + private CompilationPhases(final String desc, final CompilationPhases... bases) { 8.179 + this(desc, concatPhases(bases)); 8.180 + } 8.181 + 8.182 + private CompilationPhases(final String desc, final List<CompilationPhase> phases) { 8.183 this.desc = desc; 8.184 + this.phases = phases; 8.185 + } 8.186 8.187 - final List<CompilationPhase> newPhases = new LinkedList<>(); 8.188 - newPhases.addAll(Arrays.asList(phases)); 8.189 - this.phases = Collections.unmodifiableList(newPhases); 8.190 + private static List<CompilationPhase> concatPhases(final CompilationPhases[] bases) { 8.191 + final ArrayList<CompilationPhase> l = new ArrayList<>(); 8.192 + for(final CompilationPhases base: bases) { 8.193 + l.addAll(base.phases); 8.194 + } 8.195 + l.trimToSize(); 8.196 + return l; 8.197 + } 8.198 + 8.199 + private static <T> List<T> concat(final List<T> l1, final List<T> l2) { 8.200 + final ArrayList<T> l = new ArrayList<>(l1); 8.201 + l.addAll(l2); 8.202 + l.trimToSize(); 8.203 + return l; 8.204 } 8.205 8.206 @Override 8.207 @@ -235,45 +303,6 @@ 8.208 return "'" + desc + "' " + phases.toString(); 8.209 } 8.210 8.211 - private CompilationPhases setDescription(final String desc) { 8.212 - return new CompilationPhases(desc, phases.toArray(new CompilationPhase[phases.size()])); 8.213 - } 8.214 - 8.215 - private CompilationPhases removeLast() { 8.216 - final LinkedList<CompilationPhase> list = new LinkedList<>(phases); 8.217 - list.removeLast(); 8.218 - return new CompilationPhases(desc, list.toArray(new CompilationPhase[list.size()])); 8.219 - } 8.220 - 8.221 - private CompilationPhases addFirst(final CompilationPhase phase) { 8.222 - if (phases.contains(phase)) { 8.223 - return this; 8.224 - } 8.225 - final LinkedList<CompilationPhase> list = new LinkedList<>(phases); 8.226 - list.addFirst(phase); 8.227 - return new CompilationPhases(desc, list.toArray(new CompilationPhase[list.size()])); 8.228 - } 8.229 - 8.230 - @SuppressWarnings("unused") //TODO I'll use this soon 8.231 - private CompilationPhases replace(final CompilationPhase phase, final CompilationPhase newPhase) { 8.232 - final LinkedList<CompilationPhase> list = new LinkedList<>(); 8.233 - for (final CompilationPhase p : phases) { 8.234 - list.add(p == phase ? newPhase : p); 8.235 - } 8.236 - return new CompilationPhases(desc, list.toArray(new CompilationPhase[list.size()])); 8.237 - } 8.238 - 8.239 - private CompilationPhases addAfter(final CompilationPhase phase, final CompilationPhase newPhase) { 8.240 - final LinkedList<CompilationPhase> list = new LinkedList<>(); 8.241 - for (final CompilationPhase p : phases) { 8.242 - list.add(p); 8.243 - if (p == phase) { 8.244 - list.add(newPhase); 8.245 - } 8.246 - } 8.247 - return new CompilationPhases(desc, list.toArray(new CompilationPhase[list.size()])); 8.248 - } 8.249 - 8.250 boolean contains(final CompilationPhase phase) { 8.251 return phases.contains(phase); 8.252 } 8.253 @@ -284,7 +313,7 @@ 8.254 } 8.255 8.256 boolean isRestOfCompilation() { 8.257 - return this == COMPILE_ALL_RESTOF || this == COMPILE_FROM_BYTECODE_RESTOF; 8.258 + return this == COMPILE_ALL_RESTOF || this == GENERATE_BYTECODE_AND_INSTALL_RESTOF || this == COMPILE_SERIALIZED_RESTOF; 8.259 } 8.260 8.261 String getDesc() { 8.262 @@ -749,6 +778,14 @@ 8.263 compileUnits.addAll(newUnits); 8.264 } 8.265 8.266 + void serializeAst(final FunctionNode fn) { 8.267 + serializedAsts.put(fn.getId(), AstSerializer.serialize(fn)); 8.268 + } 8.269 + 8.270 + byte[] removeSerializedAst(final int fnId) { 8.271 + return serializedAsts.remove(fnId); 8.272 + } 8.273 + 8.274 CompileUnit findUnit(final long weight) { 8.275 for (final CompileUnit unit : compileUnits) { 8.276 if (unit.canHold(weight)) { 8.277 @@ -771,7 +808,10 @@ 8.278 } 8.279 8.280 RecompilableScriptFunctionData getScriptFunctionData(final int functionId) { 8.281 - return compiledFunction == null ? null : compiledFunction.getScriptFunctionData(functionId); 8.282 + assert compiledFunction != null; 8.283 + final RecompilableScriptFunctionData fn = compiledFunction.getScriptFunctionData(functionId); 8.284 + assert fn != null : functionId; 8.285 + return fn; 8.286 } 8.287 8.288 boolean isGlobalSymbol(final FunctionNode fn, final String name) {
9.1 --- a/src/jdk/nashorn/internal/codegen/FindScopeDepths.java Fri Oct 17 14:24:26 2014 +0200 9.2 +++ b/src/jdk/nashorn/internal/codegen/FindScopeDepths.java Mon Oct 20 12:06:36 2014 +0200 9.3 @@ -187,7 +187,6 @@ 9.4 9.5 if (compiler.isOnDemandCompilation()) { 9.6 final RecompilableScriptFunctionData data = compiler.getScriptFunctionData(newFunctionNode.getId()); 9.7 - assert data != null : newFunctionNode.getName() + " lacks data"; 9.8 if (data.inDynamicContext()) { 9.9 log.fine("Reviving scriptfunction ", quote(name), " as defined in previous (now lost) dynamic scope."); 9.10 newFunctionNode = newFunctionNode.setInDynamicContext(lc); 9.11 @@ -202,7 +201,7 @@ 9.12 9.13 //create recompilable scriptfunctiondata 9.14 final int fnId = newFunctionNode.getId(); 9.15 - final Map<Integer, RecompilableScriptFunctionData> nestedFunctions = fnIdToNestedFunctions.get(fnId); 9.16 + final Map<Integer, RecompilableScriptFunctionData> nestedFunctions = fnIdToNestedFunctions.remove(fnId); 9.17 9.18 assert nestedFunctions != null; 9.19 // Generate the object class and property map in case this function is ever used as constructor 9.20 @@ -212,8 +211,8 @@ 9.21 new AllocatorDescriptor(newFunctionNode.getThisProperties()), 9.22 nestedFunctions, 9.23 externalSymbolDepths.get(fnId), 9.24 - internalSymbols.get(fnId) 9.25 - ); 9.26 + internalSymbols.get(fnId), 9.27 + compiler.removeSerializedAst(fnId)); 9.28 9.29 if (lc.getOutermostFunction() != newFunctionNode) { 9.30 final FunctionNode parentFn = lc.getParentFunction(newFunctionNode);
10.1 --- a/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java Fri Oct 17 14:24:26 2014 +0200 10.2 +++ b/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java Mon Oct 20 12:06:36 2014 +0200 10.3 @@ -72,7 +72,7 @@ 10.4 import jdk.nashorn.internal.ir.ReturnNode; 10.5 import jdk.nashorn.internal.ir.RuntimeNode; 10.6 import jdk.nashorn.internal.ir.RuntimeNode.Request; 10.7 -import jdk.nashorn.internal.ir.SplitNode; 10.8 +import jdk.nashorn.internal.ir.SplitReturn; 10.9 import jdk.nashorn.internal.ir.Statement; 10.10 import jdk.nashorn.internal.ir.SwitchNode; 10.11 import jdk.nashorn.internal.ir.Symbol; 10.12 @@ -361,10 +361,6 @@ 10.13 // Synthetic return node that we must insert at the end of the function if it's end is reachable. 10.14 private ReturnNode syntheticReturn; 10.15 10.16 - // Topmost current split node (if any) 10.17 - private SplitNode topSplit; 10.18 - private boolean split; 10.19 - 10.20 private boolean alreadyEnteredTopLevelFunction; 10.21 10.22 // LvarType and conversion information gathered during the top-down pass; applied to nodes in the bottom-up pass. 10.23 @@ -477,22 +473,7 @@ 10.24 return false; 10.25 } 10.26 final BreakableNode target = jump.getTarget(lc); 10.27 - return splitAwareJumpToLabel(jump, target, jump.getTargetLabel(target)); 10.28 - } 10.29 - 10.30 - private boolean splitAwareJumpToLabel(final JumpStatement jumpStatement, final BreakableNode target, final Label targetLabel) { 10.31 - final JoinPredecessor jumpOrigin; 10.32 - if(topSplit != null && lc.isExternalTarget(topSplit, target)) { 10.33 - // If the jump target is outside the topmost split node, then we'll create a synthetic jump origin in the 10.34 - // split node. 10.35 - jumpOrigin = new JoinPredecessorExpression(); 10.36 - topSplit.addJump(jumpOrigin, targetLabel); 10.37 - } else { 10.38 - // Otherwise, the original jump statement is the jump origin 10.39 - jumpOrigin = jumpStatement; 10.40 - } 10.41 - 10.42 - jumpToLabel(jumpOrigin, targetLabel, getBreakTargetTypes(target)); 10.43 + jumpToLabel(jump, jump.getTargetLabel(target), getBreakTargetTypes(target)); 10.44 doesNotContinueSequentially(); 10.45 return false; 10.46 } 10.47 @@ -703,18 +684,9 @@ 10.48 } 10.49 10.50 @Override 10.51 - public boolean enterSplitNode(final SplitNode splitNode) { 10.52 - if(!reachable) { 10.53 - return false; 10.54 - } 10.55 - // Need to visit inside of split nodes. While it's true that they don't have local variables, we need to visit 10.56 - // breaks, continues, and returns in them. 10.57 - if(topSplit == null) { 10.58 - topSplit = splitNode; 10.59 - } 10.60 - split = true; 10.61 - setType(getCompilerConstantSymbol(lc.getCurrentFunction(), CompilerConstants.RETURN), LvarType.UNDEFINED); 10.62 - return true; 10.63 + public boolean enterSplitReturn(final SplitReturn splitReturn) { 10.64 + doesNotContinueSequentially(); 10.65 + return false; 10.66 } 10.67 10.68 @Override 10.69 @@ -1116,15 +1088,6 @@ 10.70 if(returnType.isUnknown()) { 10.71 returnType = Type.OBJECT; 10.72 } 10.73 - 10.74 - if(split) { 10.75 - // If the function is split, the ":return" symbol is used and needs a slot. Note we can't mark the return 10.76 - // symbol as used in enterSplitNode, as we don't know the final return type of the function earlier than 10.77 - // here. 10.78 - final Symbol retSymbol = getCompilerConstantSymbol(lc.getCurrentFunction(), CompilerConstants.RETURN); 10.79 - retSymbol.setHasSlotFor(returnType); 10.80 - retSymbol.setNeedsSlot(true); 10.81 - } 10.82 } 10.83 10.84 private void createSyntheticReturn(final Block body) {
11.1 --- a/src/jdk/nashorn/internal/codegen/Lower.java Fri Oct 17 14:24:26 2014 +0200 11.2 +++ b/src/jdk/nashorn/internal/codegen/Lower.java Mon Oct 20 12:06:36 2014 +0200 11.3 @@ -352,8 +352,6 @@ 11.4 private Node spliceFinally(final TryNode tryNode, final List<ThrowNode> rethrows, final Block finallyBody) { 11.5 assert tryNode.getFinallyBody() == null; 11.6 11.7 - final LexicalContext lowerLc = lc; 11.8 - 11.9 final TryNode newTryNode = (TryNode)tryNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { 11.10 final List<Node> insideTry = new ArrayList<>(); 11.11 11.12 @@ -406,7 +404,6 @@ 11.13 //still in the try block, store it in a result value and return it afterwards 11.14 resultNode = new IdentNode(Lower.this.compilerConstant(RETURN)); 11.15 newStatements.add(new ExpressionStatement(returnNode.getLineNumber(), returnNode.getToken(), returnNode.getFinish(), new BinaryNode(Token.recast(returnNode.getToken(), TokenType.ASSIGN), resultNode, expr))); 11.16 - lowerLc.setFlag(lowerLc.getCurrentFunction(), FunctionNode.USES_RETURN_SYMBOL); 11.17 } else { 11.18 resultNode = null; 11.19 }
12.1 --- a/src/jdk/nashorn/internal/codegen/MethodEmitter.java Fri Oct 17 14:24:26 2014 +0200 12.2 +++ b/src/jdk/nashorn/internal/codegen/MethodEmitter.java Mon Oct 20 12:06:36 2014 +0200 12.3 @@ -71,6 +71,7 @@ 12.4 import static jdk.nashorn.internal.codegen.ObjectClassGenerator.PRIMITIVE_FIELD_TYPE; 12.5 import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_OPTIMISTIC; 12.6 import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_PROGRAM_POINT_SHIFT; 12.7 + 12.8 import java.io.PrintStream; 12.9 import java.lang.reflect.Array; 12.10 import java.util.Collection; 12.11 @@ -88,11 +89,9 @@ 12.12 import jdk.nashorn.internal.codegen.types.BitwiseType; 12.13 import jdk.nashorn.internal.codegen.types.NumericType; 12.14 import jdk.nashorn.internal.codegen.types.Type; 12.15 -import jdk.nashorn.internal.ir.BreakableNode; 12.16 import jdk.nashorn.internal.ir.FunctionNode; 12.17 import jdk.nashorn.internal.ir.IdentNode; 12.18 import jdk.nashorn.internal.ir.JoinPredecessor; 12.19 -import jdk.nashorn.internal.ir.LexicalContext; 12.20 import jdk.nashorn.internal.ir.LiteralNode; 12.21 import jdk.nashorn.internal.ir.LocalVariableConversion; 12.22 import jdk.nashorn.internal.ir.RuntimeNode; 12.23 @@ -1664,19 +1663,6 @@ 12.24 } 12.25 12.26 /** 12.27 - * Goto, possibly when splitting is taking place. If 12.28 - * a splitNode exists, we need to handle the case that the 12.29 - * jump target is another method 12.30 - * 12.31 - * @param label destination label 12.32 - * @param targetNode the node to which the destination label belongs (the label is normally a break or continue 12.33 - * label) 12.34 - */ 12.35 - void splitAwareGoto(final LexicalContext lc, final Label label, final BreakableNode targetNode) { 12.36 - _goto(label); 12.37 - } 12.38 - 12.39 - /** 12.40 * Perform a comparison of two number types that are popped from the stack 12.41 * 12.42 * @param isCmpG is this a dcmpg semantic, false if it's a dcmpl semantic
13.1 --- a/src/jdk/nashorn/internal/codegen/OptimisticTypesCalculator.java Fri Oct 17 14:24:26 2014 +0200 13.2 +++ b/src/jdk/nashorn/internal/codegen/OptimisticTypesCalculator.java Mon Oct 20 12:06:36 2014 +0200 13.3 @@ -30,7 +30,6 @@ 13.4 import java.util.ArrayDeque; 13.5 import java.util.BitSet; 13.6 import java.util.Deque; 13.7 -import jdk.nashorn.internal.IntDeque; 13.8 import jdk.nashorn.internal.ir.AccessNode; 13.9 import jdk.nashorn.internal.ir.BinaryNode; 13.10 import jdk.nashorn.internal.ir.CallNode; 13.11 @@ -49,7 +48,6 @@ 13.12 import jdk.nashorn.internal.ir.Node; 13.13 import jdk.nashorn.internal.ir.Optimistic; 13.14 import jdk.nashorn.internal.ir.PropertyNode; 13.15 -import jdk.nashorn.internal.ir.SplitNode; 13.16 import jdk.nashorn.internal.ir.Symbol; 13.17 import jdk.nashorn.internal.ir.TernaryNode; 13.18 import jdk.nashorn.internal.ir.UnaryNode; 13.19 @@ -70,8 +68,6 @@ 13.20 13.21 // Per-function bit set of program points that must never be optimistic. 13.22 final Deque<BitSet> neverOptimistic = new ArrayDeque<>(); 13.23 - // Per-function depth of split nodes 13.24 - final IntDeque splitDepth = new IntDeque(); 13.25 13.26 OptimisticTypesCalculator(final Compiler compiler) { 13.27 super(new LexicalContext()); 13.28 @@ -155,7 +151,6 @@ 13.29 return false; 13.30 } 13.31 neverOptimistic.push(new BitSet()); 13.32 - splitDepth.push(0); 13.33 return true; 13.34 } 13.35 13.36 @@ -190,19 +185,6 @@ 13.37 } 13.38 13.39 @Override 13.40 - public boolean enterSplitNode(final SplitNode splitNode) { 13.41 - splitDepth.getAndIncrement(); 13.42 - return true; 13.43 - } 13.44 - 13.45 - @Override 13.46 - public Node leaveSplitNode(final SplitNode splitNode) { 13.47 - final int depth = splitDepth.decrementAndGet(); 13.48 - assert depth >= 0; 13.49 - return splitNode; 13.50 - } 13.51 - 13.52 - @Override 13.53 public boolean enterVarNode(final VarNode varNode) { 13.54 tagNeverOptimistic(varNode.getName()); 13.55 return true; 13.56 @@ -226,16 +208,11 @@ 13.57 @Override 13.58 public Node leaveFunctionNode(final FunctionNode functionNode) { 13.59 neverOptimistic.pop(); 13.60 - final int lastSplitDepth = splitDepth.pop(); 13.61 - assert lastSplitDepth == 0; 13.62 return functionNode.setState(lc, CompilationState.OPTIMISTIC_TYPES_ASSIGNED); 13.63 } 13.64 13.65 @Override 13.66 public Node leaveIdentNode(final IdentNode identNode) { 13.67 - if(inSplitNode()) { 13.68 - return identNode; 13.69 - } 13.70 final Symbol symbol = identNode.getSymbol(); 13.71 if(symbol == null) { 13.72 assert identNode.isPropertyName(); 13.73 @@ -256,7 +233,7 @@ 13.74 13.75 private Expression leaveOptimistic(final Optimistic opt) { 13.76 final int pp = opt.getProgramPoint(); 13.77 - if(isValid(pp) && !inSplitNode() && !neverOptimistic.peek().get(pp)) { 13.78 + if(isValid(pp) && !neverOptimistic.peek().get(pp)) { 13.79 return (Expression)opt.setType(compiler.getOptimisticType(opt)); 13.80 } 13.81 return (Expression)opt; 13.82 @@ -277,8 +254,4 @@ 13.83 tagNeverOptimistic(test.getExpression()); 13.84 } 13.85 } 13.86 - 13.87 - private boolean inSplitNode() { 13.88 - return splitDepth.peek() > 0; 13.89 - } 13.90 }
14.1 --- a/src/jdk/nashorn/internal/codegen/ProgramPoints.java Fri Oct 17 14:24:26 2014 +0200 14.2 +++ b/src/jdk/nashorn/internal/codegen/ProgramPoints.java Mon Oct 20 12:06:36 2014 +0200 14.3 @@ -85,7 +85,7 @@ 14.4 14.5 @Override 14.6 public boolean enterVarNode(final VarNode varNode) { 14.7 - noProgramPoint.add(varNode.getAssignmentDest()); 14.8 + noProgramPoint.add(varNode.getName()); 14.9 return true; 14.10 } 14.11
15.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 15.2 +++ b/src/jdk/nashorn/internal/codegen/ReplaceCompileUnits.java Mon Oct 20 12:06:36 2014 +0200 15.3 @@ -0,0 +1,85 @@ 15.4 +/* 15.5 + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. 15.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 15.7 + * 15.8 + * This code is free software; you can redistribute it and/or modify it 15.9 + * under the terms of the GNU General Public License version 2 only, as 15.10 + * published by the Free Software Foundation. Oracle designates this 15.11 + * particular file as subject to the "Classpath" exception as provided 15.12 + * by Oracle in the LICENSE file that accompanied this code. 15.13 + * 15.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 15.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15.17 + * version 2 for more details (a copy is included in the LICENSE file that 15.18 + * accompanied this code). 15.19 + * 15.20 + * You should have received a copy of the GNU General Public License version 15.21 + * 2 along with this work; if not, write to the Free Software Foundation, 15.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 15.23 + * 15.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 15.25 + * or visit www.oracle.com if you need additional information or have any 15.26 + * questions. 15.27 + */ 15.28 + 15.29 +package jdk.nashorn.internal.codegen; 15.30 + 15.31 +import java.util.ArrayList; 15.32 +import java.util.List; 15.33 +import jdk.nashorn.internal.ir.CompileUnitHolder; 15.34 +import jdk.nashorn.internal.ir.FunctionNode; 15.35 +import jdk.nashorn.internal.ir.FunctionNode.CompilationState; 15.36 +import jdk.nashorn.internal.ir.LexicalContext; 15.37 +import jdk.nashorn.internal.ir.LiteralNode; 15.38 +import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; 15.39 +import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit; 15.40 +import jdk.nashorn.internal.ir.Node; 15.41 +import jdk.nashorn.internal.ir.visitor.NodeVisitor; 15.42 + 15.43 +/** 15.44 + * Base class for a node visitor that replaces {@link CompileUnit}s in {@link CompileUnitHolder}s. 15.45 + */ 15.46 +abstract class ReplaceCompileUnits extends NodeVisitor<LexicalContext> { 15.47 + ReplaceCompileUnits() { 15.48 + super(new LexicalContext()); 15.49 + } 15.50 + 15.51 + /** 15.52 + * Override to provide a replacement for an old compile unit. 15.53 + * @param oldUnit the old compile unit to replace 15.54 + * @return the compile unit's replacement. 15.55 + */ 15.56 + abstract CompileUnit getReplacement(final CompileUnit oldUnit); 15.57 + 15.58 + CompileUnit getExistingReplacement(final CompileUnitHolder node) { 15.59 + final CompileUnit oldUnit = node.getCompileUnit(); 15.60 + assert oldUnit != null; 15.61 + 15.62 + final CompileUnit newUnit = getReplacement(oldUnit); 15.63 + assert newUnit != null; 15.64 + 15.65 + return newUnit; 15.66 + } 15.67 + 15.68 + @Override 15.69 + public Node leaveFunctionNode(final FunctionNode node) { 15.70 + return node.setCompileUnit(lc, getExistingReplacement(node)).setState(lc, CompilationState.COMPILE_UNITS_REUSED); 15.71 + } 15.72 + 15.73 + @Override 15.74 + public Node leaveLiteralNode(final LiteralNode<?> node) { 15.75 + if (node instanceof ArrayLiteralNode) { 15.76 + final ArrayLiteralNode aln = (ArrayLiteralNode)node; 15.77 + if (aln.getUnits() == null) { 15.78 + return node; 15.79 + } 15.80 + final List<ArrayUnit> newArrayUnits = new ArrayList<>(); 15.81 + for (final ArrayUnit au : aln.getUnits()) { 15.82 + newArrayUnits.add(new ArrayUnit(getExistingReplacement(au), au.getLo(), au.getHi())); 15.83 + } 15.84 + return aln.setUnits(lc, newArrayUnits); 15.85 + } 15.86 + return node; 15.87 + } 15.88 +}
16.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 16.2 +++ b/src/jdk/nashorn/internal/codegen/SplitIntoFunctions.java Mon Oct 20 12:06:36 2014 +0200 16.3 @@ -0,0 +1,446 @@ 16.4 +/* 16.5 + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. 16.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 16.7 + * 16.8 + * This code is free software; you can redistribute it and/or modify it 16.9 + * under the terms of the GNU General Public License version 2 only, as 16.10 + * published by the Free Software Foundation. Oracle designates this 16.11 + * particular file as subject to the "Classpath" exception as provided 16.12 + * by Oracle in the LICENSE file that accompanied this code. 16.13 + * 16.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 16.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 16.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16.17 + * version 2 for more details (a copy is included in the LICENSE file that 16.18 + * accompanied this code). 16.19 + * 16.20 + * You should have received a copy of the GNU General Public License version 16.21 + * 2 along with this work; if not, write to the Free Software Foundation, 16.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 16.23 + * 16.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 16.25 + * or visit www.oracle.com if you need additional information or have any 16.26 + * questions. 16.27 + */ 16.28 + 16.29 +package jdk.nashorn.internal.codegen; 16.30 + 16.31 +import static jdk.nashorn.internal.ir.Node.NO_FINISH; 16.32 +import static jdk.nashorn.internal.ir.Node.NO_LINE_NUMBER; 16.33 +import static jdk.nashorn.internal.ir.Node.NO_TOKEN; 16.34 + 16.35 +import java.util.ArrayDeque; 16.36 +import java.util.ArrayList; 16.37 +import java.util.Arrays; 16.38 +import java.util.Collections; 16.39 +import java.util.Deque; 16.40 +import java.util.List; 16.41 +import java.util.Objects; 16.42 +import jdk.nashorn.internal.ir.AccessNode; 16.43 +import jdk.nashorn.internal.ir.BinaryNode; 16.44 +import jdk.nashorn.internal.ir.Block; 16.45 +import jdk.nashorn.internal.ir.BlockLexicalContext; 16.46 +import jdk.nashorn.internal.ir.BreakNode; 16.47 +import jdk.nashorn.internal.ir.CallNode; 16.48 +import jdk.nashorn.internal.ir.CaseNode; 16.49 +import jdk.nashorn.internal.ir.ContinueNode; 16.50 +import jdk.nashorn.internal.ir.Expression; 16.51 +import jdk.nashorn.internal.ir.ExpressionStatement; 16.52 +import jdk.nashorn.internal.ir.FunctionNode; 16.53 +import jdk.nashorn.internal.ir.GetSplitState; 16.54 +import jdk.nashorn.internal.ir.IdentNode; 16.55 +import jdk.nashorn.internal.ir.IfNode; 16.56 +import jdk.nashorn.internal.ir.JumpStatement; 16.57 +import jdk.nashorn.internal.ir.LiteralNode; 16.58 +import jdk.nashorn.internal.ir.Node; 16.59 +import jdk.nashorn.internal.ir.ReturnNode; 16.60 +import jdk.nashorn.internal.ir.SetSplitState; 16.61 +import jdk.nashorn.internal.ir.SplitNode; 16.62 +import jdk.nashorn.internal.ir.SplitReturn; 16.63 +import jdk.nashorn.internal.ir.Statement; 16.64 +import jdk.nashorn.internal.ir.SwitchNode; 16.65 +import jdk.nashorn.internal.ir.VarNode; 16.66 +import jdk.nashorn.internal.ir.visitor.NodeVisitor; 16.67 +import jdk.nashorn.internal.parser.Token; 16.68 +import jdk.nashorn.internal.parser.TokenType; 16.69 + 16.70 +/** 16.71 + * A node visitor that replaces {@link SplitNode}s with anonymous function invocations and some additional constructs 16.72 + * to support control flow across splits. By using this transformation, split functions are translated into ordinary 16.73 + * JavaScript functions with nested anonymous functions. The transformations however introduce several AST nodes that 16.74 + * have no JavaScript source representations ({@link GetSplitState}, {@link SetSplitState}, and {@link SplitReturn}), 16.75 + * and therefore such function is no longer reparseable from its source. For that reason, split functions and their 16.76 + * fragments are serialized in-memory and deserialized when they need to be recompiled either for deoptimization or 16.77 + * for type specialization. 16.78 + * NOTE: all {@code leave*()} methods for statements are returning their input nodes. That way, they will not mutate 16.79 + * the original statement list in the block containing the statement, which is fine, as it'll be replaced by the 16.80 + * lexical context when the block is left. If we returned something else (e.g. null), we'd cause a mutation in the 16.81 + * enclosing block's statement list that is otherwise overwritten later anyway. 16.82 + */ 16.83 +final class SplitIntoFunctions extends NodeVisitor<BlockLexicalContext> { 16.84 + private static final int FALLTHROUGH_STATE = -1; 16.85 + private static final int RETURN_STATE = 0; 16.86 + private static final int BREAK_STATE = 1; 16.87 + private static final int FIRST_JUMP_STATE = 2; 16.88 + 16.89 + private static final String THIS_NAME = CompilerConstants.THIS.symbolName(); 16.90 + private static final String RETURN_NAME = CompilerConstants.RETURN.symbolName(); 16.91 + // Used as the name of the formal parameter for passing the current value of :return symbol into a split fragment. 16.92 + private static final String RETURN_PARAM_NAME = RETURN_NAME + "-in"; 16.93 + 16.94 + private final Deque<FunctionState> functionStates = new ArrayDeque<>(); 16.95 + private final Deque<SplitState> splitStates = new ArrayDeque<>(); 16.96 + private final Namespace namespace; 16.97 + 16.98 + private boolean artificialBlock = false; 16.99 + 16.100 + // -1 is program; we need to use negative ones 16.101 + private int nextFunctionId = -2; 16.102 + 16.103 + public SplitIntoFunctions(final Compiler compiler) { 16.104 + super(new BlockLexicalContext() { 16.105 + @Override 16.106 + protected Block afterSetStatements(Block block) { 16.107 + for(Statement stmt: block.getStatements()) { 16.108 + assert !(stmt instanceof SplitNode); 16.109 + } 16.110 + return block; 16.111 + } 16.112 + }); 16.113 + namespace = new Namespace(compiler.getScriptEnvironment().getNamespace()); 16.114 + } 16.115 + 16.116 + @Override 16.117 + public boolean enterFunctionNode(final FunctionNode functionNode) { 16.118 + functionStates.push(new FunctionState(functionNode)); 16.119 + return true; 16.120 + } 16.121 + 16.122 + @Override 16.123 + public Node leaveFunctionNode(final FunctionNode functionNode) { 16.124 + functionStates.pop(); 16.125 + return functionNode; 16.126 + } 16.127 + 16.128 + @Override 16.129 + protected Node leaveDefault(final Node node) { 16.130 + if (node instanceof Statement) { 16.131 + appendStatement((Statement)node); 16.132 + } 16.133 + return node; 16.134 + } 16.135 + 16.136 + @Override 16.137 + public boolean enterSplitNode(final SplitNode splitNode) { 16.138 + getCurrentFunctionState().splitDepth++; 16.139 + splitStates.push(new SplitState(splitNode)); 16.140 + return true; 16.141 + } 16.142 + 16.143 + @Override 16.144 + public Node leaveSplitNode(final SplitNode splitNode) { 16.145 + // Replace the split node with an anonymous function expression call. 16.146 + 16.147 + final FunctionState fnState = getCurrentFunctionState(); 16.148 + 16.149 + final String name = splitNode.getName(); 16.150 + Block body = splitNode.getBody(); 16.151 + final int firstLineNumber = body.getFirstStatementLineNumber(); 16.152 + final long token = body.getToken(); 16.153 + final int finish = body.getFinish(); 16.154 + 16.155 + final FunctionNode originalFn = fnState.fn; 16.156 + assert originalFn == lc.getCurrentFunction(); 16.157 + final boolean isProgram = originalFn.isProgram(); 16.158 + 16.159 + // Change SplitNode({...}) into "function () { ... }", or "function (:return-in) () { ... }" (for program) 16.160 + final long newFnToken = Token.toDesc(TokenType.FUNCTION, nextFunctionId--, 0); 16.161 + final FunctionNode fn = new FunctionNode( 16.162 + originalFn.getSource(), 16.163 + body.getFirstStatementLineNumber(), 16.164 + newFnToken, 16.165 + finish, 16.166 + NO_TOKEN, 16.167 + namespace, 16.168 + createIdent(name), 16.169 + originalFn.getName() + "$" + name, 16.170 + isProgram ? Collections.singletonList(createReturnParamIdent()) : Collections.<IdentNode>emptyList(), 16.171 + FunctionNode.Kind.NORMAL, 16.172 + // We only need IS_SPLIT conservatively, in case it contains any array units so that we force 16.173 + // the :callee's existence, to force :scope to never be in a slot lower than 2. This is actually 16.174 + // quite a horrible hack to do with CodeGenerator.fixScopeSlot not trampling other parameters 16.175 + // and should go away once we no longer have array unit handling in codegen. Note however that 16.176 + // we still use IS_SPLIT as the criteria in CompilationPhase.SERIALIZE_SPLIT_PHASE. 16.177 + FunctionNode.IS_ANONYMOUS | FunctionNode.USES_ANCESTOR_SCOPE | FunctionNode.IS_SPLIT 16.178 + ) 16.179 + .setBody(lc, body) 16.180 + .setCompileUnit(lc, splitNode.getCompileUnit()) 16.181 + .copyCompilationState(lc, originalFn); 16.182 + 16.183 + // Call the function: 16.184 + // either "(function () { ... }).call(this)" 16.185 + // or "(function (:return-in) { ... }).call(this, :return)" 16.186 + // NOTE: Function.call() has optimized linking that basically does a pass-through to the function being invoked. 16.187 + // NOTE: CompilationPhase.PROGRAM_POINT_PHASE happens after this, so these calls are subject to optimistic 16.188 + // assumptions on their return value (when they return a value), as they should be. 16.189 + final IdentNode thisIdent = createIdent(THIS_NAME); 16.190 + final CallNode callNode = new CallNode(firstLineNumber, token, finish, new AccessNode(NO_TOKEN, NO_FINISH, fn, "call"), 16.191 + isProgram ? Arrays.<Expression>asList(thisIdent, createReturnIdent()) 16.192 + : Collections.<Expression>singletonList(thisIdent), 16.193 + false); 16.194 + 16.195 + final SplitState splitState = splitStates.pop(); 16.196 + fnState.splitDepth--; 16.197 + 16.198 + final Expression callWithReturn; 16.199 + final boolean hasReturn = splitState.hasReturn; 16.200 + if (hasReturn && fnState.splitDepth > 0) { 16.201 + final SplitState parentSplit = splitStates.peek(); 16.202 + if (parentSplit != null) { 16.203 + // Propagate hasReturn to parent split 16.204 + parentSplit.hasReturn = true; 16.205 + } 16.206 + } 16.207 + if (hasReturn || isProgram) { 16.208 + // capture return value: ":return = (function () { ... })();" 16.209 + callWithReturn = new BinaryNode(Token.recast(token, TokenType.ASSIGN), createReturnIdent(), callNode); 16.210 + } else { 16.211 + // no return value, just call : "(function () { ... })();" 16.212 + callWithReturn = callNode; 16.213 + } 16.214 + appendStatement(new ExpressionStatement(firstLineNumber, token, finish, callWithReturn)); 16.215 + 16.216 + Statement splitStateHandler; 16.217 + 16.218 + final List<JumpStatement> jumpStatements = splitState.jumpStatements; 16.219 + final int jumpCount = jumpStatements.size(); 16.220 + // There are jumps (breaks or continues) that need to be propagated outside the split node. We need to 16.221 + // set up a switch statement for them: 16.222 + // switch(:scope.getScopeState()) { ... } 16.223 + if (jumpCount > 0) { 16.224 + final List<CaseNode> cases = new ArrayList<>(jumpCount + (hasReturn ? 1 : 0)); 16.225 + if (hasReturn) { 16.226 + // If the split node also contained a return, we'll slip it as a case in the switch statement 16.227 + addCase(cases, RETURN_STATE, createReturnFromSplit()); 16.228 + } 16.229 + int i = FIRST_JUMP_STATE; 16.230 + for (final JumpStatement jump: jumpStatements) { 16.231 + addCase(cases, i++, enblockAndVisit(jump)); 16.232 + } 16.233 + splitStateHandler = new SwitchNode(NO_LINE_NUMBER, token, finish, GetSplitState.INSTANCE, cases, null); 16.234 + } else { 16.235 + splitStateHandler = null; 16.236 + } 16.237 + 16.238 + // As the switch statement itself is breakable, an unlabelled break can't be in the switch statement, 16.239 + // so we need to test for it separately. 16.240 + if (splitState.hasBreak) { 16.241 + // if(:scope.getScopeState() == Scope.BREAK) { break; } 16.242 + splitStateHandler = makeIfStateEquals(firstLineNumber, token, finish, BREAK_STATE, 16.243 + enblockAndVisit(new BreakNode(NO_LINE_NUMBER, token, finish, null)), splitStateHandler); 16.244 + } 16.245 + 16.246 + // Finally, if the split node had a return statement, but there were no external jumps, we didn't have 16.247 + // the switch statement to handle the return, so we need a separate if for it. 16.248 + if (hasReturn && jumpCount == 0) { 16.249 + // if (:scope.getScopeState() == Scope.RETURN) { return :return; } 16.250 + splitStateHandler = makeIfStateEquals(NO_LINE_NUMBER, token, finish, RETURN_STATE, 16.251 + createReturnFromSplit(), splitStateHandler); 16.252 + } 16.253 + 16.254 + if (splitStateHandler != null) { 16.255 + appendStatement(splitStateHandler); 16.256 + } 16.257 + 16.258 + return splitNode; 16.259 + } 16.260 + 16.261 + private static void addCase(final List<CaseNode> cases, final int i, final Block body) { 16.262 + cases.add(new CaseNode(NO_TOKEN, NO_FINISH, intLiteral(i), body)); 16.263 + } 16.264 + 16.265 + private static LiteralNode<Number> intLiteral(final int i) { 16.266 + return LiteralNode.newInstance(NO_TOKEN, NO_FINISH, i); 16.267 + } 16.268 + 16.269 + private static Block createReturnFromSplit() { 16.270 + return new Block(NO_TOKEN, NO_FINISH, createReturnReturn()); 16.271 + } 16.272 + 16.273 + private static ReturnNode createReturnReturn() { 16.274 + return new ReturnNode(NO_LINE_NUMBER, NO_TOKEN, NO_FINISH, createReturnIdent()); 16.275 + } 16.276 + 16.277 + private static IdentNode createReturnIdent() { 16.278 + return createIdent(RETURN_NAME); 16.279 + } 16.280 + 16.281 + private static IdentNode createReturnParamIdent() { 16.282 + return createIdent(RETURN_PARAM_NAME); 16.283 + } 16.284 + 16.285 + private static IdentNode createIdent(final String name) { 16.286 + return new IdentNode(NO_TOKEN, NO_FINISH, name); 16.287 + } 16.288 + 16.289 + private Block enblockAndVisit(final JumpStatement jump) { 16.290 + artificialBlock = true; 16.291 + final Block block = (Block)new Block(NO_TOKEN, NO_FINISH, jump).accept(this); 16.292 + artificialBlock = false; 16.293 + return block; 16.294 + } 16.295 + 16.296 + private static IfNode makeIfStateEquals(final int lineNumber, final long token, final int finish, 16.297 + final int value, final Block pass, final Statement fail) { 16.298 + return new IfNode(lineNumber, token, finish, 16.299 + new BinaryNode(Token.recast(token, TokenType.EQ_STRICT), 16.300 + GetSplitState.INSTANCE, intLiteral(value)), 16.301 + pass, 16.302 + fail == null ? null : new Block(NO_TOKEN, NO_FINISH, fail)); 16.303 + } 16.304 + 16.305 + @Override 16.306 + public boolean enterVarNode(VarNode varNode) { 16.307 + if (!inSplitNode()) { 16.308 + return super.enterVarNode(varNode); 16.309 + } 16.310 + assert !varNode.isBlockScoped(); //TODO: we must handle these too, but we currently don't 16.311 + 16.312 + final Expression init = varNode.getInit(); 16.313 + if (varNode.isAnonymousFunctionDeclaration()) { 16.314 + // We ain't moving anonymous function declarations. 16.315 + return super.enterVarNode(varNode); 16.316 + } 16.317 + 16.318 + // Move a declaration-only var statement to the top of the outermost function. 16.319 + getCurrentFunctionState().varStatements.add(varNode.setInit(null)); 16.320 + // If it had an initializer, replace it with an assignment expression statement. Note that "var" is a 16.321 + // statement, so it doesn't contribute to :return of the programs, therefore we are _not_ adding a 16.322 + // ":return = ..." assignment around the original assignment. 16.323 + if (init != null) { 16.324 + final long token = Token.recast(varNode.getToken(), TokenType.ASSIGN); 16.325 + new ExpressionStatement(varNode.getLineNumber(), token, varNode.getFinish(), 16.326 + new BinaryNode(token, varNode.getName(), varNode.getInit())).accept(this); 16.327 + } 16.328 + 16.329 + return false; 16.330 + } 16.331 + 16.332 + @Override 16.333 + public Node leaveBlock(final Block block) { 16.334 + if (!artificialBlock) { 16.335 + if (lc.isFunctionBody()) { 16.336 + // Prepend declaration-only var statements to the top of the statement list. 16.337 + lc.prependStatements(getCurrentFunctionState().varStatements); 16.338 + } else if (lc.isSplitBody()) { 16.339 + appendSplitReturn(FALLTHROUGH_STATE, NO_LINE_NUMBER); 16.340 + if (getCurrentFunctionState().fn.isProgram()) { 16.341 + // If we're splitting the program, make sure every shard ends with "return :return" and 16.342 + // begins with ":return = :return-in;". 16.343 + lc.prependStatement(new ExpressionStatement(NO_LINE_NUMBER, NO_TOKEN, NO_FINISH, 16.344 + new BinaryNode(Token.toDesc(TokenType.ASSIGN, 0, 0), createReturnIdent(), createReturnParamIdent()))); 16.345 + } 16.346 + } 16.347 + } 16.348 + return block; 16.349 + } 16.350 + 16.351 + @Override 16.352 + public Node leaveBreakNode(final BreakNode breakNode) { 16.353 + return leaveJumpNode(breakNode); 16.354 + } 16.355 + 16.356 + @Override 16.357 + public Node leaveContinueNode(final ContinueNode continueNode) { 16.358 + return leaveJumpNode(continueNode); 16.359 + } 16.360 + 16.361 + private JumpStatement leaveJumpNode(final JumpStatement jump) { 16.362 + if (inSplitNode()) { 16.363 + final SplitState splitState = getCurrentSplitState(); 16.364 + final SplitNode splitNode = splitState.splitNode; 16.365 + if (lc.isExternalTarget(splitNode, jump.getTarget(lc))) { 16.366 + appendSplitReturn(splitState.getSplitStateIndex(jump), jump.getLineNumber()); 16.367 + return jump; 16.368 + } 16.369 + } 16.370 + appendStatement(jump); 16.371 + return jump; 16.372 + } 16.373 + 16.374 + private void appendSplitReturn(final int splitState, final int lineNumber) { 16.375 + appendStatement(new SetSplitState(splitState, lineNumber)); 16.376 + if (getCurrentFunctionState().fn.isProgram()) { 16.377 + // If we're splitting the program, make sure every fragment passes back :return 16.378 + appendStatement(createReturnReturn()); 16.379 + } else { 16.380 + appendStatement(SplitReturn.INSTANCE); 16.381 + } 16.382 + } 16.383 + 16.384 + @Override 16.385 + public Node leaveReturnNode(final ReturnNode returnNode) { 16.386 + if(inSplitNode()) { 16.387 + appendStatement(new SetSplitState(RETURN_STATE, returnNode.getLineNumber())); 16.388 + getCurrentSplitState().hasReturn = true; 16.389 + } 16.390 + appendStatement(returnNode); 16.391 + return returnNode; 16.392 + } 16.393 + 16.394 + private void appendStatement(final Statement statement) { 16.395 + lc.appendStatement(statement); 16.396 + } 16.397 + 16.398 + private boolean inSplitNode() { 16.399 + return getCurrentFunctionState().splitDepth > 0; 16.400 + } 16.401 + 16.402 + private FunctionState getCurrentFunctionState() { 16.403 + return functionStates.peek(); 16.404 + } 16.405 + 16.406 + private SplitState getCurrentSplitState() { 16.407 + return splitStates.peek(); 16.408 + } 16.409 + 16.410 + private static class FunctionState { 16.411 + final FunctionNode fn; 16.412 + final List<Statement> varStatements = new ArrayList<>(); 16.413 + int splitDepth; 16.414 + 16.415 + FunctionState(final FunctionNode fn) { 16.416 + this.fn = fn; 16.417 + } 16.418 + } 16.419 + 16.420 + private static class SplitState { 16.421 + final SplitNode splitNode; 16.422 + boolean hasReturn; 16.423 + boolean hasBreak; 16.424 + 16.425 + final List<JumpStatement> jumpStatements = new ArrayList<>(); 16.426 + 16.427 + int getSplitStateIndex(final JumpStatement jump) { 16.428 + if (jump instanceof BreakNode && jump.getLabelName() == null) { 16.429 + // Unlabelled break is a special case 16.430 + hasBreak = true; 16.431 + return BREAK_STATE; 16.432 + } 16.433 + 16.434 + int i = 0; 16.435 + for(final JumpStatement exJump: jumpStatements) { 16.436 + if (jump.getClass() == exJump.getClass() && Objects.equals(jump.getLabelName(), exJump.getLabelName())) { 16.437 + return i + FIRST_JUMP_STATE; 16.438 + } 16.439 + ++i; 16.440 + } 16.441 + jumpStatements.add(jump); 16.442 + return i + FIRST_JUMP_STATE; 16.443 + } 16.444 + 16.445 + SplitState(final SplitNode splitNode) { 16.446 + this.splitNode = splitNode; 16.447 + } 16.448 + } 16.449 +}
17.1 --- a/src/jdk/nashorn/internal/codegen/SplitMethodEmitter.java Fri Oct 17 14:24:26 2014 +0200 17.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 17.3 @@ -1,102 +0,0 @@ 17.4 -/* 17.5 - * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 17.6 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 17.7 - * 17.8 - * This code is free software; you can redistribute it and/or modify it 17.9 - * under the terms of the GNU General Public License version 2 only, as 17.10 - * published by the Free Software Foundation. Oracle designates this 17.11 - * particular file as subject to the "Classpath" exception as provided 17.12 - * by Oracle in the LICENSE file that accompanied this code. 17.13 - * 17.14 - * This code is distributed in the hope that it will be useful, but WITHOUT 17.15 - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 17.16 - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 17.17 - * version 2 for more details (a copy is included in the LICENSE file that 17.18 - * accompanied this code). 17.19 - * 17.20 - * You should have received a copy of the GNU General Public License version 17.21 - * 2 along with this work; if not, write to the Free Software Foundation, 17.22 - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 17.23 - * 17.24 - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 17.25 - * or visit www.oracle.com if you need additional information or have any 17.26 - * questions. 17.27 - */ 17.28 - 17.29 -package jdk.nashorn.internal.codegen; 17.30 - 17.31 -import java.util.ArrayList; 17.32 -import java.util.List; 17.33 -import jdk.internal.org.objectweb.asm.MethodVisitor; 17.34 -import jdk.nashorn.internal.codegen.types.Type; 17.35 -import jdk.nashorn.internal.ir.BreakableNode; 17.36 -import jdk.nashorn.internal.ir.LexicalContext; 17.37 -import jdk.nashorn.internal.ir.SplitNode; 17.38 - 17.39 -/** 17.40 - * Emitter used for splitting methods. Needs to keep track of if there are jump targets 17.41 - * outside the current split node. All external jump targets encountered at method 17.42 - * emission are logged, and {@code CodeGenerator#leaveSplitNode(SplitNode)} creates 17.43 - * an appropriate jump table when the SplitNode has been iterated through 17.44 - */ 17.45 -public class SplitMethodEmitter extends MethodEmitter { 17.46 - 17.47 - private final SplitNode splitNode; 17.48 - 17.49 - private final List<Label> externalTargets = new ArrayList<>(); 17.50 - /** 17.51 - * In addition to external target labels, we need to track the target breakables too as the code generator needs to 17.52 - * be able to correctly pop the scopes to the target, see {@link CodeGenerator#leaveSplitNode(SplitNode)}. Note that 17.53 - * this is only used within CodeGenerator, which doesn't mutate the AST, so keeping pointers to other nodes is not 17.54 - * incorrect. 17.55 - */ 17.56 - private final List<BreakableNode> externalTargetNodes = new ArrayList<>(); 17.57 - 17.58 - SplitMethodEmitter(final ClassEmitter classEmitter, final MethodVisitor mv, final SplitNode splitNode) { 17.59 - super(classEmitter, mv); 17.60 - this.splitNode = splitNode; 17.61 - } 17.62 - 17.63 - @Override 17.64 - void splitAwareGoto(final LexicalContext lc, final Label label, final BreakableNode targetNode) { 17.65 - assert splitNode != null; 17.66 - final int index = findExternalTarget(lc, label, targetNode); 17.67 - if (index >= 0) { 17.68 - setSplitState(index + 1); // 0 is ordinary return 17.69 - final Type retType = functionNode.getReturnType(); 17.70 - loadUndefined(retType); 17.71 - _return(retType); 17.72 - } else { 17.73 - super.splitAwareGoto(lc, label, targetNode); 17.74 - } 17.75 - } 17.76 - 17.77 - private int findExternalTarget(final LexicalContext lc, final Label label, final BreakableNode targetNode) { 17.78 - final int index = externalTargets.indexOf(label); 17.79 - 17.80 - if (index >= 0) { 17.81 - return index; 17.82 - } 17.83 - 17.84 - if (lc.isExternalTarget(splitNode, targetNode)) { 17.85 - externalTargets.add(label); 17.86 - externalTargetNodes.add(targetNode); 17.87 - return externalTargets.size() - 1; 17.88 - } 17.89 - return -1; 17.90 - } 17.91 - 17.92 - @Override 17.93 - MethodEmitter registerReturn() { 17.94 - setHasReturn(); 17.95 - return setSplitState(0); 17.96 - } 17.97 - 17.98 - final List<Label> getExternalTargets() { 17.99 - return externalTargets; 17.100 - } 17.101 - 17.102 - final List<BreakableNode> getExternalTargetNodes() { 17.103 - return externalTargetNodes; 17.104 - } 17.105 -}
18.1 --- a/src/jdk/nashorn/internal/ir/BlockLexicalContext.java Fri Oct 17 14:24:26 2014 +0200 18.2 +++ b/src/jdk/nashorn/internal/ir/BlockLexicalContext.java Mon Oct 20 12:06:36 2014 +0200 18.3 @@ -109,6 +109,16 @@ 18.4 } 18.5 18.6 /** 18.7 + * Prepend a list of statement to the block being generated 18.8 + * @param statements a list of statements to prepend 18.9 + */ 18.10 + public void prependStatements(final List<Statement> statements) { 18.11 + assert statements != null; 18.12 + sstack.peek().addAll(0, statements); 18.13 + } 18.14 + 18.15 + 18.16 + /** 18.17 * Get the last statement that was emitted into a block 18.18 * @return the last statement emitted 18.19 */
19.1 --- a/src/jdk/nashorn/internal/ir/FunctionNode.java Fri Oct 17 14:24:26 2014 +0200 19.2 +++ b/src/jdk/nashorn/internal/ir/FunctionNode.java Mon Oct 20 12:06:36 2014 +0200 19.3 @@ -109,7 +109,7 @@ 19.4 } 19.5 19.6 /** Source of entity. */ 19.7 - private final Source source; 19.8 + private transient final Source source; 19.9 19.10 /** 19.11 * Opaque object representing parser state at the end of the function. Used when reparsing outer functions 19.12 @@ -143,7 +143,7 @@ 19.13 private final long lastToken; 19.14 19.15 /** Method's namespace. */ 19.16 - private final Namespace namespace; 19.17 + private transient final Namespace namespace; 19.18 19.19 /** Current compilation state */ 19.20 @Ignore 19.21 @@ -209,31 +209,23 @@ 19.22 /** Are we vararg, but do we just pass the arguments along to apply or call */ 19.23 public static final int HAS_APPLY_TO_CALL_SPECIALIZATION = 1 << 12; 19.24 19.25 - /** Does this function explicitly use the {@link CompilerConstants#RETURN} symbol? Some functions are known to 19.26 - * always use the return symbol, namely a function that is a program (as it must track its last executed expression 19.27 - * statement's value) as well as functions that are split (to communicate return values from inner to outer 19.28 - * partitions). Other functions normally don't use the return symbol (so we optimize away its slot), except in some 19.29 - * very special cases, e.g. when containing a return statement in a finally block. These special cases set this 19.30 - * flag. */ 19.31 - public static final int USES_RETURN_SYMBOL = 1 << 13; 19.32 - 19.33 /** 19.34 * Is this function the top-level program? 19.35 */ 19.36 - public static final int IS_PROGRAM = 1 << 14; 19.37 + public static final int IS_PROGRAM = 1 << 13; 19.38 19.39 /** 19.40 * Flag indicating whether this function uses the local variable symbol for itself. Only named function expressions 19.41 * can have this flag set if they reference themselves (e.g. "(function f() { return f })". Declared functions will 19.42 * use the symbol in their parent scope instead when they reference themselves by name. 19.43 */ 19.44 - public static final int USES_SELF_SYMBOL = 1 << 15; 19.45 + public static final int USES_SELF_SYMBOL = 1 << 14; 19.46 19.47 /** Does this function use the "this" keyword? */ 19.48 - public static final int USES_THIS = 1 << 16; 19.49 + public static final int USES_THIS = 1 << 15; 19.50 19.51 /** Is this declared in a dynamic context */ 19.52 - public static final int IN_DYNAMIC_CONTEXT = 1 << 17; 19.53 + public static final int IN_DYNAMIC_CONTEXT = 1 << 16; 19.54 19.55 /** 19.56 * The following flags are derived from directive comments within this function. 19.57 @@ -241,28 +233,28 @@ 19.58 */ 19.59 19.60 /** parser, print parse tree */ 19.61 - public static final int IS_PRINT_PARSE = 1 << 18; 19.62 + public static final int IS_PRINT_PARSE = 1 << 17; 19.63 /** parser, print lower parse tree */ 19.64 - public static final int IS_PRINT_LOWER_PARSE = 1 << 19; 19.65 + public static final int IS_PRINT_LOWER_PARSE = 1 << 18; 19.66 /** parser, print AST */ 19.67 - public static final int IS_PRINT_AST = 1 << 20; 19.68 + public static final int IS_PRINT_AST = 1 << 19; 19.69 /** parser, print lower AST */ 19.70 - public static final int IS_PRINT_LOWER_AST = 1 << 21; 19.71 + public static final int IS_PRINT_LOWER_AST = 1 << 20; 19.72 /** parser, print symbols */ 19.73 - public static final int IS_PRINT_SYMBOLS = 1 << 22; 19.74 + public static final int IS_PRINT_SYMBOLS = 1 << 21; 19.75 19.76 // callsite tracing, profiling within this function 19.77 /** profile callsites in this function? */ 19.78 - public static final int IS_PROFILE = 1 << 23; 19.79 + public static final int IS_PROFILE = 1 << 22; 19.80 19.81 /** trace callsite enterexit in this function? */ 19.82 - public static final int IS_TRACE_ENTEREXIT = 1 << 24; 19.83 + public static final int IS_TRACE_ENTEREXIT = 1 << 23; 19.84 19.85 /** trace callsite misses in this function? */ 19.86 - public static final int IS_TRACE_MISSES = 1 << 25; 19.87 + public static final int IS_TRACE_MISSES = 1 << 24; 19.88 19.89 /** trace callsite values in this function? */ 19.90 - public static final int IS_TRACE_VALUES = 1 << 26; 19.91 + public static final int IS_TRACE_VALUES = 1 << 25; 19.92 19.93 /** 19.94 * Whether this function needs the callee {@link ScriptFunction} instance passed to its code as a 19.95 @@ -270,7 +262,7 @@ 19.96 * Rather, it is always calculated (see {@link #needsCallee()}). {@link RecompilableScriptFunctionData} 19.97 * will, however, cache the value of this flag. 19.98 */ 19.99 - public static final int NEEDS_CALLEE = 1 << 27; 19.100 + public static final int NEEDS_CALLEE = 1 << 26; 19.101 19.102 /** extension callsite flags mask */ 19.103 public static final int EXTENSION_CALLSITE_FLAGS = IS_PRINT_PARSE | 19.104 @@ -287,8 +279,8 @@ 19.105 /** Does this function potentially need "arguments"? Note that this is not a full test, as further negative check of REDEFINES_ARGS is needed. */ 19.106 private static final int MAYBE_NEEDS_ARGUMENTS = USES_ARGUMENTS | HAS_EVAL; 19.107 19.108 - /** Does this function need the parent scope? It needs it if either it or its descendants use variables from it, or have a deep eval. */ 19.109 - private static final int NEEDS_PARENT_SCOPE = USES_ANCESTOR_SCOPE | HAS_DEEP_EVAL; 19.110 + /** Does this function need the parent scope? It needs it if either it or its descendants use variables from it, or have a deep eval, or it's the program. */ 19.111 + public static final int NEEDS_PARENT_SCOPE = USES_ANCESTOR_SCOPE | HAS_DEEP_EVAL | IS_PROGRAM; 19.112 19.113 /** What is the return type of this function? */ 19.114 private Type returnType = Type.UNKNOWN; 19.115 @@ -352,7 +344,8 @@ 19.116 final Block body, 19.117 final List<IdentNode> parameters, 19.118 final int thisProperties, 19.119 - final Class<?> rootClass) { 19.120 + final Class<?> rootClass, 19.121 + final Source source, Namespace namespace) { 19.122 super(functionNode); 19.123 19.124 this.endParserState = endParserState; 19.125 @@ -367,11 +360,11 @@ 19.126 this.parameters = parameters; 19.127 this.thisProperties = thisProperties; 19.128 this.rootClass = rootClass; 19.129 + this.source = source; 19.130 + this.namespace = namespace; 19.131 19.132 // the fields below never change - they are final and assigned in constructor 19.133 - this.source = functionNode.source; 19.134 this.ident = functionNode.ident; 19.135 - this.namespace = functionNode.namespace; 19.136 this.kind = functionNode.kind; 19.137 this.firstToken = functionNode.firstToken; 19.138 } 19.139 @@ -437,6 +430,39 @@ 19.140 } 19.141 19.142 /** 19.143 + * Sets the source and namespace for this function. It can only set a non-null source and namespace for a function 19.144 + * that currently has both a null source and a null namespace. This is used to re-set the source and namespace for 19.145 + * a deserialized function node. 19.146 + * @param source the source for the function. 19.147 + * @param namespace the namespace for the function 19.148 + * @return a new function node with the set source and namespace 19.149 + * @throws IllegalArgumentException if the specified source or namespace is null 19.150 + * @throws IllegalStateException if the function already has either a source or namespace set. 19.151 + */ 19.152 + public FunctionNode initializeDeserialized(final Source source, final Namespace namespace) { 19.153 + if (source == null || namespace == null) { 19.154 + throw new IllegalArgumentException(); 19.155 + } else if (this.source == source && this.namespace == namespace) { 19.156 + return this; 19.157 + } else if (this.source != null || this.namespace != null) { 19.158 + throw new IllegalStateException(); 19.159 + } 19.160 + return new FunctionNode( 19.161 + this, 19.162 + lastToken, 19.163 + endParserState, 19.164 + flags, 19.165 + name, 19.166 + returnType, 19.167 + compileUnit, 19.168 + compilationState, 19.169 + body, 19.170 + parameters, 19.171 + thisProperties, 19.172 + rootClass, source, namespace); 19.173 + } 19.174 + 19.175 + /** 19.176 * Get the unique ID for this function within the script file. 19.177 * @return the id 19.178 */ 19.179 @@ -537,6 +563,28 @@ 19.180 } 19.181 final EnumSet<CompilationState> newState = EnumSet.copyOf(this.compilationState); 19.182 newState.add(state); 19.183 + return setCompilationState(lc, newState); 19.184 + } 19.185 + 19.186 + /** 19.187 + * Copy a compilation state from an original function to this function. Used when creating synthetic 19.188 + * function nodes by the splitter. 19.189 + * 19.190 + * @param lc lexical context 19.191 + * @param original the original function node to copy compilation state from 19.192 + * @return function node or a new one if state was changed 19.193 + */ 19.194 + public FunctionNode copyCompilationState(final LexicalContext lc, final FunctionNode original) { 19.195 + final EnumSet<CompilationState> origState = original.compilationState; 19.196 + if (!AssertsEnabled.assertsEnabled() || this.compilationState.containsAll(origState)) { 19.197 + return this; 19.198 + } 19.199 + final EnumSet<CompilationState> newState = EnumSet.copyOf(this.compilationState); 19.200 + newState.addAll(origState); 19.201 + return setCompilationState(lc, newState); 19.202 + } 19.203 + 19.204 + private FunctionNode setCompilationState(final LexicalContext lc, final EnumSet<CompilationState> compilationState) { 19.205 return Node.replaceInLexicalContext( 19.206 lc, 19.207 this, 19.208 @@ -548,13 +596,14 @@ 19.209 name, 19.210 returnType, 19.211 compileUnit, 19.212 - newState, 19.213 + compilationState, 19.214 body, 19.215 parameters, 19.216 thisProperties, 19.217 - rootClass)); 19.218 + rootClass, source, namespace)); 19.219 } 19.220 19.221 + 19.222 /** 19.223 * Create a unique name in the namespace of this FunctionNode 19.224 * @param base prefix for name 19.225 @@ -624,7 +673,7 @@ 19.226 body, 19.227 parameters, 19.228 thisProperties, 19.229 - rootClass)); 19.230 + rootClass, source, namespace)); 19.231 } 19.232 19.233 @Override 19.234 @@ -699,18 +748,11 @@ 19.235 * @return true if the function's generated Java method needs a {@code callee} parameter. 19.236 */ 19.237 public boolean needsCallee() { 19.238 + // NOTE: we only need isSplit() here to ensure that :scope can never drop below slot 2 for splitting array units. 19.239 return needsParentScope() || usesSelfSymbol() || isSplit() || (needsArguments() && !isStrict()) || hasOptimisticApplyToCall(); 19.240 } 19.241 19.242 /** 19.243 - * Check if this function uses the return symbol 19.244 - * @return true if uses the return symbol 19.245 - */ 19.246 - public boolean usesReturnSymbol() { 19.247 - return isProgram() || isSplit() || getFlag(USES_RETURN_SYMBOL); 19.248 - } 19.249 - 19.250 - /** 19.251 * Return {@code true} if this function makes use of the {@code this} object. 19.252 * 19.253 * @return true if function uses {@code this} object 19.254 @@ -772,7 +814,7 @@ 19.255 body, 19.256 parameters, 19.257 thisProperties, 19.258 - rootClass)); 19.259 + rootClass, source, namespace)); 19.260 } 19.261 19.262 /** 19.263 @@ -840,7 +882,7 @@ 19.264 * @return true if the function needs parent scope. 19.265 */ 19.266 public boolean needsParentScope() { 19.267 - return getFlag(NEEDS_PARENT_SCOPE) || isProgram(); 19.268 + return getFlag(NEEDS_PARENT_SCOPE); 19.269 } 19.270 19.271 /** 19.272 @@ -868,7 +910,7 @@ 19.273 body, 19.274 parameters, 19.275 thisProperties, 19.276 - rootClass)); 19.277 + rootClass, source, namespace)); 19.278 } 19.279 19.280 /** 19.281 @@ -929,7 +971,7 @@ 19.282 body, 19.283 parameters, 19.284 thisProperties, 19.285 - rootClass)); 19.286 + rootClass, source, namespace)); 19.287 } 19.288 19.289 /** 19.290 @@ -964,7 +1006,10 @@ 19.291 compilationState, 19.292 body, 19.293 parameters, 19.294 - thisProperties, rootClass)); 19.295 + thisProperties, 19.296 + rootClass, 19.297 + source, 19.298 + namespace)); 19.299 } 19.300 19.301 /** 19.302 @@ -1000,7 +1045,9 @@ 19.303 body, 19.304 parameters, 19.305 thisProperties, 19.306 - rootClass)); 19.307 + rootClass, 19.308 + source, 19.309 + namespace)); 19.310 } 19.311 19.312 /** 19.313 @@ -1014,9 +1061,9 @@ 19.314 } 19.315 19.316 /** 19.317 - * Checks if this function is a sub-function generated by splitting a larger one 19.318 + * Checks if this function is split into several smaller fragments. 19.319 * 19.320 - * @return true if this function is split from a larger one 19.321 + * @return true if this function is split into several smaller fragments. 19.322 */ 19.323 public boolean isSplit() { 19.324 return getFlag(IS_SPLIT); 19.325 @@ -1066,7 +1113,7 @@ 19.326 body, 19.327 parameters, 19.328 thisProperties, 19.329 - rootClass)); 19.330 + rootClass, source, namespace)); 19.331 } 19.332 19.333 /** 19.334 @@ -1145,7 +1192,7 @@ 19.335 body, 19.336 parameters, 19.337 thisProperties, 19.338 - rootClass 19.339 + rootClass, source, namespace 19.340 )); 19.341 } 19.342 19.343 @@ -1193,7 +1240,7 @@ 19.344 body, 19.345 parameters, 19.346 thisProperties, 19.347 - rootClass)); 19.348 + rootClass, source, namespace)); 19.349 } 19.350 19.351 /** 19.352 @@ -1249,6 +1296,6 @@ 19.353 body, 19.354 parameters, 19.355 thisProperties, 19.356 - rootClass)); 19.357 + rootClass, source, namespace)); 19.358 } 19.359 }
20.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 20.2 +++ b/src/jdk/nashorn/internal/ir/GetSplitState.java Mon Oct 20 12:06:36 2014 +0200 20.3 @@ -0,0 +1,70 @@ 20.4 +/* 20.5 + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. 20.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 20.7 + * 20.8 + * This code is free software; you can redistribute it and/or modify it 20.9 + * under the terms of the GNU General Public License version 2 only, as 20.10 + * published by the Free Software Foundation. Oracle designates this 20.11 + * particular file as subject to the "Classpath" exception as provided 20.12 + * by Oracle in the LICENSE file that accompanied this code. 20.13 + * 20.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 20.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 20.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 20.17 + * version 2 for more details (a copy is included in the LICENSE file that 20.18 + * accompanied this code). 20.19 + * 20.20 + * You should have received a copy of the GNU General Public License version 20.21 + * 2 along with this work; if not, write to the Free Software Foundation, 20.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20.23 + * 20.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20.25 + * or visit www.oracle.com if you need additional information or have any 20.26 + * questions. 20.27 + */ 20.28 + 20.29 +package jdk.nashorn.internal.ir; 20.30 + 20.31 +import java.util.function.Function; 20.32 +import jdk.nashorn.internal.codegen.CompilerConstants; 20.33 +import jdk.nashorn.internal.codegen.types.Type; 20.34 +import jdk.nashorn.internal.ir.visitor.NodeVisitor; 20.35 +import jdk.nashorn.internal.runtime.Scope; 20.36 + 20.37 +/** 20.38 + * Synthetic AST node that represents loading of the scope object and invocation of the {@link Scope#getSplitState()} 20.39 + * method on it. It has no JavaScript source representation and only occurs in synthetic functions created by 20.40 + * the split-into-functions transformation. 20.41 + */ 20.42 +public final class GetSplitState extends Expression { 20.43 + private static final long serialVersionUID = 1L; 20.44 + 20.45 + /** The sole instance of this AST node. */ 20.46 + public final static GetSplitState INSTANCE = new GetSplitState(); 20.47 + 20.48 + private GetSplitState() { 20.49 + super(NO_TOKEN, NO_FINISH); 20.50 + } 20.51 + 20.52 + @Override 20.53 + public Type getType(final Function<Symbol, Type> localVariableTypes) { 20.54 + return Type.INT; 20.55 + } 20.56 + 20.57 + @Override 20.58 + public Node accept(final NodeVisitor<? extends LexicalContext> visitor) { 20.59 + return visitor.enterGetSplitState(this) ? visitor.leaveGetSplitState(this) : this; 20.60 + } 20.61 + 20.62 + @Override 20.63 + public void toString(final StringBuilder sb, final boolean printType) { 20.64 + if (printType) { 20.65 + sb.append("{I}"); 20.66 + } 20.67 + sb.append(CompilerConstants.SCOPE.symbolName()).append('.').append(Scope.GET_SPLIT_STATE.name()).append("()"); 20.68 + } 20.69 + 20.70 + private Object readResolve() { 20.71 + return INSTANCE; 20.72 + } 20.73 +}
21.1 --- a/src/jdk/nashorn/internal/ir/LexicalContext.java Fri Oct 17 14:24:26 2014 +0200 21.2 +++ b/src/jdk/nashorn/internal/ir/LexicalContext.java Mon Oct 20 12:06:36 2014 +0200 21.3 @@ -440,6 +440,14 @@ 21.4 } 21.5 21.6 /** 21.7 + * Is the topmost lexical context element body of a SplitNode? 21.8 + * @return true if it's the body of a split node. 21.9 + */ 21.10 + public boolean isSplitBody() { 21.11 + return sp >= 2 && stack[sp - 1] instanceof Block && stack[sp - 2] instanceof SplitNode; 21.12 + } 21.13 + 21.14 + /** 21.15 * Get the parent function for a function in the lexical context 21.16 * @param functionNode function for which to get parent 21.17 * @return parent function of functionNode or null if none (e.g. if functionNode is the program) 21.18 @@ -472,9 +480,6 @@ 21.19 final LexicalContextNode node = iter.next(); 21.20 if (node == until) { 21.21 break; 21.22 - } else if (node instanceof SplitNode) { 21.23 - // Don't bother popping scopes if we're going to do a return from a split method anyway. 21.24 - return 0; 21.25 } 21.26 assert !(node instanceof FunctionNode); // Can't go outside current function 21.27 if (node instanceof WithNode || node instanceof Block && ((Block)node).needsScope()) {
22.1 --- a/src/jdk/nashorn/internal/ir/LiteralNode.java Fri Oct 17 14:24:26 2014 +0200 22.2 +++ b/src/jdk/nashorn/internal/ir/LiteralNode.java Mon Oct 20 12:06:36 2014 +0200 22.3 @@ -672,13 +672,13 @@ 22.4 private static final class ArrayLiteralInitializer { 22.5 22.6 static ArrayLiteralNode initialize(final ArrayLiteralNode node) { 22.7 - final Type elementType = computeElementType(node.value, node.elementType); 22.8 + final Type elementType = computeElementType(node.value); 22.9 final int[] postsets = computePostsets(node.value); 22.10 final Object presets = computePresets(node.value, elementType, postsets); 22.11 return new ArrayLiteralNode(node, node.value, elementType, postsets, presets, node.units); 22.12 } 22.13 22.14 - private static Type computeElementType(final Expression[] value, final Type elementType) { 22.15 + private static Type computeElementType(final Expression[] value) { 22.16 Type widestElementType = Type.INT; 22.17 22.18 for (final Expression elem : value) {
23.1 --- a/src/jdk/nashorn/internal/ir/Node.java Fri Oct 17 14:24:26 2014 +0200 23.2 +++ b/src/jdk/nashorn/internal/ir/Node.java Mon Oct 20 12:06:36 2014 +0200 23.3 @@ -38,6 +38,15 @@ 23.4 public abstract class Node implements Cloneable, Serializable { 23.5 private static final long serialVersionUID = 1L; 23.6 23.7 + /** Constant used for synthetic AST nodes that have no line number. */ 23.8 + public static final int NO_LINE_NUMBER = -1; 23.9 + 23.10 + /** Constant used for synthetic AST nodes that have no token. */ 23.11 + public static final long NO_TOKEN = 0L; 23.12 + 23.13 + /** Constant used for synthetic AST nodes that have no finish. */ 23.14 + public static final int NO_FINISH = 0; 23.15 + 23.16 /** Start of source range. */ 23.17 protected final int start; 23.18
24.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 24.2 +++ b/src/jdk/nashorn/internal/ir/SetSplitState.java Mon Oct 20 12:06:36 2014 +0200 24.3 @@ -0,0 +1,70 @@ 24.4 +/* 24.5 + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. 24.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 24.7 + * 24.8 + * This code is free software; you can redistribute it and/or modify it 24.9 + * under the terms of the GNU General Public License version 2 only, as 24.10 + * published by the Free Software Foundation. Oracle designates this 24.11 + * particular file as subject to the "Classpath" exception as provided 24.12 + * by Oracle in the LICENSE file that accompanied this code. 24.13 + * 24.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 24.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 24.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 24.17 + * version 2 for more details (a copy is included in the LICENSE file that 24.18 + * accompanied this code). 24.19 + * 24.20 + * You should have received a copy of the GNU General Public License version 24.21 + * 2 along with this work; if not, write to the Free Software Foundation, 24.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 24.23 + * 24.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 24.25 + * or visit www.oracle.com if you need additional information or have any 24.26 + * questions. 24.27 + */ 24.28 + 24.29 +package jdk.nashorn.internal.ir; 24.30 + 24.31 +import jdk.nashorn.internal.codegen.CompilerConstants; 24.32 +import jdk.nashorn.internal.ir.visitor.NodeVisitor; 24.33 +import jdk.nashorn.internal.runtime.Scope; 24.34 + 24.35 +/** 24.36 + * Synthetic AST node that represents loading of the scope object and invocation of the {@link Scope#setSplitState(int)} 24.37 + * method on it. It has no JavaScript source representation and only occurs in synthetic functions created by 24.38 + * the split-into-functions transformation. 24.39 + */ 24.40 +public final class SetSplitState extends Statement { 24.41 + private static final long serialVersionUID = 1L; 24.42 + 24.43 + private final int state; 24.44 + 24.45 + /** 24.46 + * Creates a new split state setter 24.47 + * @param state the state to set 24.48 + * @param lineNumber the line number where it is inserted 24.49 + */ 24.50 + public SetSplitState(final int state, final int lineNumber) { 24.51 + super(lineNumber, NO_TOKEN, NO_FINISH); 24.52 + this.state = state; 24.53 + } 24.54 + 24.55 + /** 24.56 + * Returns the state this setter sets. 24.57 + * @return the state this setter sets. 24.58 + */ 24.59 + public int getState() { 24.60 + return state; 24.61 + } 24.62 + 24.63 + @Override 24.64 + public Node accept(final NodeVisitor<? extends LexicalContext> visitor) { 24.65 + return visitor.enterSetSplitState(this) ? visitor.leaveSetSplitState(this) : this; 24.66 + } 24.67 + 24.68 + @Override 24.69 + public void toString(final StringBuilder sb, final boolean printType) { 24.70 + sb.append(CompilerConstants.SCOPE.symbolName()).append('.').append(Scope.SET_SPLIT_STATE.name()) 24.71 + .append('(').append(state).append(");"); 24.72 + } 24.73 +}
25.1 --- a/src/jdk/nashorn/internal/ir/SplitNode.java Fri Oct 17 14:24:26 2014 +0200 25.2 +++ b/src/jdk/nashorn/internal/ir/SplitNode.java Mon Oct 20 12:06:36 2014 +0200 25.3 @@ -25,13 +25,10 @@ 25.4 25.5 package jdk.nashorn.internal.ir; 25.6 25.7 -import java.util.ArrayList; 25.8 -import java.util.Collections; 25.9 -import java.util.HashMap; 25.10 -import java.util.List; 25.11 -import java.util.Map; 25.12 +import java.io.IOException; 25.13 +import java.io.NotSerializableException; 25.14 +import java.io.ObjectOutputStream; 25.15 import jdk.nashorn.internal.codegen.CompileUnit; 25.16 -import jdk.nashorn.internal.codegen.Label; 25.17 import jdk.nashorn.internal.ir.annotations.Immutable; 25.18 import jdk.nashorn.internal.ir.visitor.NodeVisitor; 25.19 25.20 @@ -39,7 +36,7 @@ 25.21 * Node indicating code is split across classes. 25.22 */ 25.23 @Immutable 25.24 -public class SplitNode extends LexicalContextStatement implements Labels, CompileUnitHolder { 25.25 +public class SplitNode extends LexicalContextStatement implements CompileUnitHolder { 25.26 private static final long serialVersionUID = 1L; 25.27 25.28 /** Split node method name. */ 25.29 @@ -51,8 +48,6 @@ 25.30 /** Body of split code. */ 25.31 private final Block body; 25.32 25.33 - private Map<Label, JoinPredecessor> jumps; 25.34 - 25.35 /** 25.36 * Constructor 25.37 * 25.38 @@ -67,19 +62,18 @@ 25.39 this.compileUnit = compileUnit; 25.40 } 25.41 25.42 - private SplitNode(final SplitNode splitNode, final Block body, final CompileUnit compileUnit, final Map<Label, JoinPredecessor> jumps) { 25.43 + private SplitNode(final SplitNode splitNode, final Block body, final CompileUnit compileUnit) { 25.44 super(splitNode); 25.45 this.name = splitNode.name; 25.46 this.body = body; 25.47 this.compileUnit = compileUnit; 25.48 - this.jumps = jumps; 25.49 } 25.50 25.51 /** 25.52 * Get the body for this split node - i.e. the actual code it encloses 25.53 * @return body for split node 25.54 */ 25.55 - public Node getBody() { 25.56 + public Block getBody() { 25.57 return body; 25.58 } 25.59 25.60 @@ -87,7 +81,7 @@ 25.61 if (this.body == body) { 25.62 return this; 25.63 } 25.64 - return Node.replaceInLexicalContext(lc, this, new SplitNode(this, body, compileUnit, jumps)); 25.65 + return Node.replaceInLexicalContext(lc, this, new SplitNode(this, body, compileUnit)); 25.66 } 25.67 25.68 @Override 25.69 @@ -133,33 +127,12 @@ 25.70 if (this.compileUnit == compileUnit) { 25.71 return this; 25.72 } 25.73 - return Node.replaceInLexicalContext(lc, this, new SplitNode(this, body, compileUnit, jumps)); 25.74 + return Node.replaceInLexicalContext(lc, this, new SplitNode(this, body, compileUnit)); 25.75 } 25.76 25.77 - /** 25.78 - * Adds a jump that crosses this split node's boundary (it originates within the split node, and goes to a target 25.79 - * outside of it). 25.80 - * @param jumpOrigin the join predecessor that's the origin of the jump 25.81 - * @param targetLabel the label that's the target of the jump. 25.82 - */ 25.83 - public void addJump(final JoinPredecessor jumpOrigin, final Label targetLabel) { 25.84 - if (jumps == null) { 25.85 - jumps = new HashMap<>(); 25.86 - } 25.87 - jumps.put(targetLabel, jumpOrigin); 25.88 - } 25.89 - 25.90 - /** 25.91 - * Returns the jump origin within this split node for a target. 25.92 - * @param targetLabel the target for which a jump origin is sought. 25.93 - * @return the jump origin, or null. 25.94 - */ 25.95 - public JoinPredecessor getJumpOrigin(final Label targetLabel) { 25.96 - return jumps == null ? null : jumps.get(targetLabel); 25.97 - } 25.98 - 25.99 - @Override 25.100 - public List<Label> getLabels() { 25.101 - return Collections.unmodifiableList(new ArrayList<>(jumps.keySet())); 25.102 + private void writeObject(final ObjectOutputStream out) throws IOException { 25.103 + // We are only serializing the AST after we run SplitIntoFunctions; no SplitNodes can remain for the 25.104 + // serialization. 25.105 + throw new NotSerializableException(getClass().getName()); 25.106 } 25.107 }
26.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 26.2 +++ b/src/jdk/nashorn/internal/ir/SplitReturn.java Mon Oct 20 12:06:36 2014 +0200 26.3 @@ -0,0 +1,64 @@ 26.4 +/* 26.5 + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. 26.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 26.7 + * 26.8 + * This code is free software; you can redistribute it and/or modify it 26.9 + * under the terms of the GNU General Public License version 2 only, as 26.10 + * published by the Free Software Foundation. Oracle designates this 26.11 + * particular file as subject to the "Classpath" exception as provided 26.12 + * by Oracle in the LICENSE file that accompanied this code. 26.13 + * 26.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 26.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 26.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 26.17 + * version 2 for more details (a copy is included in the LICENSE file that 26.18 + * accompanied this code). 26.19 + * 26.20 + * You should have received a copy of the GNU General Public License version 26.21 + * 2 along with this work; if not, write to the Free Software Foundation, 26.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 26.23 + * 26.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 26.25 + * or visit www.oracle.com if you need additional information or have any 26.26 + * questions. 26.27 + */ 26.28 + 26.29 +package jdk.nashorn.internal.ir; 26.30 + 26.31 +import jdk.nashorn.internal.ir.visitor.NodeVisitor; 26.32 + 26.33 +/** 26.34 + * Synthetic AST node that represents return from a split fragment of a split function for control flow reasons (break 26.35 + * or continue into a target outside the current fragment). It has no JavaScript source representation and only occurs 26.36 + * in synthetic functions created by the split-into-functions transformation. It is different from a return node in 26.37 + * that the return value is irrelevant, and doesn't affect the function's return type calculation. 26.38 + */ 26.39 +public final class SplitReturn extends Statement { 26.40 + private static final long serialVersionUID = 1L; 26.41 + 26.42 + /** The sole instance of this AST node. */ 26.43 + public static final SplitReturn INSTANCE = new SplitReturn(); 26.44 + 26.45 + private SplitReturn() { 26.46 + super(NO_LINE_NUMBER, NO_TOKEN, NO_FINISH); 26.47 + } 26.48 + 26.49 + @Override 26.50 + public boolean isTerminal() { 26.51 + return true; 26.52 + } 26.53 + 26.54 + @Override 26.55 + public Node accept(final NodeVisitor<? extends LexicalContext> visitor) { 26.56 + return visitor.enterSplitReturn(this) ? visitor.leaveSplitReturn(this) : this; 26.57 + } 26.58 + 26.59 + @Override 26.60 + public void toString(StringBuilder sb, boolean printType) { 26.61 + sb.append(":splitreturn;"); 26.62 + } 26.63 + 26.64 + private Object readResolve() { 26.65 + return INSTANCE; 26.66 + } 26.67 +}
27.1 --- a/src/jdk/nashorn/internal/ir/Symbol.java Fri Oct 17 14:24:26 2014 +0200 27.2 +++ b/src/jdk/nashorn/internal/ir/Symbol.java Mon Oct 20 12:06:36 2014 +0200 27.3 @@ -97,7 +97,7 @@ 27.4 private int firstSlot = -1; 27.5 27.6 /** Field number in scope or property; array index in varargs when not using arguments object. */ 27.7 - private int fieldIndex; 27.8 + private int fieldIndex = -1; 27.9 27.10 /** Number of times this symbol is used in code */ 27.11 private int useCount; 27.12 @@ -135,28 +135,15 @@ 27.13 * 27.14 * @param name name of symbol 27.15 * @param flags symbol flags 27.16 - * @param slot bytecode slot for this symbol 27.17 */ 27.18 - protected Symbol(final String name, final int flags, final int slot) { 27.19 + public Symbol(final String name, final int flags) { 27.20 this.name = name; 27.21 this.flags = flags; 27.22 - this.firstSlot = slot; 27.23 - this.fieldIndex = -1; 27.24 if(shouldTrace()) { 27.25 trace("CREATE SYMBOL " + name); 27.26 } 27.27 } 27.28 27.29 - /** 27.30 - * Constructor 27.31 - * 27.32 - * @param name name of symbol 27.33 - * @param flags symbol flags 27.34 - */ 27.35 - public Symbol(final String name, final int flags) { 27.36 - this(name, flags, -1); 27.37 - } 27.38 - 27.39 private static String align(final String string, final int max) { 27.40 final StringBuilder sb = new StringBuilder(); 27.41 sb.append(string.substring(0, Math.min(string.length(), max)));
28.1 --- a/src/jdk/nashorn/internal/ir/visitor/NodeVisitor.java Fri Oct 17 14:24:26 2014 +0200 28.2 +++ b/src/jdk/nashorn/internal/ir/visitor/NodeVisitor.java Mon Oct 20 12:06:36 2014 +0200 28.3 @@ -38,6 +38,7 @@ 28.4 import jdk.nashorn.internal.ir.ExpressionStatement; 28.5 import jdk.nashorn.internal.ir.ForNode; 28.6 import jdk.nashorn.internal.ir.FunctionNode; 28.7 +import jdk.nashorn.internal.ir.GetSplitState; 28.8 import jdk.nashorn.internal.ir.IdentNode; 28.9 import jdk.nashorn.internal.ir.IfNode; 28.10 import jdk.nashorn.internal.ir.IndexNode; 28.11 @@ -50,7 +51,9 @@ 28.12 import jdk.nashorn.internal.ir.PropertyNode; 28.13 import jdk.nashorn.internal.ir.ReturnNode; 28.14 import jdk.nashorn.internal.ir.RuntimeNode; 28.15 +import jdk.nashorn.internal.ir.SetSplitState; 28.16 import jdk.nashorn.internal.ir.SplitNode; 28.17 +import jdk.nashorn.internal.ir.SplitReturn; 28.18 import jdk.nashorn.internal.ir.SwitchNode; 28.19 import jdk.nashorn.internal.ir.TernaryNode; 28.20 import jdk.nashorn.internal.ir.ThrowNode; 28.21 @@ -390,6 +393,26 @@ 28.22 } 28.23 28.24 /** 28.25 + * Callback for entering a {@link GetSplitState}. 28.26 + * 28.27 + * @param getSplitState the get split state expression 28.28 + * @return true if traversal should continue and node children be traversed, false otherwise 28.29 + */ 28.30 + public boolean enterGetSplitState(final GetSplitState getSplitState) { 28.31 + return enterDefault(getSplitState); 28.32 + } 28.33 + 28.34 + /** 28.35 + * Callback for leaving a {@link GetSplitState}. 28.36 + * 28.37 + * @param getSplitState the get split state expression 28.38 + * @return processed node, which will replace the original one, or the original node 28.39 + */ 28.40 + public Node leaveGetSplitState(final GetSplitState getSplitState) { 28.41 + return leaveDefault(getSplitState); 28.42 + } 28.43 + 28.44 + /** 28.45 * Callback for entering an IdentNode 28.46 * 28.47 * @param identNode the node 28.48 @@ -570,6 +593,26 @@ 28.49 } 28.50 28.51 /** 28.52 + * Callback for entering a {@link SetSplitState}. 28.53 + * 28.54 + * @param setSplitState the set split state statement 28.55 + * @return true if traversal should continue and node children be traversed, false otherwise 28.56 + */ 28.57 + public boolean enterSetSplitState(final SetSplitState setSplitState) { 28.58 + return enterDefault(setSplitState); 28.59 + } 28.60 + 28.61 + /** 28.62 + * Callback for leaving a {@link SetSplitState}. 28.63 + * 28.64 + * @param setSplitState the set split state expression 28.65 + * @return processed node, which will replace the original one, or the original node 28.66 + */ 28.67 + public Node leaveSetSplitState(final SetSplitState setSplitState) { 28.68 + return leaveDefault(setSplitState); 28.69 + } 28.70 + 28.71 + /** 28.72 * Callback for entering a SplitNode 28.73 * 28.74 * @param splitNode the node 28.75 @@ -590,6 +633,26 @@ 28.76 } 28.77 28.78 /** 28.79 + * Callback for entering a SplitReturn 28.80 + * 28.81 + * @param splitReturn the node 28.82 + * @return true if traversal should continue and node children be traversed, false otherwise 28.83 + */ 28.84 + public boolean enterSplitReturn(final SplitReturn splitReturn) { 28.85 + return enterDefault(splitReturn); 28.86 + } 28.87 + 28.88 + /** 28.89 + * Callback for leaving a SplitReturn 28.90 + * 28.91 + * @param splitReturn the node 28.92 + * @return processed node, which will replace the original one, or the original node 28.93 + */ 28.94 + public Node leaveSplitReturn(final SplitReturn splitReturn) { 28.95 + return leaveDefault(splitReturn); 28.96 + } 28.97 + 28.98 + /** 28.99 * Callback for entering a SwitchNode 28.100 * 28.101 * @param switchNode the node
29.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 29.2 +++ b/src/jdk/nashorn/internal/runtime/AstDeserializer.java Mon Oct 20 12:06:36 2014 +0200 29.3 @@ -0,0 +1,47 @@ 29.4 +/* 29.5 + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. 29.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 29.7 + * 29.8 + * This code is free software; you can redistribute it and/or modify it 29.9 + * under the terms of the GNU General Public License version 2 only, as 29.10 + * published by the Free Software Foundation. Oracle designates this 29.11 + * particular file as subject to the "Classpath" exception as provided 29.12 + * by Oracle in the LICENSE file that accompanied this code. 29.13 + * 29.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 29.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 29.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 29.17 + * version 2 for more details (a copy is included in the LICENSE file that 29.18 + * accompanied this code). 29.19 + * 29.20 + * You should have received a copy of the GNU General Public License version 29.21 + * 2 along with this work; if not, write to the Free Software Foundation, 29.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 29.23 + * 29.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 29.25 + * or visit www.oracle.com if you need additional information or have any 29.26 + * questions. 29.27 + */ 29.28 +package jdk.nashorn.internal.runtime; 29.29 + 29.30 +import java.io.ByteArrayInputStream; 29.31 +import java.io.IOException; 29.32 +import java.io.ObjectInputStream; 29.33 +import java.util.zip.InflaterInputStream; 29.34 +import jdk.nashorn.internal.ir.FunctionNode; 29.35 + 29.36 +/** 29.37 + * This static utility class performs deserialization of FunctionNode ASTs from a byte array. 29.38 + * The format is a standard Java serialization stream, deflated. 29.39 + */ 29.40 +final class AstDeserializer { 29.41 + static FunctionNode deserialize(final byte[] serializedAst) { 29.42 + try { 29.43 + return (FunctionNode)new ObjectInputStream(new InflaterInputStream(new ByteArrayInputStream( 29.44 + serializedAst))).readObject(); 29.45 + } catch (final ClassNotFoundException | IOException e) { 29.46 + // This is internal, can't happen 29.47 + throw new AssertionError("Unexpected exception deserializing function", e); 29.48 + } 29.49 + } 29.50 +}
30.1 --- a/src/jdk/nashorn/internal/runtime/CompiledFunction.java Fri Oct 17 14:24:26 2014 +0200 30.2 +++ b/src/jdk/nashorn/internal/runtime/CompiledFunction.java Mon Oct 20 12:06:36 2014 +0200 30.3 @@ -27,6 +27,7 @@ 30.4 import static jdk.nashorn.internal.lookup.Lookup.MH; 30.5 import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT; 30.6 import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid; 30.7 + 30.8 import java.lang.invoke.CallSite; 30.9 import java.lang.invoke.MethodHandle; 30.10 import java.lang.invoke.MethodHandles; 30.11 @@ -786,6 +787,7 @@ 30.12 // isn't available, we'll use the old one bound into the call site. 30.13 final OptimismInfo effectiveOptInfo = currentOptInfo != null ? currentOptInfo : oldOptInfo; 30.14 FunctionNode fn = effectiveOptInfo.reparse(); 30.15 + final boolean serialized = effectiveOptInfo.isSerialized(); 30.16 final Compiler compiler = effectiveOptInfo.getCompiler(fn, callSiteType, re); //set to non rest-of 30.17 30.18 if (!shouldRecompile) { 30.19 @@ -793,17 +795,17 @@ 30.20 // recompiled a deoptimized version for an inner invocation. 30.21 // We still need to do the rest of from the beginning 30.22 logRecompile("Rest-of compilation [STANDALONE] ", fn, callSiteType, effectiveOptInfo.invalidatedProgramPoints); 30.23 - return restOfHandle(effectiveOptInfo, compiler.compile(fn, CompilationPhases.COMPILE_ALL_RESTOF), currentOptInfo != null); 30.24 + return restOfHandle(effectiveOptInfo, compiler.compile(fn, serialized ? CompilationPhases.COMPILE_SERIALIZED_RESTOF : CompilationPhases.COMPILE_ALL_RESTOF), currentOptInfo != null); 30.25 } 30.26 30.27 logRecompile("Deoptimizing recompilation (up to bytecode) ", fn, callSiteType, effectiveOptInfo.invalidatedProgramPoints); 30.28 - fn = compiler.compile(fn, CompilationPhases.COMPILE_UPTO_BYTECODE); 30.29 + fn = compiler.compile(fn, serialized ? CompilationPhases.RECOMPILE_SERIALIZED_UPTO_BYTECODE : CompilationPhases.COMPILE_UPTO_BYTECODE); 30.30 log.info("Reusable IR generated"); 30.31 30.32 // compile the rest of the function, and install it 30.33 log.info("Generating and installing bytecode from reusable IR..."); 30.34 logRecompile("Rest-of compilation [CODE PIPELINE REUSE] ", fn, callSiteType, effectiveOptInfo.invalidatedProgramPoints); 30.35 - final FunctionNode normalFn = compiler.compile(fn, CompilationPhases.COMPILE_FROM_BYTECODE); 30.36 + final FunctionNode normalFn = compiler.compile(fn, CompilationPhases.GENERATE_BYTECODE_AND_INSTALL); 30.37 30.38 if (effectiveOptInfo.data.usePersistentCodeCache()) { 30.39 final RecompilableScriptFunctionData data = effectiveOptInfo.data; 30.40 @@ -829,7 +831,7 @@ 30.41 constructor = null; // Will be regenerated when needed 30.42 30.43 log.info("Done: ", invoker); 30.44 - final MethodHandle restOf = restOfHandle(effectiveOptInfo, compiler.compile(fn, CompilationPhases.COMPILE_FROM_BYTECODE_RESTOF), canBeDeoptimized); 30.45 + final MethodHandle restOf = restOfHandle(effectiveOptInfo, compiler.compile(fn, CompilationPhases.GENERATE_BYTECODE_AND_INSTALL_RESTOF), canBeDeoptimized); 30.46 30.47 // Note that we only adjust the switch point after we set the invoker/constructor. This is important. 30.48 if (canBeDeoptimized) { 30.49 @@ -921,6 +923,10 @@ 30.50 FunctionNode reparse() { 30.51 return data.reparse(); 30.52 } 30.53 + 30.54 + boolean isSerialized() { 30.55 + return data.isSerialized(); 30.56 + } 30.57 } 30.58 30.59 @SuppressWarnings("unused")
31.1 --- a/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Fri Oct 17 14:24:26 2014 +0200 31.2 +++ b/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Mon Oct 20 12:06:36 2014 +0200 31.3 @@ -43,6 +43,7 @@ 31.4 import jdk.nashorn.internal.codegen.Compiler.CompilationPhases; 31.5 import jdk.nashorn.internal.codegen.CompilerConstants; 31.6 import jdk.nashorn.internal.codegen.FunctionSignature; 31.7 +import jdk.nashorn.internal.codegen.Namespace; 31.8 import jdk.nashorn.internal.codegen.ObjectClassGenerator.AllocatorDescriptor; 31.9 import jdk.nashorn.internal.codegen.OptimisticTypesPersistence; 31.10 import jdk.nashorn.internal.codegen.TypeMap; 31.11 @@ -79,6 +80,9 @@ 31.12 /** Source from which FunctionNode was parsed. */ 31.13 private transient Source source; 31.14 31.15 + /** Serialized, compressed form of the AST. Used by split functions as they can't be reparsed from source. */ 31.16 + private final byte[] serializedAst; 31.17 + 31.18 /** Token of this function within the source. */ 31.19 private final long token; 31.20 31.21 @@ -127,6 +131,7 @@ 31.22 * @param nestedFunctions nested function map 31.23 * @param externalScopeDepths external scope depths 31.24 * @param internalSymbols internal symbols to method, defined in its scope 31.25 + * @param serializedAst a serialized AST representation. Normally only used for split functions. 31.26 */ 31.27 public RecompilableScriptFunctionData( 31.28 final FunctionNode functionNode, 31.29 @@ -134,7 +139,8 @@ 31.30 final AllocatorDescriptor allocationDescriptor, 31.31 final Map<Integer, RecompilableScriptFunctionData> nestedFunctions, 31.32 final Map<String, Integer> externalScopeDepths, 31.33 - final Set<String> internalSymbols) { 31.34 + final Set<String> internalSymbols, 31.35 + final byte[] serializedAst) { 31.36 31.37 super(functionName(functionNode), 31.38 Math.min(functionNode.getParameters().size(), MAX_ARITY), 31.39 @@ -158,6 +164,7 @@ 31.40 nfn.setParent(this); 31.41 } 31.42 31.43 + this.serializedAst = serializedAst; 31.44 createLogger(); 31.45 } 31.46 31.47 @@ -212,10 +219,7 @@ 31.48 */ 31.49 public int getExternalSymbolDepth(final String symbolName) { 31.50 final Integer depth = externalScopeDepths.get(symbolName); 31.51 - if (depth == null) { 31.52 - return -1; 31.53 - } 31.54 - return depth; 31.55 + return depth == null ? -1 : depth; 31.56 } 31.57 31.58 /** 31.59 @@ -354,8 +358,15 @@ 31.60 return allocationStrategy.allocate(map); 31.61 } 31.62 31.63 + boolean isSerialized() { 31.64 + return serializedAst != null; 31.65 + } 31.66 + 31.67 FunctionNode reparse() { 31.68 - // NOTE: If we aren't recompiling the top-level program, we decrease functionNodeId 'cause we'll have a synthetic program node 31.69 + if (isSerialized()) { 31.70 + return deserialize(); 31.71 + } 31.72 + 31.73 final int descPosition = Token.descPosition(token); 31.74 final Context context = Context.getContextTrusted(); 31.75 final Parser parser = new Parser( 31.76 @@ -363,8 +374,10 @@ 31.77 source, 31.78 new Context.ThrowErrorManager(), 31.79 isStrict(), 31.80 + // source starts at line 0, so even though lineNumber is the correct declaration line, back off 31.81 + // one to make it exclusive 31.82 lineNumber - 1, 31.83 - context.getLogger(Parser.class)); // source starts at line 0, so even though lineNumber is the correct declaration line, back off one to make it exclusive 31.84 + context.getLogger(Parser.class)); 31.85 31.86 if (getFunctionFlag(FunctionNode.IS_ANONYMOUS)) { 31.87 parser.setFunctionName(functionName); 31.88 @@ -378,6 +391,17 @@ 31.89 return (isProgram() ? program : extractFunctionFromScript(program)).setName(null, functionName); 31.90 } 31.91 31.92 + private FunctionNode deserialize() { 31.93 + final ScriptEnvironment env = installer.getOwner(); 31.94 + final Timing timing = env._timing; 31.95 + final long t1 = System.nanoTime(); 31.96 + try { 31.97 + return AstDeserializer.deserialize(serializedAst).initializeDeserialized(source, new Namespace(env.getNamespace())); 31.98 + } finally { 31.99 + timing.accumulateTime("'Deserialize'", System.nanoTime() - t1); 31.100 + } 31.101 + } 31.102 + 31.103 private boolean getFunctionFlag(final int flag) { 31.104 return (functionFlags & flag) != 0; 31.105 } 31.106 @@ -486,7 +510,8 @@ 31.107 31.108 final FunctionNode fn = reparse(); 31.109 final Compiler compiler = getCompiler(fn, actualCallSiteType, runtimeScope); 31.110 - final FunctionNode compiledFn = compiler.compile(fn, CompilationPhases.COMPILE_ALL); 31.111 + final FunctionNode compiledFn = compiler.compile(fn, 31.112 + isSerialized() ? CompilationPhases.COMPILE_ALL_SERIALIZED : CompilationPhases.COMPILE_ALL); 31.113 31.114 if (persist && !compiledFn.getFlag(FunctionNode.HAS_APPLY_TO_CALL_SPECIALIZATION)) { 31.115 compiler.persistClassInfo(cacheKey, compiledFn); 31.116 @@ -606,7 +631,7 @@ 31.117 31.118 MethodHandle lookupCodeMethod(final Class<?> codeClass, final MethodType targetType) { 31.119 if (log.isEnabled()) { 31.120 - log.info("Looking up ", DebugLogger.quote(name), " type=", targetType); 31.121 + log.info("Looking up ", DebugLogger.quote(functionName), " type=", targetType); 31.122 } 31.123 return MH.findStatic(LOOKUP, codeClass, functionName, targetType); 31.124 } 31.125 @@ -817,6 +842,26 @@ 31.126 return true; 31.127 } 31.128 31.129 + /** 31.130 + * Restores the {@link #getFunctionFlags()} flags to a function node. During on-demand compilation, we might need 31.131 + * to restore flags to a function node that was otherwise not subjected to a full compile pipeline (e.g. its parse 31.132 + * was skipped, or it's a nested function of a deserialized function. 31.133 + * @param lc current lexical context 31.134 + * @param fn the function node to restore flags onto 31.135 + * @return the transformed function node 31.136 + */ 31.137 + public FunctionNode restoreFlags(final LexicalContext lc, final FunctionNode fn) { 31.138 + assert fn.getId() == functionNodeId; 31.139 + FunctionNode newFn = fn.setFlags(lc, functionFlags); 31.140 + // This compensates for missing markEval() in case the function contains an inner function 31.141 + // that contains eval(), that now we didn't discover since we skipped the inner function. 31.142 + if (newFn.hasNestedEval()) { 31.143 + assert newFn.hasScopeBlock(); 31.144 + newFn = newFn.setBody(lc, newFn.getBody().setNeedsScope(null)); 31.145 + } 31.146 + return newFn; 31.147 + } 31.148 + 31.149 private void readObject(final java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { 31.150 in.defaultReadObject(); 31.151 createLogger();