8008648: Lazy JIT scope and callee semantics bugfixes. Broke out wallclock timer.
Reviewed-by: attila, hannesw
--- a/nashorn/src/jdk/internal/dynalink/beans/BeansLinker.java Wed Feb 20 16:43:21 2013 +0100
+++ b/nashorn/src/jdk/internal/dynalink/beans/BeansLinker.java Thu Feb 21 16:57:21 2013 +0100
@@ -159,7 +159,7 @@
return linkers.get(clazz);
}
- /*
+ /**
* Returns true if the object is a Dynalink Java dynamic method.
*
* @param obj the object we want to test for being a dynamic method
--- a/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java Wed Feb 20 16:43:21 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java Thu Feb 21 16:57:21 2013 +0100
@@ -850,7 +850,7 @@
* Determine if function is varargs and consequently variables have to
* be in the scope.
*/
- final boolean varsInScope = function.varsInScope();
+ final boolean varsInScope = function.allVarsInScope();
// TODO for LET we can do better: if *block* does not contain any eval/with, we don't need its vars in scope.
@@ -2040,7 +2040,6 @@
}
final Symbol varSymbol = varNode.getSymbol();
-
assert varSymbol != null : "variable node " + varNode + " requires a symbol";
assert method != null;
@@ -2058,7 +2057,7 @@
}
final IdentNode identNode = varNode.getName();
final Type type = identNode.getType();
- if(varSymbol.isFastScope(getCurrentFunctionNode())) {
+ if (varSymbol.isFastScope(getCurrentFunctionNode())) {
storeFastScopeVar(type, varSymbol, flags);
} else {
method.dynamicSet(type, identNode.getName(), flags);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CompilationException.java Thu Feb 21 16:57:21 2013 +0100
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.nashorn.internal.codegen;
+
+/**
+ * Exception when something in the compiler breaks down. Can only
+ * be instantiated by the codegen package
+ */
+@SuppressWarnings("serial")
+public class CompilationException extends RuntimeException {
+
+ CompilationException(final String description) {
+ super(description);
+ }
+
+ CompilationException(final Exception cause) {
+ super(cause);
+ }
+}
--- a/nashorn/src/jdk/nashorn/internal/codegen/CompilationPhase.java Wed Feb 20 16:43:21 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CompilationPhase.java Thu Feb 21 16:57:21 2013 +0100
@@ -12,15 +12,21 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.Set;
+
import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.ir.CallNode;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.ir.Node;
+import jdk.nashorn.internal.ir.ReferenceNode;
+import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.ir.debug.ASTWriter;
import jdk.nashorn.internal.ir.debug.PrintVisitor;
-import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.ECMAErrors;
+import jdk.nashorn.internal.runtime.Timing;
/**
* A compilation phase is a step in the processes of turning a JavaScript FunctionNode
@@ -35,7 +41,7 @@
*/
LAZY_INITIALIZATION_PHASE(EnumSet.of(FunctionNode.CompilationState.INITIALIZED)) {
@Override
- boolean transform(final Compiler compiler, final FunctionNode fn) {
+ void transform(final Compiler compiler, final FunctionNode fn) {
/*
* For lazy compilation, we might be given a node previously marked as lazy
@@ -53,15 +59,48 @@
outermostFunctionNode.setIsLazy(false);
outermostFunctionNode.setReturnType(Type.UNKNOWN);
+ final Set<FunctionNode> neverLazy = new HashSet<>();
+ final Set<FunctionNode> lazy = new HashSet<>();
+
outermostFunctionNode.accept(new NodeVisitor() {
+ // self references are done with invokestatic and thus cannot have trampolines - never lazy
+ @Override
+ public Node enter(final CallNode node) {
+ final Node callee = node.getFunction();
+ if (callee instanceof ReferenceNode) {
+ neverLazy.add(((ReferenceNode)callee).getReference());
+ return null;
+ }
+ return node;
+ }
+
@Override
public Node enter(final FunctionNode node) {
+ if (node == outermostFunctionNode) {
+ return node;
+ }
assert Compiler.LAZY_JIT;
- node.setIsLazy(node != outermostFunctionNode);
+ lazy.add(node);
+
return node;
}
});
- return true;
+
+ for (final FunctionNode node : neverLazy) {
+ Compiler.LOG.fine("Marking " + node.getName() + " as non lazy, as it's a self reference");
+ node.setIsLazy(false);
+ lazy.remove(node);
+ }
+
+ for (final FunctionNode node : lazy) {
+ Compiler.LOG.fine("Marking " + node.getName() + " as lazy");
+ node.setIsLazy(true);
+ final FunctionNode parent = node.findParentFunction();
+ if (parent != null) {
+ Compiler.LOG.fine("Marking " + parent.getName() + " as having lazy children - it needs scope for all variables");
+ parent.setHasLazyChildren();
+ }
+ }
}
@Override
@@ -76,9 +115,8 @@
*/
CONSTANT_FOLDING_PHASE(EnumSet.of(INITIALIZED), CONSTANT_FOLDED) {
@Override
- boolean transform(final Compiler compiler, final FunctionNode fn) {
+ void transform(final Compiler compiler, final FunctionNode fn) {
fn.accept(new FoldConstants());
- return true;
}
@Override
@@ -98,9 +136,8 @@
*/
LOWERING_PHASE(EnumSet.of(INITIALIZED, CONSTANT_FOLDED), LOWERED) {
@Override
- boolean transform(final Compiler compiler, final FunctionNode fn) {
+ void transform(final Compiler compiler, final FunctionNode fn) {
fn.accept(new Lower());
- return true;
}
@Override
@@ -115,20 +152,17 @@
*/
ATTRIBUTION_PHASE(EnumSet.of(INITIALIZED, CONSTANT_FOLDED, LOWERED), ATTR) {
@Override
- boolean transform(final Compiler compiler, final FunctionNode fn) {
+ void transform(final Compiler compiler, final FunctionNode fn) {
final Context context = compiler.getContext();
- try {
- fn.accept(new Attr(context));
- return true;
- } finally {
- if (context._print_lower_ast) {
- context.getErr().println(new ASTWriter(fn));
- }
- if (context._print_lower_parse) {
- context.getErr().println(new PrintVisitor(fn));
- }
+ fn.accept(new Attr(context));
+ if (context._print_lower_ast) {
+ context.getErr().println(new ASTWriter(fn));
}
+
+ if (context._print_lower_parse) {
+ context.getErr().println(new PrintVisitor(fn));
+ }
}
@Override
@@ -146,7 +180,7 @@
*/
SPLITTING_PHASE(EnumSet.of(INITIALIZED, CONSTANT_FOLDED, LOWERED, ATTR), SPLIT) {
@Override
- boolean transform(final Compiler compiler, final FunctionNode fn) {
+ void transform(final Compiler compiler, final FunctionNode fn) {
final CompileUnit outermostCompileUnit = compiler.addCompileUnit(compiler.firstCompileUnitName());
new Splitter(compiler, fn, outermostCompileUnit).split();
@@ -157,7 +191,6 @@
assert compiler.getStrictMode();
compiler.setStrictMode(true);
}
- return true;
}
@Override
@@ -181,9 +214,8 @@
*/
TYPE_FINALIZATION_PHASE(EnumSet.of(INITIALIZED, CONSTANT_FOLDED, LOWERED, ATTR, SPLIT), FINALIZED) {
@Override
- boolean transform(final Compiler compiler, final FunctionNode fn) {
+ void transform(final Compiler compiler, final FunctionNode fn) {
fn.accept(new FinalizeTypes());
- return true;
}
@Override
@@ -199,7 +231,7 @@
*/
BYTECODE_GENERATION_PHASE(EnumSet.of(INITIALIZED, CONSTANT_FOLDED, LOWERED, ATTR, SPLIT, FINALIZED), EMITTED) {
@Override
- boolean transform(final Compiler compiler, final FunctionNode fn) {
+ void transform(final Compiler compiler, final FunctionNode fn) {
final Context context = compiler.getContext();
try {
@@ -253,7 +285,7 @@
final File dir = new File(fileName.substring(0, index));
try {
if (!dir.exists() && !dir.mkdirs()) {
- throw new IOException();
+ throw new IOException(dir.toString());
}
final File file = new File(context._dest_dir, fileName);
try (final FileOutputStream fos = new FileOutputStream(file)) {
@@ -265,8 +297,6 @@
}
}
}
-
- return true;
}
@Override
@@ -281,8 +311,6 @@
private long endTime;
private boolean isFinished;
- private static final long[] accumulatedTime = new long[CompilationPhase.values().length];
-
private CompilationPhase(final EnumSet<CompilationState> pre) {
this(pre, null);
}
@@ -313,7 +341,7 @@
protected void end(final FunctionNode functionNode) {
endTime = System.currentTimeMillis();
- accumulatedTime[ordinal()] += (endTime - startTime);
+ Timing.accumulateTime(toString(), endTime - startTime);
if (post != null) {
functionNode.setState(post);
@@ -334,23 +362,15 @@
return endTime;
}
- public static long getAccumulatedTime(final CompilationPhase phase) {
- return accumulatedTime[phase.ordinal()];
- }
-
- abstract boolean transform(final Compiler compiler, final FunctionNode functionNode);
+ abstract void transform(final Compiler compiler, final FunctionNode functionNode) throws CompilationException;
- final boolean apply(final Compiler compiler, final FunctionNode functionNode) {
- try {
- if (!isApplicable(functionNode)) {
- return false;
- }
- begin(functionNode);
- transform(compiler, functionNode);
- return true;
- } finally {
- end(functionNode);
+ final void apply(final Compiler compiler, final FunctionNode functionNode) throws CompilationException {
+ if (!isApplicable(functionNode)) {
+ throw new CompilationException("compile phase not applicable: " + this);
}
+ begin(functionNode);
+ transform(compiler, functionNode);
+ end(functionNode);
}
}
--- a/nashorn/src/jdk/nashorn/internal/codegen/Compiler.java Wed Feb 20 16:43:21 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Compiler.java Thu Feb 21 16:57:21 2013 +0100
@@ -54,6 +54,7 @@
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.DebugLogger;
import jdk.nashorn.internal.runtime.Source;
+import jdk.nashorn.internal.runtime.Timing;
import jdk.nashorn.internal.runtime.options.Options;
/**
@@ -72,8 +73,6 @@
static final boolean LAZY_JIT = Options.getBooleanProperty("nashorn.compiler.lazy");
- static final boolean TIME_COMPILATION = Options.getBooleanProperty("nashorn.compiler.time");
-
private final Map<String, byte[]> bytecode;
private final Set<CompileUnit> compileUnits;
@@ -92,7 +91,9 @@
private CodeInstaller<Context> installer;
- static final DebugLogger LOG = new DebugLogger("compiler");
+ /** logger for compiler, trampolines, splits and related code generation events
+ * that affect classes */
+ public static final DebugLogger LOG = new DebugLogger("compiler");
/**
* This array contains names that need to be reserved at the start
@@ -179,6 +180,13 @@
SEQUENCE_LAZY :
SEQUENCE_NORMAL;
+ private static String lazyTag(final FunctionNode functionNode) {
+ if (functionNode.isLazy()) {
+ return '$' + LAZY.tag() + '$' + functionNode.getName();
+ }
+ return "";
+ }
+
/**
* Constructor
*
@@ -187,6 +195,7 @@
* @param sequence {@link Compiler#CompilationSequence} of {@link CompilationPhase}s to apply as this compilation
* @param strict should this compilation use strict mode semantics
*/
+ //TODO support an array of FunctionNodes for batch lazy compilation
Compiler(final Context context, final CodeInstaller<Context> installer, final FunctionNode functionNode, final CompilationSequence sequence, final boolean strict) {
this.context = context;
this.functionNode = functionNode;
@@ -198,14 +207,16 @@
this.bytecode = new HashMap<>();
final StringBuilder sb = new StringBuilder();
- sb.append(functionNode.uniqueName(DEFAULT_SCRIPT_NAME.tag())).
+ sb.append(functionNode.uniqueName(DEFAULT_SCRIPT_NAME.tag() + lazyTag(functionNode))).
append('$').
- append(safeSourceName(functionNode.getSource())).
- append(functionNode.isLazy() ? LAZY.tag() : "");
+ append(safeSourceName(functionNode.getSource()));
this.scriptName = sb.toString();
- LOG.info("Initializing compiler for scriptName = " + scriptName + ", root function: '" + functionNode.getName() + "'");
+ LOG.info("Initializing compiler for '" + functionNode.getName() + "' scriptName = " + scriptName + ", root function: '" + functionNode.getName() + "'");
+ if (functionNode.isLazy()) {
+ LOG.info(">>> This is a lazy recompilation triggered by a trampoline");
+ }
}
/**
@@ -241,22 +252,23 @@
/**
* Execute the compilation this Compiler was created with
- * @return true if compilation succeeds.
+ * @throws CompilationException if something goes wrong
*/
- public boolean compile() {
+ public void compile() throws CompilationException {
for (final String reservedName : RESERVED_NAMES) {
functionNode.uniqueName(reservedName);
}
for (final CompilationPhase phase : sequence) {
- LOG.info("Entering compile phase " + phase + " for function '" + functionNode.getName() + "'");
- if (phase.isApplicable(functionNode)) {
- if (!phase.apply(this, functionNode)) { //TODO exceptions, error logging
- return false;
- }
+ phase.apply(this, functionNode);
+ final String end = phase.toString() + " done for function '" + functionNode.getName() + "'";
+ if (Timing.isEnabled()) {
+ final long duration = phase.getEndTime() - phase.getStartTime();
+ LOG.info(end + " in " + duration + " ms");
+ } else {
+ LOG.info(end);
}
}
- return true;
}
/**
@@ -264,11 +276,15 @@
* @return root script class - if there are several compile units they will also be installed
*/
public Class<?> install() {
+ final long t0 = Timing.isEnabled() ? System.currentTimeMillis() : 0L;
+
+ assert functionNode.hasState(CompilationState.EMITTED) : functionNode.getName() + " has no bytecode and cannot be installed";
+
Class<?> rootClass = null;
for (final Entry<String, byte[]> entry : bytecode.entrySet()) {
final String className = entry.getKey();
- LOG.info("Installing class " + className);
+ LOG.fine("Installing class " + className);
final byte[] code = entry.getValue();
final Class<?> clazz = installer.install(Compiler.binaryName(className), code);
@@ -299,7 +315,13 @@
}
}
- LOG.info("Root class: " + rootClass);
+ LOG.info("Installed root class: " + rootClass + " and " + bytecode.size() + " compile unit classes");
+ if (Timing.isEnabled()) {
+ final long duration = System.currentTimeMillis() - t0;
+ Timing.accumulateTime("[Code Installation]", duration);
+ LOG.info("Installation time: " + duration + " ms");
+ }
+
return rootClass;
}
@@ -376,7 +398,7 @@
private CompileUnit addCompileUnit(final String unitClassName, final long initialWeight) {
final CompileUnit compileUnit = initCompileUnit(unitClassName, initialWeight);
compileUnits.add(compileUnit);
- LOG.info("Added compile unit " + compileUnit);
+ LOG.fine("Added compile unit " + compileUnit);
return compileUnit;
}
@@ -438,24 +460,4 @@
assert !USE_INT_ARITH : "Integer arithmetic is not enabled";
}
- static {
- if (TIME_COMPILATION) {
- Runtime.getRuntime().addShutdownHook(new Thread() {
- @Override
- public void run() {
- for (final CompilationPhase phase : CompilationPhase.values()) {
- final StringBuilder sb = new StringBuilder();
- sb.append(phase);
- while (sb.length() < 32) {
- sb.append(' ');
- }
- sb.append(CompilationPhase.getAccumulatedTime(phase));
- sb.append(' ');
- sb.append(" ms");
- System.err.println(sb.toString()); //Context err is gone by shutdown TODO
- }
- }
- });
- }
- }
}
--- a/nashorn/src/jdk/nashorn/internal/codegen/FinalizeTypes.java Wed Feb 20 16:43:21 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/codegen/FinalizeTypes.java Thu Feb 21 16:57:21 2013 +0100
@@ -562,7 +562,7 @@
final FunctionNode functionNode = block.getFunction();
final List<Symbol> symbols = block.getFrame().getSymbols();
- final boolean allVarsInScope = functionNode.varsInScope();
+ final boolean allVarsInScope = functionNode.allVarsInScope();
final boolean isVarArg = functionNode.isVarArg();
for (final Symbol symbol : symbols) {
--- a/nashorn/src/jdk/nashorn/internal/codegen/Lower.java Wed Feb 20 16:43:21 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Lower.java Thu Feb 21 16:57:21 2013 +0100
@@ -709,7 +709,6 @@
* @return eval location
*/
private static String evalLocation(final IdentNode node) {
- //final StringBuilder sb = new StringBuilder(node.getSource().getName());
return new StringBuilder().
append(node.getSource().getName()).
append('#').
--- a/nashorn/src/jdk/nashorn/internal/codegen/MethodEmitter.java Wed Feb 20 16:43:21 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/codegen/MethodEmitter.java Thu Feb 21 16:57:21 2013 +0100
@@ -203,7 +203,7 @@
@Override
public String toString() {
- return "methodEmitter: " + (functionNode == null ? method : functionNode.getName()).toString();
+ return "methodEmitter: " + (functionNode == null ? method : functionNode.getName()).toString() + ' ' + stack;
}
/**
--- a/nashorn/src/jdk/nashorn/internal/codegen/Splitter.java Wed Feb 20 16:43:21 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Splitter.java Thu Feb 21 16:57:21 2013 +0100
@@ -92,15 +92,15 @@
*/
void split() {
if (functionNode.isLazy()) {
- LOG.info("Postponing split of '" + functionNode.getName() + "' as it's lazy");
+ LOG.fine("Postponing split of '" + functionNode.getName() + "' as it's lazy");
return;
}
- LOG.info("Initiating split of '" + functionNode.getName() + "'");
+ LOG.fine("Initiating split of '" + functionNode.getName() + "'");
long weight = WeighNodes.weigh(functionNode);
if (weight >= SPLIT_THRESHOLD) {
- LOG.info("Splitting '" + functionNode.getName() + "' as its weight " + weight + " exceeds split threshold " + SPLIT_THRESHOLD);
+ LOG.fine("Splitting '" + functionNode.getName() + "' as its weight " + weight + " exceeds split threshold " + SPLIT_THRESHOLD);
functionNode.accept(this);
--- a/nashorn/src/jdk/nashorn/internal/ir/FunctionNode.java Wed Feb 20 16:43:21 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/FunctionNode.java Thu Feb 21 16:57:21 2013 +0100
@@ -188,25 +188,27 @@
private static final int IS_LOWERED = 0b0000_0000_0001_0000;
/** Has this node been split because it was too large? */
private static final int IS_SPLIT = 0b0000_0000_0010_0000;
- /** Is this function lazily compiled? */
- private static final int IS_LAZY = 0b0000_0000_0100_0000;
/** Does the function call eval? */
- private static final int HAS_EVAL = 0b0000_0000_1000_0000;
+ private static final int HAS_EVAL = 0b0000_0000_0100_0000;
/** Does the function contain a with block ? */
- private static final int HAS_WITH = 0b0000_0001_0000_0000;
+ private static final int HAS_WITH = 0b0000_0000_1000_0000;
/** Does a descendant function contain a with or eval? */
- private static final int HAS_DESCENDANT_WITH_OR_EVAL = 0b0000_0010_0000_0000;
+ private static final int HAS_DESCENDANT_WITH_OR_EVAL = 0b0000_0001_0000_0000;
/** Does the function define "arguments" identifier as a parameter of nested function name? */
- private static final int DEFINES_ARGUMENTS = 0b0000_0100_0000_0000;
+ private static final int DEFINES_ARGUMENTS = 0b0000_0010_0000_0000;
/** Does the function need a self symbol? */
- private static final int NEEDS_SELF_SYMBOL = 0b0000_1000_0000_0000;
+ private static final int NEEDS_SELF_SYMBOL = 0b0000_0100_0000_0000;
/** Does this function or any of its descendants use variables from an ancestor function's scope (incl. globals)? */
- private static final int USES_ANCESTOR_SCOPE = 0b0001_0000_0000_0000;
+ private static final int USES_ANCESTOR_SCOPE = 0b0000_1000_0000_0000;
+ /** Is this function lazily compiled? */
+ private static final int IS_LAZY = 0b0001_0000_0000_0000;
+ /** Does this function have lazy, yet uncompiled children */
+ private static final int HAS_LAZY_CHILDREN = 0b0010_0000_0000_0000;
/** Does this function or any nested functions contain a with or an eval? */
private static final int HAS_DEEP_WITH_OR_EVAL = HAS_EVAL | HAS_WITH | HAS_DESCENDANT_WITH_OR_EVAL;
/** Does this function need to store all its variables in scope? */
- private static final int HAS_ALL_VARS_IN_SCOPE = HAS_DEEP_WITH_OR_EVAL | IS_SPLIT;
+ private static final int HAS_ALL_VARS_IN_SCOPE = HAS_DEEP_WITH_OR_EVAL | IS_SPLIT | HAS_LAZY_CHILDREN;
/** Does this function potentially need "arguments"? Note that this is not a full test, as further negative check of REDEFINES_ARGS is needed. */
private static final int MAYBE_NEEDS_ARGUMENTS = USES_ARGUMENTS | HAS_EVAL;
/** 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. */
@@ -862,7 +864,7 @@
*
* @return true if all variables should be in scope
*/
- public boolean varsInScope() {
+ public boolean allVarsInScope() {
return isScript() || (flags & HAS_ALL_VARS_IN_SCOPE) != 0;
}
@@ -884,6 +886,23 @@
}
/**
+ * Checks if this function has yet-to-be-generated child functions
+ *
+ * @return true if there are lazy child functions
+ */
+ public boolean hasLazyChildren() {
+ return (flags & HAS_LAZY_CHILDREN) != 0;
+ }
+
+ /**
+ * Flag this function node as having yet-to-be-generated child functions
+ */
+ public void setHasLazyChildren() {
+ this.flags |= HAS_LAZY_CHILDREN;
+ setNeedsScope();
+ }
+
+ /**
* Get the parameters to this function
* @return a list of IdentNodes which represent the function parameters, in order
*/
--- a/nashorn/src/jdk/nashorn/internal/ir/TernaryNode.java Wed Feb 20 16:43:21 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/ir/TernaryNode.java Thu Feb 21 16:57:21 2013 +0100
@@ -54,7 +54,7 @@
private TernaryNode(final TernaryNode ternaryNode, final CopyState cs) {
super(ternaryNode, cs);
- third = cs.existingOrCopy(ternaryNode.third);
+ this.third = cs.existingOrCopy(ternaryNode.third);
}
@Override
--- a/nashorn/src/jdk/nashorn/internal/objects/ScriptFunctionTrampolineImpl.java Wed Feb 20 16:43:21 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/objects/ScriptFunctionTrampolineImpl.java Thu Feb 21 16:57:21 2013 +0100
@@ -5,6 +5,7 @@
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
+import jdk.nashorn.internal.codegen.CompilationException;
import jdk.nashorn.internal.codegen.Compiler;
import jdk.nashorn.internal.codegen.FunctionSignature;
import jdk.nashorn.internal.codegen.types.Type;
@@ -77,12 +78,10 @@
return super.makeBoundFunction(data);
}
- private MethodHandle compile() {
+ private MethodHandle compile() throws CompilationException {
final Compiler compiler = new Compiler(installer, functionNode);
- if (!compiler.compile()) {
- assert false : "compilation error in trampoline for " + functionNode.getName();
- return null;
- }
+
+ compiler.compile();
final Class<?> clazz = compiler.install();
/* compute function signature for lazy method. this can be done first after compilation, as only then do we know
@@ -91,7 +90,9 @@
final MethodType mt = signature.getMethodType();
MethodHandle mh = MH.findStatic(MethodHandles.publicLookup(), clazz, functionNode.getName(), mt);
- mh = MH.bindTo(mh, this);
+ if (functionNode.needsCallee()) {
+ mh = MH.bindTo(mh, this);
+ }
// now the invoker method looks like the one our superclass is expecting
resetInvoker(mh);
@@ -100,11 +101,11 @@
}
@SuppressWarnings("unused")
- private Object trampoline(final Object... args) {
- /** Create a new compiler for the lazy node, using the same installation policy as the old one */
-
+ private Object trampoline(final Object... args) throws CompilationException {
+ Compiler.LOG.info(">>> TRAMPOLINE: Hitting trampoline for '" + functionNode.getName() + "'");
MethodHandle mh = compile();
+ Compiler.LOG.info("<<< COMPILED TO: " + mh);
// spread the array to invididual args of the correct type
mh = MH.asSpreader(mh, Object[].class, mh.type().parameterCount());
--- a/nashorn/src/jdk/nashorn/internal/parser/Parser.java Wed Feb 20 16:43:21 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/parser/Parser.java Thu Feb 21 16:57:21 2013 +0100
@@ -97,15 +97,16 @@
import jdk.nashorn.internal.ir.WhileNode;
import jdk.nashorn.internal.ir.WithNode;
import jdk.nashorn.internal.runtime.Context;
+import jdk.nashorn.internal.runtime.DebugLogger;
import jdk.nashorn.internal.runtime.ErrorManager;
import jdk.nashorn.internal.runtime.JSErrorType;
import jdk.nashorn.internal.runtime.ParserException;
import jdk.nashorn.internal.runtime.ScriptingFunctions;
import jdk.nashorn.internal.runtime.Source;
+import jdk.nashorn.internal.runtime.Timing;
/**
* Builds the IR.
- *
*/
public class Parser extends AbstractParser {
/** Current context. */
@@ -126,6 +127,8 @@
/** Namespace for function names where not explicitly given */
private final Namespace namespace;
+ private static DebugLogger LOG = new DebugLogger("parser");
+
/**
* Constructor
*
@@ -176,6 +179,9 @@
* @return function node resulting from successful parse
*/
public FunctionNode parse(final String scriptName) {
+ final long t0 = Timing.isEnabled() ? System.currentTimeMillis() : 0L;
+ LOG.info(this + " begin for '" + scriptName + "'");
+
try {
stream = new TokenStream();
lexer = new Lexer(source, stream, scripting && !context._no_syntax_extensions);
@@ -208,6 +214,14 @@
}
return null;
+ } finally {
+ final String end = this + " end '" + scriptName + "'";
+ if (Timing.isEnabled()) {
+ Timing.accumulateTime(toString(), System.currentTimeMillis() - t0);
+ LOG.info(end + "' in " + (System.currentTimeMillis() - t0) + " ms");
+ } else {
+ LOG.info(end);
+ }
}
}
@@ -3064,4 +3078,8 @@
return null;
}
+ @Override
+ public String toString() {
+ return "[JavaScript Parsing]";
+ }
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/NashornLoader.java Wed Feb 20 16:43:21 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/runtime/NashornLoader.java Thu Feb 21 16:57:21 2013 +0100
@@ -57,7 +57,7 @@
* @param name name of the class to be loaded
* @param resolve whether the class should be resolved or not
* @return Class object
- * @throws ClassNotFoundException
+ * @throws ClassNotFoundException if class cannot be loaded
*/
protected final Class<?> loadClassTrusted(final String name, final boolean resolve) throws ClassNotFoundException {
return super.loadClass(name, resolve);
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptingFunctions.java Wed Feb 20 16:43:21 2013 +0100
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptingFunctions.java Thu Feb 21 16:57:21 2013 +0100
@@ -121,11 +121,11 @@
*
* @param self self reference
* @param string string to execute
- * @param input
+ * @param input input
*
* @return output string from the request
- * @throws IOException
- * @throws InterruptedException
+ * @throws IOException if any stream access fails
+ * @throws InterruptedException if execution is interrupted
*/
public static Object exec(final Object self, final Object string, final Object input) throws IOException, InterruptedException {
// Current global is need to fetch additional inputs and for additional results.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/runtime/Timing.java Thu Feb 21 16:57:21 2013 +0100
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.nashorn.internal.runtime;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import jdk.nashorn.internal.runtime.options.Options;
+
+/**
+ * Simple wallclock timing framework
+ */
+public final class Timing {
+ private static final boolean ENABLED = Options.getBooleanProperty("nashorn.time");
+ private static final Map<String, Long> TIMINGS;
+ private static final long START_TIME;
+
+ static {
+ if (ENABLED) {
+ TIMINGS = new LinkedHashMap<>();
+ START_TIME = System.currentTimeMillis();
+ Runtime.getRuntime().addShutdownHook(new Thread() {
+ @Override
+ public void run() {
+ final long t = System.currentTimeMillis();
+ long knownTime = 0L;
+ int maxLength = 0;
+
+ for (final Map.Entry<String, Long> entry : TIMINGS.entrySet()) {
+ maxLength = Math.max(maxLength, entry.getKey().length());
+ }
+ maxLength++;
+
+ for (final Map.Entry<String, Long> entry : TIMINGS.entrySet()) {
+ final StringBuilder sb = new StringBuilder();
+
+ sb.append(entry.getKey());
+ while (sb.length() < maxLength) {
+ sb.append(' ');
+ }
+
+ final long duration = entry.getValue();
+ sb.append(duration);
+ sb.append(' ');
+ sb.append(" ms");
+
+ knownTime += duration;
+
+ System.err.println(sb.toString()); //Context err is gone by shutdown TODO
+ }
+
+ final long total = t - START_TIME;
+ System.err.println("Total runtime: " + total + " ms (Non-runtime: " + knownTime + " ms [" + (int)(knownTime * 100.0 / total) + "%])");
+ }
+ });
+ } else {
+ TIMINGS = null;
+ START_TIME = 0L;
+ }
+ }
+
+ /**
+ * Check if timing is inabled
+ * @return true if timing is enabled
+ */
+ public static boolean isEnabled() {
+ return ENABLED;
+ }
+
+ /**
+ * When timing, this can be called to register a new module for timing
+ * or add to its accumulated time
+ *
+ * @param module module name
+ * @param duration duration to add to accumulated time for module
+ */
+ public static void accumulateTime(final String module, final long duration) {
+ if (Timing.isEnabled()) {
+ Long accumulatedTime = TIMINGS.get(module);
+ if (accumulatedTime == null) {
+ accumulatedTime = 0L;
+ }
+ TIMINGS.put(module, accumulatedTime + duration);
+ }
+ }
+
+}
--- a/nashorn/test/script/trusted/JDK-8006529.js Wed Feb 20 16:43:21 2013 +0100
+++ b/nashorn/test/script/trusted/JDK-8006529.js Thu Feb 21 16:57:21 2013 +0100
@@ -60,7 +60,7 @@
var getFunctionsMethod = FunctionNode.class.getMethod("getFunctions");
// These are method names of methods in FunctionNode class
-var allAssertionList = ['isVarArg', 'needsParentScope', 'needsCallee', 'needsScope', 'needsSelfSymbol', 'isSplit', 'hasEval', 'hasWith', 'hasDeepWithOrEval', 'varsInScope', 'isStrictMode']
+var allAssertionList = ['isVarArg', 'needsParentScope', 'needsCallee', 'needsScope', 'needsSelfSymbol', 'isSplit', 'hasEval', 'hasWith', 'hasDeepWithOrEval', 'allVarsInScope', 'isStrictMode']
// corresponding Method objects of FunctionNode class
var functionNodeMethods = {};
@@ -189,18 +189,18 @@
// and all variables in scope. Actually, we could make "with" less wasteful,
// and only put those variables in scope that it actually references, similar
// to what nested functions do with variables in their parents.
-testFirstFn("(function f() { var o; with(o) {} })", 'needsParentScope', 'needsCallee', 'needsScope', 'hasWith', 'hasDeepWithOrEval', 'varsInScope')
+testFirstFn("(function f() { var o; with(o) {} })", 'needsParentScope', 'needsCallee', 'needsScope', 'hasWith', 'hasDeepWithOrEval', 'allVarsInScope')
// Using "eval" is as bad as using "with" with the added requirement of
// being vararg, 'cause we don't know if eval will be using "arguments".
-testFirstFn("(function f() { eval() })", 'needsParentScope', 'needsCallee', 'needsScope', 'hasEval', 'isVarArg', 'hasDeepWithOrEval', 'varsInScope')
+testFirstFn("(function f() { eval() })", 'needsParentScope', 'needsCallee', 'needsScope', 'hasEval', 'isVarArg', 'hasDeepWithOrEval', 'allVarsInScope')
// Nested function using "with" is pretty much the same as the parent
// function needing with.
-testFirstFn("(function f() { function g() { var o; with(o) {} } })", 'needsParentScope', 'needsCallee', 'needsScope', 'hasDeepWithOrEval', 'varsInScope')
+testFirstFn("(function f() { function g() { var o; with(o) {} } })", 'needsParentScope', 'needsCallee', 'needsScope', 'hasDeepWithOrEval', 'allVarsInScope')
// Nested function using "eval" is almost the same as parent function using
// eval, but at least the parent doesn't have to be vararg.
-testFirstFn("(function f() { function g() { eval() } })", 'needsParentScope', 'needsCallee', 'needsScope', 'hasDeepWithOrEval', 'varsInScope')
+testFirstFn("(function f() { function g() { eval() } })", 'needsParentScope', 'needsCallee', 'needsScope', 'hasDeepWithOrEval', 'allVarsInScope')
// Function with 250 named parameters is ordinary
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 }")