Thu, 21 Feb 2013 16:57:21 +0100
8008648: Lazy JIT scope and callee semantics bugfixes. Broke out wallclock timer.
Reviewed-by: attila, hannesw
1.1 --- a/src/jdk/internal/dynalink/beans/BeansLinker.java Wed Feb 20 16:43:21 2013 +0100 1.2 +++ b/src/jdk/internal/dynalink/beans/BeansLinker.java Thu Feb 21 16:57:21 2013 +0100 1.3 @@ -159,7 +159,7 @@ 1.4 return linkers.get(clazz); 1.5 } 1.6 1.7 - /* 1.8 + /** 1.9 * Returns true if the object is a Dynalink Java dynamic method. 1.10 * 1.11 * @param obj the object we want to test for being a dynamic method
2.1 --- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java Wed Feb 20 16:43:21 2013 +0100 2.2 +++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java Thu Feb 21 16:57:21 2013 +0100 2.3 @@ -850,7 +850,7 @@ 2.4 * Determine if function is varargs and consequently variables have to 2.5 * be in the scope. 2.6 */ 2.7 - final boolean varsInScope = function.varsInScope(); 2.8 + final boolean varsInScope = function.allVarsInScope(); 2.9 2.10 // TODO for LET we can do better: if *block* does not contain any eval/with, we don't need its vars in scope. 2.11 2.12 @@ -2040,7 +2040,6 @@ 2.13 } 2.14 2.15 final Symbol varSymbol = varNode.getSymbol(); 2.16 - 2.17 assert varSymbol != null : "variable node " + varNode + " requires a symbol"; 2.18 2.19 assert method != null; 2.20 @@ -2058,7 +2057,7 @@ 2.21 } 2.22 final IdentNode identNode = varNode.getName(); 2.23 final Type type = identNode.getType(); 2.24 - if(varSymbol.isFastScope(getCurrentFunctionNode())) { 2.25 + if (varSymbol.isFastScope(getCurrentFunctionNode())) { 2.26 storeFastScopeVar(type, varSymbol, flags); 2.27 } else { 2.28 method.dynamicSet(type, identNode.getName(), flags);
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 3.2 +++ b/src/jdk/nashorn/internal/codegen/CompilationException.java Thu Feb 21 16:57:21 2013 +0100 3.3 @@ -0,0 +1,41 @@ 3.4 +/* 3.5 + * Copyright (c) 2010, 2013, 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 +/** 3.31 + * Exception when something in the compiler breaks down. Can only 3.32 + * be instantiated by the codegen package 3.33 + */ 3.34 +@SuppressWarnings("serial") 3.35 +public class CompilationException extends RuntimeException { 3.36 + 3.37 + CompilationException(final String description) { 3.38 + super(description); 3.39 + } 3.40 + 3.41 + CompilationException(final Exception cause) { 3.42 + super(cause); 3.43 + } 3.44 +}
4.1 --- a/src/jdk/nashorn/internal/codegen/CompilationPhase.java Wed Feb 20 16:43:21 2013 +0100 4.2 +++ b/src/jdk/nashorn/internal/codegen/CompilationPhase.java Thu Feb 21 16:57:21 2013 +0100 4.3 @@ -12,15 +12,21 @@ 4.4 import java.io.FileOutputStream; 4.5 import java.io.IOException; 4.6 import java.util.EnumSet; 4.7 +import java.util.HashSet; 4.8 +import java.util.Set; 4.9 + 4.10 import jdk.nashorn.internal.codegen.types.Type; 4.11 +import jdk.nashorn.internal.ir.CallNode; 4.12 import jdk.nashorn.internal.ir.FunctionNode; 4.13 import jdk.nashorn.internal.ir.FunctionNode.CompilationState; 4.14 import jdk.nashorn.internal.ir.Node; 4.15 +import jdk.nashorn.internal.ir.ReferenceNode; 4.16 +import jdk.nashorn.internal.ir.visitor.NodeVisitor; 4.17 import jdk.nashorn.internal.ir.debug.ASTWriter; 4.18 import jdk.nashorn.internal.ir.debug.PrintVisitor; 4.19 -import jdk.nashorn.internal.ir.visitor.NodeVisitor; 4.20 import jdk.nashorn.internal.runtime.Context; 4.21 import jdk.nashorn.internal.runtime.ECMAErrors; 4.22 +import jdk.nashorn.internal.runtime.Timing; 4.23 4.24 /** 4.25 * A compilation phase is a step in the processes of turning a JavaScript FunctionNode 4.26 @@ -35,7 +41,7 @@ 4.27 */ 4.28 LAZY_INITIALIZATION_PHASE(EnumSet.of(FunctionNode.CompilationState.INITIALIZED)) { 4.29 @Override 4.30 - boolean transform(final Compiler compiler, final FunctionNode fn) { 4.31 + void transform(final Compiler compiler, final FunctionNode fn) { 4.32 4.33 /* 4.34 * For lazy compilation, we might be given a node previously marked as lazy 4.35 @@ -53,15 +59,48 @@ 4.36 outermostFunctionNode.setIsLazy(false); 4.37 outermostFunctionNode.setReturnType(Type.UNKNOWN); 4.38 4.39 + final Set<FunctionNode> neverLazy = new HashSet<>(); 4.40 + final Set<FunctionNode> lazy = new HashSet<>(); 4.41 + 4.42 outermostFunctionNode.accept(new NodeVisitor() { 4.43 + // self references are done with invokestatic and thus cannot have trampolines - never lazy 4.44 + @Override 4.45 + public Node enter(final CallNode node) { 4.46 + final Node callee = node.getFunction(); 4.47 + if (callee instanceof ReferenceNode) { 4.48 + neverLazy.add(((ReferenceNode)callee).getReference()); 4.49 + return null; 4.50 + } 4.51 + return node; 4.52 + } 4.53 + 4.54 @Override 4.55 public Node enter(final FunctionNode node) { 4.56 + if (node == outermostFunctionNode) { 4.57 + return node; 4.58 + } 4.59 assert Compiler.LAZY_JIT; 4.60 - node.setIsLazy(node != outermostFunctionNode); 4.61 + lazy.add(node); 4.62 + 4.63 return node; 4.64 } 4.65 }); 4.66 - return true; 4.67 + 4.68 + for (final FunctionNode node : neverLazy) { 4.69 + Compiler.LOG.fine("Marking " + node.getName() + " as non lazy, as it's a self reference"); 4.70 + node.setIsLazy(false); 4.71 + lazy.remove(node); 4.72 + } 4.73 + 4.74 + for (final FunctionNode node : lazy) { 4.75 + Compiler.LOG.fine("Marking " + node.getName() + " as lazy"); 4.76 + node.setIsLazy(true); 4.77 + final FunctionNode parent = node.findParentFunction(); 4.78 + if (parent != null) { 4.79 + Compiler.LOG.fine("Marking " + parent.getName() + " as having lazy children - it needs scope for all variables"); 4.80 + parent.setHasLazyChildren(); 4.81 + } 4.82 + } 4.83 } 4.84 4.85 @Override 4.86 @@ -76,9 +115,8 @@ 4.87 */ 4.88 CONSTANT_FOLDING_PHASE(EnumSet.of(INITIALIZED), CONSTANT_FOLDED) { 4.89 @Override 4.90 - boolean transform(final Compiler compiler, final FunctionNode fn) { 4.91 + void transform(final Compiler compiler, final FunctionNode fn) { 4.92 fn.accept(new FoldConstants()); 4.93 - return true; 4.94 } 4.95 4.96 @Override 4.97 @@ -98,9 +136,8 @@ 4.98 */ 4.99 LOWERING_PHASE(EnumSet.of(INITIALIZED, CONSTANT_FOLDED), LOWERED) { 4.100 @Override 4.101 - boolean transform(final Compiler compiler, final FunctionNode fn) { 4.102 + void transform(final Compiler compiler, final FunctionNode fn) { 4.103 fn.accept(new Lower()); 4.104 - return true; 4.105 } 4.106 4.107 @Override 4.108 @@ -115,20 +152,17 @@ 4.109 */ 4.110 ATTRIBUTION_PHASE(EnumSet.of(INITIALIZED, CONSTANT_FOLDED, LOWERED), ATTR) { 4.111 @Override 4.112 - boolean transform(final Compiler compiler, final FunctionNode fn) { 4.113 + void transform(final Compiler compiler, final FunctionNode fn) { 4.114 final Context context = compiler.getContext(); 4.115 - try { 4.116 - fn.accept(new Attr(context)); 4.117 - return true; 4.118 - } finally { 4.119 - if (context._print_lower_ast) { 4.120 - context.getErr().println(new ASTWriter(fn)); 4.121 - } 4.122 4.123 - if (context._print_lower_parse) { 4.124 - context.getErr().println(new PrintVisitor(fn)); 4.125 - } 4.126 + fn.accept(new Attr(context)); 4.127 + if (context._print_lower_ast) { 4.128 + context.getErr().println(new ASTWriter(fn)); 4.129 } 4.130 + 4.131 + if (context._print_lower_parse) { 4.132 + context.getErr().println(new PrintVisitor(fn)); 4.133 + } 4.134 } 4.135 4.136 @Override 4.137 @@ -146,7 +180,7 @@ 4.138 */ 4.139 SPLITTING_PHASE(EnumSet.of(INITIALIZED, CONSTANT_FOLDED, LOWERED, ATTR), SPLIT) { 4.140 @Override 4.141 - boolean transform(final Compiler compiler, final FunctionNode fn) { 4.142 + void transform(final Compiler compiler, final FunctionNode fn) { 4.143 final CompileUnit outermostCompileUnit = compiler.addCompileUnit(compiler.firstCompileUnitName()); 4.144 4.145 new Splitter(compiler, fn, outermostCompileUnit).split(); 4.146 @@ -157,7 +191,6 @@ 4.147 assert compiler.getStrictMode(); 4.148 compiler.setStrictMode(true); 4.149 } 4.150 - return true; 4.151 } 4.152 4.153 @Override 4.154 @@ -181,9 +214,8 @@ 4.155 */ 4.156 TYPE_FINALIZATION_PHASE(EnumSet.of(INITIALIZED, CONSTANT_FOLDED, LOWERED, ATTR, SPLIT), FINALIZED) { 4.157 @Override 4.158 - boolean transform(final Compiler compiler, final FunctionNode fn) { 4.159 + void transform(final Compiler compiler, final FunctionNode fn) { 4.160 fn.accept(new FinalizeTypes()); 4.161 - return true; 4.162 } 4.163 4.164 @Override 4.165 @@ -199,7 +231,7 @@ 4.166 */ 4.167 BYTECODE_GENERATION_PHASE(EnumSet.of(INITIALIZED, CONSTANT_FOLDED, LOWERED, ATTR, SPLIT, FINALIZED), EMITTED) { 4.168 @Override 4.169 - boolean transform(final Compiler compiler, final FunctionNode fn) { 4.170 + void transform(final Compiler compiler, final FunctionNode fn) { 4.171 final Context context = compiler.getContext(); 4.172 4.173 try { 4.174 @@ -253,7 +285,7 @@ 4.175 final File dir = new File(fileName.substring(0, index)); 4.176 try { 4.177 if (!dir.exists() && !dir.mkdirs()) { 4.178 - throw new IOException(); 4.179 + throw new IOException(dir.toString()); 4.180 } 4.181 final File file = new File(context._dest_dir, fileName); 4.182 try (final FileOutputStream fos = new FileOutputStream(file)) { 4.183 @@ -265,8 +297,6 @@ 4.184 } 4.185 } 4.186 } 4.187 - 4.188 - return true; 4.189 } 4.190 4.191 @Override 4.192 @@ -281,8 +311,6 @@ 4.193 private long endTime; 4.194 private boolean isFinished; 4.195 4.196 - private static final long[] accumulatedTime = new long[CompilationPhase.values().length]; 4.197 - 4.198 private CompilationPhase(final EnumSet<CompilationState> pre) { 4.199 this(pre, null); 4.200 } 4.201 @@ -313,7 +341,7 @@ 4.202 4.203 protected void end(final FunctionNode functionNode) { 4.204 endTime = System.currentTimeMillis(); 4.205 - accumulatedTime[ordinal()] += (endTime - startTime); 4.206 + Timing.accumulateTime(toString(), endTime - startTime); 4.207 4.208 if (post != null) { 4.209 functionNode.setState(post); 4.210 @@ -334,23 +362,15 @@ 4.211 return endTime; 4.212 } 4.213 4.214 - public static long getAccumulatedTime(final CompilationPhase phase) { 4.215 - return accumulatedTime[phase.ordinal()]; 4.216 - } 4.217 + abstract void transform(final Compiler compiler, final FunctionNode functionNode) throws CompilationException; 4.218 4.219 - abstract boolean transform(final Compiler compiler, final FunctionNode functionNode); 4.220 - 4.221 - final boolean apply(final Compiler compiler, final FunctionNode functionNode) { 4.222 - try { 4.223 - if (!isApplicable(functionNode)) { 4.224 - return false; 4.225 - } 4.226 - begin(functionNode); 4.227 - transform(compiler, functionNode); 4.228 - return true; 4.229 - } finally { 4.230 - end(functionNode); 4.231 + final void apply(final Compiler compiler, final FunctionNode functionNode) throws CompilationException { 4.232 + if (!isApplicable(functionNode)) { 4.233 + throw new CompilationException("compile phase not applicable: " + this); 4.234 } 4.235 + begin(functionNode); 4.236 + transform(compiler, functionNode); 4.237 + end(functionNode); 4.238 } 4.239 4.240 }
5.1 --- a/src/jdk/nashorn/internal/codegen/Compiler.java Wed Feb 20 16:43:21 2013 +0100 5.2 +++ b/src/jdk/nashorn/internal/codegen/Compiler.java Thu Feb 21 16:57:21 2013 +0100 5.3 @@ -54,6 +54,7 @@ 5.4 import jdk.nashorn.internal.runtime.Context; 5.5 import jdk.nashorn.internal.runtime.DebugLogger; 5.6 import jdk.nashorn.internal.runtime.Source; 5.7 +import jdk.nashorn.internal.runtime.Timing; 5.8 import jdk.nashorn.internal.runtime.options.Options; 5.9 5.10 /** 5.11 @@ -72,8 +73,6 @@ 5.12 5.13 static final boolean LAZY_JIT = Options.getBooleanProperty("nashorn.compiler.lazy"); 5.14 5.15 - static final boolean TIME_COMPILATION = Options.getBooleanProperty("nashorn.compiler.time"); 5.16 - 5.17 private final Map<String, byte[]> bytecode; 5.18 5.19 private final Set<CompileUnit> compileUnits; 5.20 @@ -92,7 +91,9 @@ 5.21 5.22 private CodeInstaller<Context> installer; 5.23 5.24 - static final DebugLogger LOG = new DebugLogger("compiler"); 5.25 + /** logger for compiler, trampolines, splits and related code generation events 5.26 + * that affect classes */ 5.27 + public static final DebugLogger LOG = new DebugLogger("compiler"); 5.28 5.29 /** 5.30 * This array contains names that need to be reserved at the start 5.31 @@ -179,6 +180,13 @@ 5.32 SEQUENCE_LAZY : 5.33 SEQUENCE_NORMAL; 5.34 5.35 + private static String lazyTag(final FunctionNode functionNode) { 5.36 + if (functionNode.isLazy()) { 5.37 + return '$' + LAZY.tag() + '$' + functionNode.getName(); 5.38 + } 5.39 + return ""; 5.40 + } 5.41 + 5.42 /** 5.43 * Constructor 5.44 * 5.45 @@ -187,6 +195,7 @@ 5.46 * @param sequence {@link Compiler#CompilationSequence} of {@link CompilationPhase}s to apply as this compilation 5.47 * @param strict should this compilation use strict mode semantics 5.48 */ 5.49 + //TODO support an array of FunctionNodes for batch lazy compilation 5.50 Compiler(final Context context, final CodeInstaller<Context> installer, final FunctionNode functionNode, final CompilationSequence sequence, final boolean strict) { 5.51 this.context = context; 5.52 this.functionNode = functionNode; 5.53 @@ -198,14 +207,16 @@ 5.54 this.bytecode = new HashMap<>(); 5.55 5.56 final StringBuilder sb = new StringBuilder(); 5.57 - sb.append(functionNode.uniqueName(DEFAULT_SCRIPT_NAME.tag())). 5.58 + sb.append(functionNode.uniqueName(DEFAULT_SCRIPT_NAME.tag() + lazyTag(functionNode))). 5.59 append('$'). 5.60 - append(safeSourceName(functionNode.getSource())). 5.61 - append(functionNode.isLazy() ? LAZY.tag() : ""); 5.62 + append(safeSourceName(functionNode.getSource())); 5.63 5.64 this.scriptName = sb.toString(); 5.65 5.66 - LOG.info("Initializing compiler for scriptName = " + scriptName + ", root function: '" + functionNode.getName() + "'"); 5.67 + LOG.info("Initializing compiler for '" + functionNode.getName() + "' scriptName = " + scriptName + ", root function: '" + functionNode.getName() + "'"); 5.68 + if (functionNode.isLazy()) { 5.69 + LOG.info(">>> This is a lazy recompilation triggered by a trampoline"); 5.70 + } 5.71 } 5.72 5.73 /** 5.74 @@ -241,22 +252,23 @@ 5.75 5.76 /** 5.77 * Execute the compilation this Compiler was created with 5.78 - * @return true if compilation succeeds. 5.79 + * @throws CompilationException if something goes wrong 5.80 */ 5.81 - public boolean compile() { 5.82 + public void compile() throws CompilationException { 5.83 for (final String reservedName : RESERVED_NAMES) { 5.84 functionNode.uniqueName(reservedName); 5.85 } 5.86 5.87 for (final CompilationPhase phase : sequence) { 5.88 - LOG.info("Entering compile phase " + phase + " for function '" + functionNode.getName() + "'"); 5.89 - if (phase.isApplicable(functionNode)) { 5.90 - if (!phase.apply(this, functionNode)) { //TODO exceptions, error logging 5.91 - return false; 5.92 - } 5.93 + phase.apply(this, functionNode); 5.94 + final String end = phase.toString() + " done for function '" + functionNode.getName() + "'"; 5.95 + if (Timing.isEnabled()) { 5.96 + final long duration = phase.getEndTime() - phase.getStartTime(); 5.97 + LOG.info(end + " in " + duration + " ms"); 5.98 + } else { 5.99 + LOG.info(end); 5.100 } 5.101 } 5.102 - return true; 5.103 } 5.104 5.105 /** 5.106 @@ -264,11 +276,15 @@ 5.107 * @return root script class - if there are several compile units they will also be installed 5.108 */ 5.109 public Class<?> install() { 5.110 + final long t0 = Timing.isEnabled() ? System.currentTimeMillis() : 0L; 5.111 + 5.112 + assert functionNode.hasState(CompilationState.EMITTED) : functionNode.getName() + " has no bytecode and cannot be installed"; 5.113 + 5.114 Class<?> rootClass = null; 5.115 5.116 for (final Entry<String, byte[]> entry : bytecode.entrySet()) { 5.117 final String className = entry.getKey(); 5.118 - LOG.info("Installing class " + className); 5.119 + LOG.fine("Installing class " + className); 5.120 5.121 final byte[] code = entry.getValue(); 5.122 final Class<?> clazz = installer.install(Compiler.binaryName(className), code); 5.123 @@ -299,7 +315,13 @@ 5.124 } 5.125 } 5.126 5.127 - LOG.info("Root class: " + rootClass); 5.128 + LOG.info("Installed root class: " + rootClass + " and " + bytecode.size() + " compile unit classes"); 5.129 + if (Timing.isEnabled()) { 5.130 + final long duration = System.currentTimeMillis() - t0; 5.131 + Timing.accumulateTime("[Code Installation]", duration); 5.132 + LOG.info("Installation time: " + duration + " ms"); 5.133 + } 5.134 + 5.135 return rootClass; 5.136 } 5.137 5.138 @@ -376,7 +398,7 @@ 5.139 private CompileUnit addCompileUnit(final String unitClassName, final long initialWeight) { 5.140 final CompileUnit compileUnit = initCompileUnit(unitClassName, initialWeight); 5.141 compileUnits.add(compileUnit); 5.142 - LOG.info("Added compile unit " + compileUnit); 5.143 + LOG.fine("Added compile unit " + compileUnit); 5.144 return compileUnit; 5.145 } 5.146 5.147 @@ -438,24 +460,4 @@ 5.148 assert !USE_INT_ARITH : "Integer arithmetic is not enabled"; 5.149 } 5.150 5.151 - static { 5.152 - if (TIME_COMPILATION) { 5.153 - Runtime.getRuntime().addShutdownHook(new Thread() { 5.154 - @Override 5.155 - public void run() { 5.156 - for (final CompilationPhase phase : CompilationPhase.values()) { 5.157 - final StringBuilder sb = new StringBuilder(); 5.158 - sb.append(phase); 5.159 - while (sb.length() < 32) { 5.160 - sb.append(' '); 5.161 - } 5.162 - sb.append(CompilationPhase.getAccumulatedTime(phase)); 5.163 - sb.append(' '); 5.164 - sb.append(" ms"); 5.165 - System.err.println(sb.toString()); //Context err is gone by shutdown TODO 5.166 - } 5.167 - } 5.168 - }); 5.169 - } 5.170 - } 5.171 }
6.1 --- a/src/jdk/nashorn/internal/codegen/FinalizeTypes.java Wed Feb 20 16:43:21 2013 +0100 6.2 +++ b/src/jdk/nashorn/internal/codegen/FinalizeTypes.java Thu Feb 21 16:57:21 2013 +0100 6.3 @@ -562,7 +562,7 @@ 6.4 6.5 final FunctionNode functionNode = block.getFunction(); 6.6 final List<Symbol> symbols = block.getFrame().getSymbols(); 6.7 - final boolean allVarsInScope = functionNode.varsInScope(); 6.8 + final boolean allVarsInScope = functionNode.allVarsInScope(); 6.9 final boolean isVarArg = functionNode.isVarArg(); 6.10 6.11 for (final Symbol symbol : symbols) {
7.1 --- a/src/jdk/nashorn/internal/codegen/Lower.java Wed Feb 20 16:43:21 2013 +0100 7.2 +++ b/src/jdk/nashorn/internal/codegen/Lower.java Thu Feb 21 16:57:21 2013 +0100 7.3 @@ -709,7 +709,6 @@ 7.4 * @return eval location 7.5 */ 7.6 private static String evalLocation(final IdentNode node) { 7.7 - //final StringBuilder sb = new StringBuilder(node.getSource().getName()); 7.8 return new StringBuilder(). 7.9 append(node.getSource().getName()). 7.10 append('#').
8.1 --- a/src/jdk/nashorn/internal/codegen/MethodEmitter.java Wed Feb 20 16:43:21 2013 +0100 8.2 +++ b/src/jdk/nashorn/internal/codegen/MethodEmitter.java Thu Feb 21 16:57:21 2013 +0100 8.3 @@ -203,7 +203,7 @@ 8.4 8.5 @Override 8.6 public String toString() { 8.7 - return "methodEmitter: " + (functionNode == null ? method : functionNode.getName()).toString(); 8.8 + return "methodEmitter: " + (functionNode == null ? method : functionNode.getName()).toString() + ' ' + stack; 8.9 } 8.10 8.11 /**
9.1 --- a/src/jdk/nashorn/internal/codegen/Splitter.java Wed Feb 20 16:43:21 2013 +0100 9.2 +++ b/src/jdk/nashorn/internal/codegen/Splitter.java Thu Feb 21 16:57:21 2013 +0100 9.3 @@ -92,15 +92,15 @@ 9.4 */ 9.5 void split() { 9.6 if (functionNode.isLazy()) { 9.7 - LOG.info("Postponing split of '" + functionNode.getName() + "' as it's lazy"); 9.8 + LOG.fine("Postponing split of '" + functionNode.getName() + "' as it's lazy"); 9.9 return; 9.10 } 9.11 - LOG.info("Initiating split of '" + functionNode.getName() + "'"); 9.12 + LOG.fine("Initiating split of '" + functionNode.getName() + "'"); 9.13 9.14 long weight = WeighNodes.weigh(functionNode); 9.15 9.16 if (weight >= SPLIT_THRESHOLD) { 9.17 - LOG.info("Splitting '" + functionNode.getName() + "' as its weight " + weight + " exceeds split threshold " + SPLIT_THRESHOLD); 9.18 + LOG.fine("Splitting '" + functionNode.getName() + "' as its weight " + weight + " exceeds split threshold " + SPLIT_THRESHOLD); 9.19 9.20 functionNode.accept(this); 9.21
10.1 --- a/src/jdk/nashorn/internal/ir/FunctionNode.java Wed Feb 20 16:43:21 2013 +0100 10.2 +++ b/src/jdk/nashorn/internal/ir/FunctionNode.java Thu Feb 21 16:57:21 2013 +0100 10.3 @@ -188,25 +188,27 @@ 10.4 private static final int IS_LOWERED = 0b0000_0000_0001_0000; 10.5 /** Has this node been split because it was too large? */ 10.6 private static final int IS_SPLIT = 0b0000_0000_0010_0000; 10.7 + /** Does the function call eval? */ 10.8 + private static final int HAS_EVAL = 0b0000_0000_0100_0000; 10.9 + /** Does the function contain a with block ? */ 10.10 + private static final int HAS_WITH = 0b0000_0000_1000_0000; 10.11 + /** Does a descendant function contain a with or eval? */ 10.12 + private static final int HAS_DESCENDANT_WITH_OR_EVAL = 0b0000_0001_0000_0000; 10.13 + /** Does the function define "arguments" identifier as a parameter of nested function name? */ 10.14 + private static final int DEFINES_ARGUMENTS = 0b0000_0010_0000_0000; 10.15 + /** Does the function need a self symbol? */ 10.16 + private static final int NEEDS_SELF_SYMBOL = 0b0000_0100_0000_0000; 10.17 + /** Does this function or any of its descendants use variables from an ancestor function's scope (incl. globals)? */ 10.18 + private static final int USES_ANCESTOR_SCOPE = 0b0000_1000_0000_0000; 10.19 /** Is this function lazily compiled? */ 10.20 - private static final int IS_LAZY = 0b0000_0000_0100_0000; 10.21 - /** Does the function call eval? */ 10.22 - private static final int HAS_EVAL = 0b0000_0000_1000_0000; 10.23 - /** Does the function contain a with block ? */ 10.24 - private static final int HAS_WITH = 0b0000_0001_0000_0000; 10.25 - /** Does a descendant function contain a with or eval? */ 10.26 - private static final int HAS_DESCENDANT_WITH_OR_EVAL = 0b0000_0010_0000_0000; 10.27 - /** Does the function define "arguments" identifier as a parameter of nested function name? */ 10.28 - private static final int DEFINES_ARGUMENTS = 0b0000_0100_0000_0000; 10.29 - /** Does the function need a self symbol? */ 10.30 - private static final int NEEDS_SELF_SYMBOL = 0b0000_1000_0000_0000; 10.31 - /** Does this function or any of its descendants use variables from an ancestor function's scope (incl. globals)? */ 10.32 - private static final int USES_ANCESTOR_SCOPE = 0b0001_0000_0000_0000; 10.33 + private static final int IS_LAZY = 0b0001_0000_0000_0000; 10.34 + /** Does this function have lazy, yet uncompiled children */ 10.35 + private static final int HAS_LAZY_CHILDREN = 0b0010_0000_0000_0000; 10.36 10.37 /** Does this function or any nested functions contain a with or an eval? */ 10.38 private static final int HAS_DEEP_WITH_OR_EVAL = HAS_EVAL | HAS_WITH | HAS_DESCENDANT_WITH_OR_EVAL; 10.39 /** Does this function need to store all its variables in scope? */ 10.40 - private static final int HAS_ALL_VARS_IN_SCOPE = HAS_DEEP_WITH_OR_EVAL | IS_SPLIT; 10.41 + private static final int HAS_ALL_VARS_IN_SCOPE = HAS_DEEP_WITH_OR_EVAL | IS_SPLIT | HAS_LAZY_CHILDREN; 10.42 /** Does this function potentially need "arguments"? Note that this is not a full test, as further negative check of REDEFINES_ARGS is needed. */ 10.43 private static final int MAYBE_NEEDS_ARGUMENTS = USES_ARGUMENTS | HAS_EVAL; 10.44 /** Does this function need the parent scope? It needs it if either it or its descendants use variables from it, or have a deep with or eval. */ 10.45 @@ -862,7 +864,7 @@ 10.46 * 10.47 * @return true if all variables should be in scope 10.48 */ 10.49 - public boolean varsInScope() { 10.50 + public boolean allVarsInScope() { 10.51 return isScript() || (flags & HAS_ALL_VARS_IN_SCOPE) != 0; 10.52 } 10.53 10.54 @@ -884,6 +886,23 @@ 10.55 } 10.56 10.57 /** 10.58 + * Checks if this function has yet-to-be-generated child functions 10.59 + * 10.60 + * @return true if there are lazy child functions 10.61 + */ 10.62 + public boolean hasLazyChildren() { 10.63 + return (flags & HAS_LAZY_CHILDREN) != 0; 10.64 + } 10.65 + 10.66 + /** 10.67 + * Flag this function node as having yet-to-be-generated child functions 10.68 + */ 10.69 + public void setHasLazyChildren() { 10.70 + this.flags |= HAS_LAZY_CHILDREN; 10.71 + setNeedsScope(); 10.72 + } 10.73 + 10.74 + /** 10.75 * Get the parameters to this function 10.76 * @return a list of IdentNodes which represent the function parameters, in order 10.77 */
11.1 --- a/src/jdk/nashorn/internal/ir/TernaryNode.java Wed Feb 20 16:43:21 2013 +0100 11.2 +++ b/src/jdk/nashorn/internal/ir/TernaryNode.java Thu Feb 21 16:57:21 2013 +0100 11.3 @@ -54,7 +54,7 @@ 11.4 private TernaryNode(final TernaryNode ternaryNode, final CopyState cs) { 11.5 super(ternaryNode, cs); 11.6 11.7 - third = cs.existingOrCopy(ternaryNode.third); 11.8 + this.third = cs.existingOrCopy(ternaryNode.third); 11.9 } 11.10 11.11 @Override
12.1 --- a/src/jdk/nashorn/internal/objects/ScriptFunctionTrampolineImpl.java Wed Feb 20 16:43:21 2013 +0100 12.2 +++ b/src/jdk/nashorn/internal/objects/ScriptFunctionTrampolineImpl.java Thu Feb 21 16:57:21 2013 +0100 12.3 @@ -5,6 +5,7 @@ 12.4 import java.lang.invoke.MethodHandle; 12.5 import java.lang.invoke.MethodHandles; 12.6 import java.lang.invoke.MethodType; 12.7 +import jdk.nashorn.internal.codegen.CompilationException; 12.8 import jdk.nashorn.internal.codegen.Compiler; 12.9 import jdk.nashorn.internal.codegen.FunctionSignature; 12.10 import jdk.nashorn.internal.codegen.types.Type; 12.11 @@ -77,12 +78,10 @@ 12.12 return super.makeBoundFunction(data); 12.13 } 12.14 12.15 - private MethodHandle compile() { 12.16 + private MethodHandle compile() throws CompilationException { 12.17 final Compiler compiler = new Compiler(installer, functionNode); 12.18 - if (!compiler.compile()) { 12.19 - assert false : "compilation error in trampoline for " + functionNode.getName(); 12.20 - return null; 12.21 - } 12.22 + 12.23 + compiler.compile(); 12.24 12.25 final Class<?> clazz = compiler.install(); 12.26 /* compute function signature for lazy method. this can be done first after compilation, as only then do we know 12.27 @@ -91,7 +90,9 @@ 12.28 final MethodType mt = signature.getMethodType(); 12.29 12.30 MethodHandle mh = MH.findStatic(MethodHandles.publicLookup(), clazz, functionNode.getName(), mt); 12.31 - mh = MH.bindTo(mh, this); 12.32 + if (functionNode.needsCallee()) { 12.33 + mh = MH.bindTo(mh, this); 12.34 + } 12.35 12.36 // now the invoker method looks like the one our superclass is expecting 12.37 resetInvoker(mh); 12.38 @@ -100,11 +101,11 @@ 12.39 } 12.40 12.41 @SuppressWarnings("unused") 12.42 - private Object trampoline(final Object... args) { 12.43 - /** Create a new compiler for the lazy node, using the same installation policy as the old one */ 12.44 - 12.45 + private Object trampoline(final Object... args) throws CompilationException { 12.46 + Compiler.LOG.info(">>> TRAMPOLINE: Hitting trampoline for '" + functionNode.getName() + "'"); 12.47 MethodHandle mh = compile(); 12.48 12.49 + Compiler.LOG.info("<<< COMPILED TO: " + mh); 12.50 // spread the array to invididual args of the correct type 12.51 mh = MH.asSpreader(mh, Object[].class, mh.type().parameterCount()); 12.52
13.1 --- a/src/jdk/nashorn/internal/parser/Parser.java Wed Feb 20 16:43:21 2013 +0100 13.2 +++ b/src/jdk/nashorn/internal/parser/Parser.java Thu Feb 21 16:57:21 2013 +0100 13.3 @@ -97,15 +97,16 @@ 13.4 import jdk.nashorn.internal.ir.WhileNode; 13.5 import jdk.nashorn.internal.ir.WithNode; 13.6 import jdk.nashorn.internal.runtime.Context; 13.7 +import jdk.nashorn.internal.runtime.DebugLogger; 13.8 import jdk.nashorn.internal.runtime.ErrorManager; 13.9 import jdk.nashorn.internal.runtime.JSErrorType; 13.10 import jdk.nashorn.internal.runtime.ParserException; 13.11 import jdk.nashorn.internal.runtime.ScriptingFunctions; 13.12 import jdk.nashorn.internal.runtime.Source; 13.13 +import jdk.nashorn.internal.runtime.Timing; 13.14 13.15 /** 13.16 * Builds the IR. 13.17 - * 13.18 */ 13.19 public class Parser extends AbstractParser { 13.20 /** Current context. */ 13.21 @@ -126,6 +127,8 @@ 13.22 /** Namespace for function names where not explicitly given */ 13.23 private final Namespace namespace; 13.24 13.25 + private static DebugLogger LOG = new DebugLogger("parser"); 13.26 + 13.27 /** 13.28 * Constructor 13.29 * 13.30 @@ -176,6 +179,9 @@ 13.31 * @return function node resulting from successful parse 13.32 */ 13.33 public FunctionNode parse(final String scriptName) { 13.34 + final long t0 = Timing.isEnabled() ? System.currentTimeMillis() : 0L; 13.35 + LOG.info(this + " begin for '" + scriptName + "'"); 13.36 + 13.37 try { 13.38 stream = new TokenStream(); 13.39 lexer = new Lexer(source, stream, scripting && !context._no_syntax_extensions); 13.40 @@ -208,6 +214,14 @@ 13.41 } 13.42 13.43 return null; 13.44 + } finally { 13.45 + final String end = this + " end '" + scriptName + "'"; 13.46 + if (Timing.isEnabled()) { 13.47 + Timing.accumulateTime(toString(), System.currentTimeMillis() - t0); 13.48 + LOG.info(end + "' in " + (System.currentTimeMillis() - t0) + " ms"); 13.49 + } else { 13.50 + LOG.info(end); 13.51 + } 13.52 } 13.53 } 13.54 13.55 @@ -3064,4 +3078,8 @@ 13.56 return null; 13.57 } 13.58 13.59 + @Override 13.60 + public String toString() { 13.61 + return "[JavaScript Parsing]"; 13.62 + } 13.63 }
14.1 --- a/src/jdk/nashorn/internal/runtime/NashornLoader.java Wed Feb 20 16:43:21 2013 +0100 14.2 +++ b/src/jdk/nashorn/internal/runtime/NashornLoader.java Thu Feb 21 16:57:21 2013 +0100 14.3 @@ -57,7 +57,7 @@ 14.4 * @param name name of the class to be loaded 14.5 * @param resolve whether the class should be resolved or not 14.6 * @return Class object 14.7 - * @throws ClassNotFoundException 14.8 + * @throws ClassNotFoundException if class cannot be loaded 14.9 */ 14.10 protected final Class<?> loadClassTrusted(final String name, final boolean resolve) throws ClassNotFoundException { 14.11 return super.loadClass(name, resolve);
15.1 --- a/src/jdk/nashorn/internal/runtime/ScriptingFunctions.java Wed Feb 20 16:43:21 2013 +0100 15.2 +++ b/src/jdk/nashorn/internal/runtime/ScriptingFunctions.java Thu Feb 21 16:57:21 2013 +0100 15.3 @@ -121,11 +121,11 @@ 15.4 * 15.5 * @param self self reference 15.6 * @param string string to execute 15.7 - * @param input 15.8 + * @param input input 15.9 * 15.10 * @return output string from the request 15.11 - * @throws IOException 15.12 - * @throws InterruptedException 15.13 + * @throws IOException if any stream access fails 15.14 + * @throws InterruptedException if execution is interrupted 15.15 */ 15.16 public static Object exec(final Object self, final Object string, final Object input) throws IOException, InterruptedException { 15.17 // Current global is need to fetch additional inputs and for additional results.
16.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 16.2 +++ b/src/jdk/nashorn/internal/runtime/Timing.java Thu Feb 21 16:57:21 2013 +0100 16.3 @@ -0,0 +1,109 @@ 16.4 +/* 16.5 + * Copyright (c) 2010, 2013, 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 +package jdk.nashorn.internal.runtime; 16.29 + 16.30 +import java.util.LinkedHashMap; 16.31 +import java.util.Map; 16.32 + 16.33 +import jdk.nashorn.internal.runtime.options.Options; 16.34 + 16.35 +/** 16.36 + * Simple wallclock timing framework 16.37 + */ 16.38 +public final class Timing { 16.39 + private static final boolean ENABLED = Options.getBooleanProperty("nashorn.time"); 16.40 + private static final Map<String, Long> TIMINGS; 16.41 + private static final long START_TIME; 16.42 + 16.43 + static { 16.44 + if (ENABLED) { 16.45 + TIMINGS = new LinkedHashMap<>(); 16.46 + START_TIME = System.currentTimeMillis(); 16.47 + Runtime.getRuntime().addShutdownHook(new Thread() { 16.48 + @Override 16.49 + public void run() { 16.50 + final long t = System.currentTimeMillis(); 16.51 + long knownTime = 0L; 16.52 + int maxLength = 0; 16.53 + 16.54 + for (final Map.Entry<String, Long> entry : TIMINGS.entrySet()) { 16.55 + maxLength = Math.max(maxLength, entry.getKey().length()); 16.56 + } 16.57 + maxLength++; 16.58 + 16.59 + for (final Map.Entry<String, Long> entry : TIMINGS.entrySet()) { 16.60 + final StringBuilder sb = new StringBuilder(); 16.61 + 16.62 + sb.append(entry.getKey()); 16.63 + while (sb.length() < maxLength) { 16.64 + sb.append(' '); 16.65 + } 16.66 + 16.67 + final long duration = entry.getValue(); 16.68 + sb.append(duration); 16.69 + sb.append(' '); 16.70 + sb.append(" ms"); 16.71 + 16.72 + knownTime += duration; 16.73 + 16.74 + System.err.println(sb.toString()); //Context err is gone by shutdown TODO 16.75 + } 16.76 + 16.77 + final long total = t - START_TIME; 16.78 + System.err.println("Total runtime: " + total + " ms (Non-runtime: " + knownTime + " ms [" + (int)(knownTime * 100.0 / total) + "%])"); 16.79 + } 16.80 + }); 16.81 + } else { 16.82 + TIMINGS = null; 16.83 + START_TIME = 0L; 16.84 + } 16.85 + } 16.86 + 16.87 + /** 16.88 + * Check if timing is inabled 16.89 + * @return true if timing is enabled 16.90 + */ 16.91 + public static boolean isEnabled() { 16.92 + return ENABLED; 16.93 + } 16.94 + 16.95 + /** 16.96 + * When timing, this can be called to register a new module for timing 16.97 + * or add to its accumulated time 16.98 + * 16.99 + * @param module module name 16.100 + * @param duration duration to add to accumulated time for module 16.101 + */ 16.102 + public static void accumulateTime(final String module, final long duration) { 16.103 + if (Timing.isEnabled()) { 16.104 + Long accumulatedTime = TIMINGS.get(module); 16.105 + if (accumulatedTime == null) { 16.106 + accumulatedTime = 0L; 16.107 + } 16.108 + TIMINGS.put(module, accumulatedTime + duration); 16.109 + } 16.110 + } 16.111 + 16.112 +}
17.1 --- a/test/script/trusted/JDK-8006529.js Wed Feb 20 16:43:21 2013 +0100 17.2 +++ b/test/script/trusted/JDK-8006529.js Thu Feb 21 16:57:21 2013 +0100 17.3 @@ -60,7 +60,7 @@ 17.4 var getFunctionsMethod = FunctionNode.class.getMethod("getFunctions"); 17.5 17.6 // These are method names of methods in FunctionNode class 17.7 -var allAssertionList = ['isVarArg', 'needsParentScope', 'needsCallee', 'needsScope', 'needsSelfSymbol', 'isSplit', 'hasEval', 'hasWith', 'hasDeepWithOrEval', 'varsInScope', 'isStrictMode'] 17.8 +var allAssertionList = ['isVarArg', 'needsParentScope', 'needsCallee', 'needsScope', 'needsSelfSymbol', 'isSplit', 'hasEval', 'hasWith', 'hasDeepWithOrEval', 'allVarsInScope', 'isStrictMode'] 17.9 17.10 // corresponding Method objects of FunctionNode class 17.11 var functionNodeMethods = {}; 17.12 @@ -189,18 +189,18 @@ 17.13 // and all variables in scope. Actually, we could make "with" less wasteful, 17.14 // and only put those variables in scope that it actually references, similar 17.15 // to what nested functions do with variables in their parents. 17.16 -testFirstFn("(function f() { var o; with(o) {} })", 'needsParentScope', 'needsCallee', 'needsScope', 'hasWith', 'hasDeepWithOrEval', 'varsInScope') 17.17 +testFirstFn("(function f() { var o; with(o) {} })", 'needsParentScope', 'needsCallee', 'needsScope', 'hasWith', 'hasDeepWithOrEval', 'allVarsInScope') 17.18 17.19 // Using "eval" is as bad as using "with" with the added requirement of 17.20 // being vararg, 'cause we don't know if eval will be using "arguments". 17.21 -testFirstFn("(function f() { eval() })", 'needsParentScope', 'needsCallee', 'needsScope', 'hasEval', 'isVarArg', 'hasDeepWithOrEval', 'varsInScope') 17.22 +testFirstFn("(function f() { eval() })", 'needsParentScope', 'needsCallee', 'needsScope', 'hasEval', 'isVarArg', 'hasDeepWithOrEval', 'allVarsInScope') 17.23 17.24 // Nested function using "with" is pretty much the same as the parent 17.25 // function needing with. 17.26 -testFirstFn("(function f() { function g() { var o; with(o) {} } })", 'needsParentScope', 'needsCallee', 'needsScope', 'hasDeepWithOrEval', 'varsInScope') 17.27 +testFirstFn("(function f() { function g() { var o; with(o) {} } })", 'needsParentScope', 'needsCallee', 'needsScope', 'hasDeepWithOrEval', 'allVarsInScope') 17.28 // Nested function using "eval" is almost the same as parent function using 17.29 // eval, but at least the parent doesn't have to be vararg. 17.30 -testFirstFn("(function f() { function g() { eval() } })", 'needsParentScope', 'needsCallee', 'needsScope', 'hasDeepWithOrEval', 'varsInScope') 17.31 +testFirstFn("(function f() { function g() { eval() } })", 'needsParentScope', 'needsCallee', 'needsScope', 'hasDeepWithOrEval', 'allVarsInScope') 17.32 17.33 // Function with 250 named parameters is ordinary 17.34 testFirstFn("function f(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, p30, p31, p32, p33, p34, p35, p36, p37, p38, p39, p40, p41, p42, p43, p44, p45, p46, p47, p48, p49, p50, p51, p52, p53, p54, p55, p56, p57, p58, p59, p60, p61, p62, p63, p64, p65, p66, p67, p68, p69, p70, p71, p72, p73, p74, p75, p76, p77, p78, p79, p80, p81, p82, p83, p84, p85, p86, p87, p88, p89, p90, p91, p92, p93, p94, p95, p96, p97, p98, p99, p100, p101, p102, p103, p104, p105, p106, p107, p108, p109, p110, p111, p112, p113, p114, p115, p116, p117, p118, p119, p120, p121, p122, p123, p124, p125, p126, p127, p128, p129, p130, p131, p132, p133, p134, p135, p136, p137, p138, p139, p140, p141, p142, p143, p144, p145, p146, p147, p148, p149, p150, p151, p152, p153, p154, p155, p156, p157, p158, p159, p160, p161, p162, p163, p164, p165, p166, p167, p168, p169, p170, p171, p172, p173, p174, p175, p176, p177, p178, p179, p180, p181, p182, p183, p184, p185, p186, p187, p188, p189, p190, p191, p192, p193, p194, p195, p196, p197, p198, p199, p200, p201, p202, p203, p204, p205, p206, p207, p208, p209, p210, p211, p212, p213, p214, p215, p216, p217, p218, p219, p220, p221, p222, p223, p224, p225, p226, p227, p228, p229, p230, p231, p232, p233, p234, p235, p236, p237, p238, p239, p240, p241, p242, p243, p244, p245, p246, p247, p248, p249, p250) { p250 = p249 }")