diff --git a/nashorn/.hgignore b/nashorn/.hgignore index 02ec40d7e8d..7ee241994a9 100644 --- a/nashorn/.hgignore +++ b/nashorn/.hgignore @@ -26,3 +26,5 @@ jcov2/* test/lib/testng.jar test/script/external/* .project +.externalToolBuilders/* +.settings/* diff --git a/nashorn/bin/runopt.sh b/nashorn/bin/runopt.sh new file mode 100644 index 00000000000..0a61491e624 --- /dev/null +++ b/nashorn/bin/runopt.sh @@ -0,0 +1,137 @@ +#!/bin/sh +# +# Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# 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. +# + +########################################################################################### +# This is a helper script to evaluate nashorn with optimistic types +# it produces a flight recording for every run, and uses the best +# known flags for performance for the current configration +########################################################################################### + +# Flags to enable assertions, we need the system assertions too, since +# this script runs Nashorn in the BCP to override any nashorn.jar that might +# reside in your $JAVA_HOME/jre/lib/ext/nashorn.jar +# +ENABLE_ASSERTIONS_FLAGS="-ea -esa" + +# Flags to instrument lambdaform computation, caching, interpretation and compilation +# Default compile threshold for lambdaforms is 30 +# +#LAMBDAFORM_FLAGS="\ +# -Djava.lang.invoke.MethodHandle.COMPILE_THRESHOLD=3 \ +# -Djava.lang.invoke.MethodHandle.DUMP_CLASS_FILES=true \ +# -Djava.lang.invoke.MethodHandle.TRACE_METHOD_LINKAGE=true \ +# -Djava.lang.invoke.MethodHandle.TRACE_INTERPRETER=true" + +# Flags to run trusted tests from the Nashorn test suite +# +#TRUSTED_TEST_FLAGS="\ +#-Djava.security.manager \ +#-Djava.security.policy=../build/nashorn.policy -Dnashorn.debug" + +# Testing out new code optimizations using the generic hotspot "new code" parameter +# +#USE_NEW_CODE_FLAGS=-XX:+UnlockDiagnosticVMOptions -XX:+UseNewCode + +# +#-Dnashorn.typeInfo.disabled=false \ +# and for Nashorn options: +# --class-cache-size=0 --persistent-code-cache=false + +# Unique timestamped file name for JFR recordings. For JFR, we also have to +# crank up the stack cutoff depth to 1024, because of ridiculously long lambda form +# stack traces. +# +# It is also recommended that you go into $JAVA_HOME/jre/lib/jfr/default.jfc and +# set the "method-sampling-interval" Normal and Maximum sample time as low as you +# can go (10 ms on most platforms). The default is normally higher. The increased +# sampling overhead is usually negligible for Nashorn runs, but the data is better + +if [ -z $JFR_FILENAME ]; then + JFR_FILENAME="./nashorn_$(date|sed "s/ /_/g"|sed "s/:/_/g").jfr" + echo "Using default JFR filename: ${JFR_FILENAME}..." +fi + +# Flight recorder +# +# see above - already in place, copy the flags down here to disable +ENABLE_FLIGHT_RECORDER_FLAGS="\ + -XX:+UnlockCommercialFeatures \ + -XX:+FlightRecorder \ + -XX:FlightRecorderOptions=defaultrecording=true,disk=true,dumponexit=true,dumponexitpath=$JFR_FILENAME,stackdepth=1024" + +# Type specialization and math intrinsic replacement should be enabled by default in 8u20 and nine, +# keeping this flag around for experimental reasons. Replace + with - to switch it off +# +#ENABLE_TYPE_SPECIALIZATION_FLAGS=-XX:+UseTypeSpeculation + +# Same with math intrinsics. They should be enabled by default in 8u20 and 9, so +# this disables them if needed +# +#DISABLE_MATH_INTRINSICS_FLAGS=-XX:-UseMathExactIntrinsics + +# Add timing to time the compilation phases. +#ENABLE_TIME_FLAGS=--log=time + +# Add ShowHiddenFrames to get lambda form internals on the stack traces +#ENABLE_SHOW_HIDDEN_FRAMES_FLAGS=-XX:+ShowHiddenFrames + +# Add print optoassembly to get an asm dump. This requires 1) a debug build, not product, +# That tired compilation is switched off, for C2 only output and that the number of +# compiler threads is set to 1 for determinsm. +# +#PRINT_ASM_FLAGS=-XX:+PrintOptoAssembly -XX:-TieredCompilation -XX:CICompilerCount=1 \ + +# Tier compile threasholds. Default value is 10. (1-100 is useful for experiments) +#TIER_COMPILATION_THRESHOLD_FLAGS=-XX:IncreaseFirstTierCompileThresholdAt=10 + +# Directory where to look for nashorn.jar in a dist folder. The default is "..", assuming +# that we run the script from the make dir +DIR=.. +NASHORN_JAR=$DIR/dist/nashorn.jar + + +# The built Nashorn jar is placed first in the bootclasspath to override the JDK +# nashorn.jar in $JAVA_HOME/jre/lib/ext. Thus, we also need -esa, as assertions in +# nashorn count as system assertions in this configuration + +# Type profiling default level is 111, 222 adds some compile time, but is faster + +$JAVA_HOME/bin/java \ +$ENABLE_ASSERTIONS_FLAGS \ +$LAMBDAFORM_FLAGS \ +$TRUSTED_FLAGS \ +$USE_NEW_CODE_FLAGS \ +$ENABLE_SHOW_HIDDEN_FRAMES_FLAGS \ +$ENABLE_FLIGHT_RECORDER_FLAGS \ +$ENABLE_TYPE_SPECIALIZATION_FLAGS \ +$TIERED_COMPILATION_THRESOLD_FLAGS \ +$DISABLE_MATH_INTRINSICS_FLAGS \ +$PRINT_ASM_FLAGS \ +-Xbootclasspath/p:$NASHORN_JAR \ +-Xms2G -Xmx2G \ +-XX:TypeProfileLevel=222 \ +-cp $CLASSPATH:../build/test/classes/ \ +jdk.nashorn.tools.Shell $ENABLE_TIME_FLAGS ${@} + + diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/scripting/ScriptUtils.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/scripting/ScriptUtils.java index b6c4c97e547..e6e3915ab29 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/scripting/ScriptUtils.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/scripting/ScriptUtils.java @@ -25,8 +25,6 @@ package jdk.nashorn.api.scripting; -import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; - import java.lang.invoke.MethodHandle; import jdk.internal.dynalink.beans.StaticClass; import jdk.internal.dynalink.linker.LinkerServices; diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Block.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Block.java index 86a84ca6de8..e3a26893cb5 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Block.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Block.java @@ -80,11 +80,12 @@ public class Block extends Node implements BreakableNode, Terminal, Flags /** * Constructor * - * @param token token - * @param finish finish - * @param statements statements + * @param token The first token of the block + * @param finish The index of the last character + * @param flags The flags of the block + * @param statements All statements in the block */ - public Block(final long token, final int finish, final Statement... statements) { + public Block(final long token, final int finish, final int flags, final Statement... statements) { super(token, finish); this.statements = Arrays.asList(statements); @@ -92,29 +93,52 @@ public class Block extends Node implements BreakableNode, Terminal, Flags this.entryLabel = new Label("block_entry"); this.breakLabel = new Label("block_break"); final int len = statements.length; - this.flags = len > 0 && statements[len - 1].hasTerminalFlags() ? IS_TERMINAL : 0; + final int terminalFlags = len > 0 && statements[len - 1].hasTerminalFlags() ? IS_TERMINAL : 0; + this.flags = terminalFlags | flags; this.conversion = null; } + /** + * Constructs a new block + * + * @param token The first token of the block + * @param finish The index of the last character + * @param statements All statements in the block + */ + public Block(final long token, final int finish, final Statement...statements){ + this(token, finish, 0, statements); + } + + /** + * Constructs a new block + * + * @param token The first token of the block + * @param finish The index of the last character + * @param statements All statements in the block + */ + public Block(final long token, final int finish, final List statements){ + this(token, finish, 0, statements); + } + /** * Constructor * - * @param token token - * @param finish finish - * @param statements statements + * @param token The first token of the block + * @param finish The index of the last character + * @param flags The flags of the block + * @param statements All statements in the block */ - public Block(final long token, final int finish, final List statements) { - this(token, finish, statements.toArray(new Statement[statements.size()])); + public Block(final long token, final int finish, final int flags, final List statements) { + this(token, finish, flags, statements.toArray(new Statement[statements.size()])); } private Block(final Block block, final int finish, final List statements, final int flags, final Map symbols, final LocalVariableConversion conversion) { - super(block); + super(block, finish); this.statements = statements; this.flags = flags; this.symbols = new LinkedHashMap<>(symbols); //todo - symbols have no dependencies on any IR node and can as far as we understand it be shallow copied now this.entryLabel = new Label(block.entryLabel); this.breakLabel = new Label(block.breakLabel); - this.finish = finish; this.conversion = conversion; } diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ForNode.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ForNode.java index 9b4cb6d6e1c..bc864eb68bb 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ForNode.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ForNode.java @@ -54,20 +54,37 @@ public final class ForNode extends LoopNode { private final int flags; + /** + * Constructs a ForNode + * + * @param lineNumber The line number of header + * @param token The for token + * @param finish The last character of the for node + * @param body The body of the for node + * @param flags The flags + */ + public ForNode(final int lineNumber, final long token, final int finish, final Block body, final int flags){ + this(lineNumber, token, finish, body, flags, null, null, null); + } + /** * Constructor * - * @param lineNumber line number - * @param token token - * @param finish finish - * @param body body - * @param flags flags + * @param lineNumber The line number of header + * @param token The for token + * @param finish The last character of the for node + * @param body The body of the for node + * @param flags The flags + * @param init The initial expression + * @param test The test expression + * @param modify The modify expression */ - public ForNode(final int lineNumber, final long token, final int finish, final Block body, final int flags) { - super(lineNumber, token, finish, body, false); + public ForNode(final int lineNumber, final long token, final int finish, final Block body, final int flags, final Expression init, final JoinPredecessorExpression test, final JoinPredecessorExpression modify) { + super(lineNumber, token, finish, body, test, false); this.flags = flags; - this.init = null; - this.modify = null; + this.init = init; + this.modify = modify; + } private ForNode(final ForNode forNode, final Expression init, final JoinPredecessorExpression test, @@ -166,16 +183,6 @@ public final class ForNode extends LoopNode { public boolean isForIn() { return (flags & IS_FOR_IN) != 0; } - - /** - * Flag this to be a for in construct - * @param lc lexical context - * @return new for node if changed or existing if not - */ - public ForNode setIsForIn(final LexicalContext lc) { - return setFlags(lc, flags | IS_FOR_IN); - } - /** * Is this a for each construct, known from e.g. Rhino. This will be a for of construct * in ECMAScript 6 @@ -185,15 +192,6 @@ public final class ForNode extends LoopNode { return (flags & IS_FOR_EACH) != 0; } - /** - * Flag this to be a for each construct - * @param lc lexical context - * @return new for node if changed or existing if not - */ - public ForNode setIsForEach(final LexicalContext lc) { - return setFlags(lc, flags | IS_FOR_EACH); - } - /** * If this is a for in or for each construct, there is an iterator symbol * @return the symbol for the iterator to be used, or null if none exists @@ -260,13 +258,6 @@ public final class ForNode extends LoopNode { return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion)); } - private ForNode setFlags(final LexicalContext lc, final int flags) { - if (this.flags == flags) { - return this; - } - return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion)); - } - @Override JoinPredecessor setLocalVariableConversionChanged(final LexicalContext lc, final LocalVariableConversion conversion) { return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion)); diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/FunctionNode.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/FunctionNode.java index 1bbc7ab0834..ad19f8d53a8 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/FunctionNode.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/FunctionNode.java @@ -31,7 +31,6 @@ import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALL import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_TRACE_ENTEREXIT; import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_TRACE_MISSES; import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_TRACE_VALUES; - import java.util.Collections; import java.util.EnumSet; import java.util.Iterator; @@ -46,6 +45,7 @@ import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.annotations.Ignore; import jdk.nashorn.internal.ir.annotations.Immutable; import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.parser.Token; import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData; import jdk.nashorn.internal.runtime.ScriptFunction; import jdk.nashorn.internal.runtime.Source; @@ -299,12 +299,16 @@ public final class FunctionNode extends LexicalContextExpression implements Flag * @param token token * @param finish finish * @param firstToken first token of the function node (including the function declaration) + * @param lastToken lastToken * @param namespace the namespace * @param ident the identifier * @param name the name of the function * @param parameters parameter list * @param kind kind of function as in {@link FunctionNode.Kind} * @param flags initial flags + * @param body body of the function + * @param state The initial state from the parser. Must be one of {@link CompilationState#PARSED} and {@link CompilationState#PARSE_ERROR} + * @param endParserState The parser state at the end of the parsing. */ public FunctionNode( final Source source, @@ -312,12 +316,16 @@ public final class FunctionNode extends LexicalContextExpression implements Flag final long token, final int finish, final long firstToken, + final long lastToken, final Namespace namespace, final IdentNode ident, final String name, final List parameters, final FunctionNode.Kind kind, - final int flags) { + final int flags, + final Block body, + final CompilationState state, + final Object endParserState) { super(token, finish); this.source = source; @@ -327,15 +335,15 @@ public final class FunctionNode extends LexicalContextExpression implements Flag this.kind = kind; this.parameters = parameters; this.firstToken = firstToken; - this.lastToken = token; + this.lastToken = lastToken; this.namespace = namespace; - this.compilationState = EnumSet.of(CompilationState.INITIALIZED); + this.compilationState = EnumSet.of(CompilationState.INITIALIZED, state); this.flags = flags; this.compileUnit = null; - this.body = null; + this.body = body; this.thisProperties = 0; this.rootClass = null; - this.endParserState = null; + this.endParserState = endParserState; } private FunctionNode( @@ -439,7 +447,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag * @return the id */ public int getId() { - return position(); + return isProgram() ? -1: Token.descPosition(firstToken); } /** @@ -902,34 +910,6 @@ public final class FunctionNode extends LexicalContextExpression implements Flag return lastToken; } - /** - * Set the last token for this function's code - * @param lc lexical context - * @param lastToken the last token - * @return function node or a new one if state was changed - */ - public FunctionNode setLastToken(final LexicalContext lc, final long lastToken) { - if (this.lastToken == lastToken) { - return this; - } - return Node.replaceInLexicalContext( - lc, - this, - new FunctionNode( - this, - lastToken, - endParserState, - flags, - name, - returnType, - compileUnit, - compilationState, - body, - parameters, - thisProperties, - rootClass)); - } - /** * Returns the end parser state for this function. * @return the end parser state for this function. @@ -938,33 +918,6 @@ public final class FunctionNode extends LexicalContextExpression implements Flag return endParserState; } - /** - * Set the end parser state for this function. - * @param lc lexical context - * @param endParserState the parser state to set - * @return function node or a new one if state was changed - */ - public FunctionNode setEndParserState(final LexicalContext lc, final Object endParserState) { - if (this.endParserState == endParserState) { - return this; - } - return Node.replaceInLexicalContext( - lc, - this, - new FunctionNode( - this, - lastToken, - endParserState, - flags, - name, - returnType, - compileUnit, - compilationState, - body, - parameters, - thisProperties, rootClass)); - } - /** * Get the name of this function * @return the name diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LoopNode.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LoopNode.java index 86ef3cdad49..bfc86a69a43 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LoopNode.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LoopNode.java @@ -53,14 +53,15 @@ public abstract class LoopNode extends BreakableStatement { * @param token token * @param finish finish * @param body loop body + * @param test test * @param controlFlowEscapes controlFlowEscapes */ - protected LoopNode(final int lineNumber, final long token, final int finish, final Block body, final boolean controlFlowEscapes) { + protected LoopNode(final int lineNumber, final long token, final int finish, final Block body, final JoinPredecessorExpression test, final boolean controlFlowEscapes) { super(lineNumber, token, finish, new Label("while_break")); this.continueLabel = new Label("while_continue"); - this.test = null; this.body = body; this.controlFlowEscapes = controlFlowEscapes; + this.test = test; } /** diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Node.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Node.java index 37ec4b96a39..861f12a05ff 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Node.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Node.java @@ -39,7 +39,7 @@ public abstract class Node implements Cloneable { protected final int start; /** End of source range. */ - protected int finish; + protected final int finish; /** Token descriptor. */ private final long token; @@ -80,6 +80,18 @@ public abstract class Node implements Cloneable { this.finish = node.finish; } + /** + * Copy constructor that overrides finish + * + * @param node source node + * @param finish Last character + */ + protected Node(final Node node, final int finish) { + this.token = node.token; + this.start = node.start; + this.finish = finish; + } + /** * Is this a loop node? * @@ -151,14 +163,6 @@ public abstract class Node implements Cloneable { return finish; } - /** - * Set finish position for this node in the source string - * @param finish finish - */ - public void setFinish(final int finish) { - this.finish = finish; - } - /** * Get start position for node * @return start position diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/WhileNode.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/WhileNode.java index 0cced567fbc..99bdce87c19 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/WhileNode.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/WhileNode.java @@ -45,9 +45,11 @@ public final class WhileNode extends LoopNode { * @param token token * @param finish finish * @param isDoWhile is this a do while loop? + * @param test test expression + * @param body body of the while loop */ - public WhileNode(final int lineNumber, final long token, final int finish, final boolean isDoWhile) { - super(lineNumber, token, finish, null, false); + public WhileNode(final int lineNumber, final long token, final int finish, final boolean isDoWhile, final JoinPredecessorExpression test, final Block body) { + super(lineNumber, token, finish, body, test, false); this.isDoWhile = isDoWhile; } @@ -55,10 +57,10 @@ public final class WhileNode extends LoopNode { * Internal copy constructor * * @param whileNode while node - * @param test test - * @param body body + * @param test Test expression + * @param body body of the while loop * @param controlFlowEscapes control flow escapes? - * @param conversion TODO + * @param conversion local variable conversion info */ private WhileNode(final WhileNode whileNode, final JoinPredecessorExpression test, final Block body, final boolean controlFlowEscapes, final LocalVariableConversion conversion) { super(whileNode, test, body, controlFlowEscapes, conversion); diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/WithNode.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/WithNode.java index 0ea52c98bb4..20e319294d2 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/WithNode.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/WithNode.java @@ -42,14 +42,16 @@ public final class WithNode extends LexicalContextStatement { /** * Constructor * - * @param lineNumber line number - * @param token token - * @param finish finish + * @param lineNumber Line number of the header + * @param token First token + * @param finish Character index of the last token + * @param expression With expression + * @param body Body of with node */ - public WithNode(final int lineNumber, final long token, final int finish) { + public WithNode(final int lineNumber, final long token, final int finish, final Expression expression, final Block body) { super(lineNumber, token, finish); - this.expression = null; - this.body = null; + this.expression = expression; + this.body = body; } private WithNode(final WithNode node, final Expression expression, final Block body) { diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/Global.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/Global.java index eb56de0e18f..61d7948f621 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/Global.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/Global.java @@ -561,7 +561,6 @@ public final class Global extends ScriptObject implements Scope { * * @param engine ScriptEngine to initialize */ - @SuppressWarnings("hiding") public void initBuiltinObjects(final ScriptEngine engine) { if (this.builtinObject != null) { // already initialized, just return @@ -1718,7 +1717,6 @@ public final class Global extends ScriptObject implements Scope { return func; } - @SuppressWarnings("hiding") private void init(final ScriptEngine engine) { assert Context.getGlobal() == this : "this global is not set as current"; diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeError.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeError.java index e1d95ce097f..41ea9a5f4e2 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeError.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeError.java @@ -27,7 +27,6 @@ package jdk.nashorn.internal.objects; import static jdk.nashorn.internal.lookup.Lookup.MH; import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; - import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import jdk.nashorn.api.scripting.NashornException; @@ -131,7 +130,6 @@ public final class NativeError extends ScriptObject { // This is called NativeError, NativeTypeError etc. to // associate a ECMAException with the ECMA Error object. - @SuppressWarnings("unused") static void initException(final ScriptObject self) { // ECMAException constructor has side effects new ECMAException(self, null); diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeJava.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeJava.java index 7c6e60a967d..e9a4380dadc 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeJava.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeJava.java @@ -27,7 +27,6 @@ package jdk.nashorn.internal.objects; import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; - import java.lang.invoke.MethodHandles; import java.lang.reflect.Array; import java.util.Collection; @@ -36,7 +35,6 @@ import java.util.List; import jdk.internal.dynalink.beans.StaticClass; import jdk.internal.dynalink.support.TypeUtilities; import jdk.nashorn.api.scripting.JSObject; -import jdk.nashorn.api.scripting.ScriptUtils; import jdk.nashorn.internal.objects.annotations.Attribute; import jdk.nashorn.internal.objects.annotations.Function; import jdk.nashorn.internal.objects.annotations.ScriptClass; diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java index 3162e184468..32f1df63895 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java @@ -53,7 +53,6 @@ import static jdk.nashorn.internal.parser.TokenType.RPAREN; import static jdk.nashorn.internal.parser.TokenType.SEMICOLON; import static jdk.nashorn.internal.parser.TokenType.TERNARY; import static jdk.nashorn.internal.parser.TokenType.WHILE; - import java.io.Serializable; import java.util.ArrayDeque; import java.util.ArrayList; @@ -71,10 +70,8 @@ import jdk.nashorn.internal.ir.AccessNode; import jdk.nashorn.internal.ir.BaseNode; import jdk.nashorn.internal.ir.BinaryNode; import jdk.nashorn.internal.ir.Block; -import jdk.nashorn.internal.ir.BlockLexicalContext; import jdk.nashorn.internal.ir.BlockStatement; import jdk.nashorn.internal.ir.BreakNode; -import jdk.nashorn.internal.ir.BreakableNode; import jdk.nashorn.internal.ir.CallNode; import jdk.nashorn.internal.ir.CaseNode; import jdk.nashorn.internal.ir.CatchNode; @@ -90,9 +87,7 @@ import jdk.nashorn.internal.ir.IfNode; import jdk.nashorn.internal.ir.IndexNode; import jdk.nashorn.internal.ir.JoinPredecessorExpression; import jdk.nashorn.internal.ir.LabelNode; -import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LiteralNode; -import jdk.nashorn.internal.ir.LoopNode; import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.ObjectNode; import jdk.nashorn.internal.ir.PropertyKey; @@ -138,8 +133,8 @@ public class Parser extends AbstractParser implements Loggable { private List functionDeclarations; - private final BlockLexicalContext lc = new BlockLexicalContext(); - private final Deque defaultNames = new ArrayDeque<>(); + private final ParserContext lc; + private final Deque defaultNames; /** Namespace for function names where not explicitly given */ private final Namespace namespace; @@ -187,6 +182,8 @@ public class Parser extends AbstractParser implements Loggable { */ public Parser(final ScriptEnvironment env, final Source source, final ErrorManager errors, final boolean strict, final int lineOffset, final DebugLogger log) { super(source, errors, strict, lineOffset); + this.lc = new ParserContext(); + this.defaultNames = new ArrayDeque<>(); this.env = env; this.namespace = new Namespace(env.getNamespace()); this.scripting = env._scripting; @@ -344,26 +341,35 @@ public class Parser extends AbstractParser implements Loggable { final long functionToken = Token.toDesc(FUNCTION, 0, source.getLength()); // Set up the function to append elements. - FunctionNode function = newFunctionNode( - functionToken, - new IdentNode(functionToken, Token.descPosition(functionToken), PROGRAM.symbolName()), - new ArrayList(), - FunctionNode.Kind.NORMAL, - functionLine); + final IdentNode ident = new IdentNode(functionToken, Token.descPosition(functionToken), PROGRAM.symbolName()); + final ParserContextFunctionNode function = createParserContextFunctionNode(ident, functionToken, FunctionNode.Kind.NORMAL, functionLine, Collections.emptyList()); + lc.push(function); + + final ParserContextBlockNode body = newBlock(); functionDeclarations = new ArrayList<>(); sourceElements(false); addFunctionDeclarations(function); functionDeclarations = null; + restoreBlock(body); + body.setFlag(Block.NEEDS_SCOPE); + + final Block functionBody = new Block(functionToken, source.getLength() - 1, body.getFlags(), body.getStatements()); + lc.pop(function); + expect(EOF); - function.setFinish(source.getLength() - 1); - function = restoreFunctionNode(function, token); //commit code - function = function.setBody(lc, function.getBody().setNeedsScope(lc)); - - printAST(function); - return function; + final FunctionNode functionNode = createFunctionNode( + function, + functionToken, + ident, + Collections.emptyList(), + FunctionNode.Kind.NORMAL, + functionLine, + functionBody); + printAST(functionNode); + return functionNode; } catch (final Exception e) { handleParseException(e); return null; @@ -444,21 +450,15 @@ loop: * * @return New block. */ - private Block newBlock() { - return lc.push(new Block(token, Token.descPosition(token))); + private ParserContextBlockNode newBlock() { + return lc.push(new ParserContextBlockNode(token)); } - /** - * Set up a new function block. - * - * @param ident Name of function. - * @return New block. - */ - private FunctionNode newFunctionNode(final long startToken, final IdentNode ident, final List parameters, final FunctionNode.Kind kind, final int functionLine) { + private ParserContextFunctionNode createParserContextFunctionNode(final IdentNode ident, final long functionToken, final FunctionNode.Kind kind, final int functionLine, final List parameters) { // Build function name. final StringBuilder sb = new StringBuilder(); - final FunctionNode parentFunction = lc.getCurrentFunction(); + final ParserContextFunctionNode parentFunction = lc.getCurrentFunction(); if (parentFunction != null && !parentFunction.isProgram()) { sb.append(parentFunction.getName()).append('$'); } @@ -477,25 +477,33 @@ loop: flags |= FunctionNode.IS_PROGRAM; } + final ParserContextFunctionNode functionNode = new ParserContextFunctionNode(functionToken, ident, name, namespace, functionLine, kind, parameters); + functionNode.setFlag(flags); + return functionNode; + } + + private FunctionNode createFunctionNode(final ParserContextFunctionNode function, final long startToken, final IdentNode ident, final List parameters, final FunctionNode.Kind kind, final int functionLine, final Block body){ + final CompilationState state = errors.hasErrors() ? CompilationState.PARSE_ERROR : CompilationState.PARSED; // Start new block. final FunctionNode functionNode = new FunctionNode( source, functionLine, - token, - Token.descPosition(token), + body.getToken(), + Token.descPosition(body.getToken()), startToken, + function.getLastToken(), namespace, ident, - name, + function.getName(), parameters, kind, - flags); + function.getFlags(), + body, + state, + function.getEndParserState()); - lc.push(functionNode); - // Create new block, and just put it on the context stack, restoreFunctionNode() will associate it with the - // FunctionNode. - newBlock(); + printAST(functionNode); return functionNode; } @@ -503,27 +511,17 @@ loop: /** * Restore the current block. */ - private Block restoreBlock(final Block block) { + private ParserContextBlockNode restoreBlock(final ParserContextBlockNode block) { return lc.pop(block); } - - private FunctionNode restoreFunctionNode(final FunctionNode functionNode, final long lastToken) { - final Block newBody = restoreBlock(lc.getFunctionBody(functionNode)); - - return lc.pop(functionNode). - setBody(lc, newBody). - setLastToken(lc, lastToken). - setState(lc, errors.hasErrors() ? CompilationState.PARSE_ERROR : CompilationState.PARSED); - } - /** * Get the statements in a block. * @return Block statements. */ private Block getBlock(final boolean needsBraces) { - // Set up new block. Captures LBRACE. - Block newBlock = newBlock(); + final long blockToken = token; + final ParserContextBlockNode newBlock = newBlock(); try { // Block opening brace. if (needsBraces) { @@ -533,21 +531,18 @@ loop: statementList(); } finally { - newBlock = restoreBlock(newBlock); + restoreBlock(newBlock); } - final int possibleEnd = Token.descPosition(token) + Token.descLength(token); - // Block closing brace. if (needsBraces) { expect(RBRACE); } - newBlock.setFinish(possibleEnd); - - return newBlock; + return new Block(blockToken, finish, newBlock.getFlags(), newBlock.getStatements()); } + /** * Get all the statements generated by a single statement. * @return Statements. @@ -557,13 +552,13 @@ loop: return getBlock(true); } // Set up new block. Captures first token. - Block newBlock = newBlock(); + final ParserContextBlockNode newBlock = newBlock(); try { statement(); } finally { - newBlock = restoreBlock(newBlock); + restoreBlock(newBlock); } - return newBlock; + return new Block(newBlock.getToken(), finish, newBlock.getFlags(), newBlock.getStatements()); } /** @@ -584,7 +579,7 @@ loop: */ private void detectSpecialProperty(final IdentNode ident) { if (isArguments(ident)) { - lc.setFlag(lc.getCurrentFunction(), FunctionNode.USES_ARGUMENTS); + lc.getCurrentFunction().setFlag(FunctionNode.USES_ARGUMENTS); } } @@ -698,18 +693,19 @@ loop: // Make a pseudo-token for the script holding its start and length. final long functionToken = Token.toDesc(FUNCTION, Token.descPosition(Token.withDelimiter(token)), source.getLength()); final int functionLine = line; - // Set up the script to append elements. - - FunctionNode script = newFunctionNode( - functionToken, - new IdentNode(functionToken, Token.descPosition(functionToken), scriptName), - new ArrayList(), - FunctionNode.Kind.SCRIPT, - functionLine); + final IdentNode ident = new IdentNode(functionToken, Token.descPosition(functionToken), scriptName); + final ParserContextFunctionNode script = createParserContextFunctionNode( + ident, + functionToken, + FunctionNode.Kind.SCRIPT, + functionLine, + Collections.emptyList()); + lc.push(script); + final ParserContextBlockNode body = newBlock(); // If ES6 block scope is enabled add a per-script block for top-level LET and CONST declarations. final int startLine = start; - Block outer = useBlockScope() ? newBlock() : null; + final ParserContextBlockNode outer = useBlockScope() ? newBlock() : null; functionDeclarations = new ArrayList<>(); try { @@ -717,20 +713,25 @@ loop: addFunctionDeclarations(script); } finally { if (outer != null) { - outer = restoreBlock(outer); - appendStatement(new BlockStatement(startLine, outer)); + restoreBlock(outer); + appendStatement(new BlockStatement( + startLine, + new Block( + functionToken, + startLine, outer.getFlags(), + outer.getStatements()))); } } functionDeclarations = null; + restoreBlock(body); + body.setFlag(Block.NEEDS_SCOPE); + final Block programBody = new Block(functionToken, functionLine, body.getFlags(), body.getStatements()); + lc.pop(script); + script.setLastToken(token); expect(EOF); - script.setFinish(source.getLength() - 1); - - script = restoreFunctionNode(script, token); //commit code - script = script.setBody(lc, script.getBody().setNeedsScope(lc)); - - return script; + return createFunctionNode(script, functionToken, ident, Collections.emptyList(), FunctionNode.Kind.SCRIPT, functionLine, programBody); } /** @@ -789,7 +790,7 @@ loop: // check for directive prologues if (checkDirective) { // skip any debug statement like line number to get actual first line - final Node lastStatement = lc.getLastStatement(); + final Statement lastStatement = lc.getLastStatement(); // get directive prologue, if any final String directive = getDirective(lastStatement); @@ -809,8 +810,8 @@ loop: // handle use strict directive if ("use strict".equals(directive)) { isStrictMode = true; - final FunctionNode function = lc.getCurrentFunction(); - lc.setFlag(lc.getCurrentFunction(), FunctionNode.IS_STRICT); + final ParserContextFunctionNode function = lc.getCurrentFunction(); + function.setFlag(FunctionNode.IS_STRICT); // We don't need to check these, if lexical environment is already strict if (!oldStrictMode && directiveStmts != null) { @@ -831,8 +832,8 @@ loop: } else if (Context.DEBUG) { final int flag = FunctionNode.getDirectiveFlag(directive); if (flag != 0) { - final FunctionNode function = lc.getCurrentFunction(); - lc.setFlag(function, flag); + final ParserContextFunctionNode function = lc.getCurrentFunction(); + function.setFlag(flag); } } } @@ -1114,11 +1115,7 @@ loop: // If is a statement then handle end of line. if (isStatement) { - final boolean semicolon = type == SEMICOLON; endOfLine(); - if (semicolon) { - lc.getCurrentBlock().setFinish(finish); - } } return vars; @@ -1166,11 +1163,6 @@ loop: } endOfLine(); - - if (expressionStatement != null) { - expressionStatement.setFinish(finish); - lc.getCurrentBlock().setFinish(finish); - } } /** @@ -1216,13 +1208,23 @@ loop: * Parse a FOR statement. */ private void forStatement() { + final long forToken = token; + final int forLine = line; // When ES6 for-let is enabled we create a container block to capture the LET. final int startLine = start; - Block outer = useBlockScope() ? newBlock() : null; + final ParserContextBlockNode outer = useBlockScope() ? newBlock() : null; + // Create FOR node, capturing FOR token. - ForNode forNode = new ForNode(line, token, Token.descPosition(token), null, ForNode.IS_FOR); + final ParserContextLoopNode forNode = new ParserContextLoopNode(); lc.push(forNode); + Block body = null; + List vars = null; + Expression init = null; + JoinPredecessorExpression test = null; + JoinPredecessorExpression modify = null; + + int flags = 0; try { // FOR tested in caller. @@ -1231,13 +1233,12 @@ loop: // Nashorn extension: for each expression. // iterate property values rather than property names. if (!env._no_syntax_extensions && type == IDENT && "each".equals(getValue())) { - forNode = forNode.setIsForEach(lc); + flags |= ForNode.IS_FOR_EACH; next(); } expect(LPAREN); - List vars = null; switch (type) { case VAR: @@ -1258,8 +1259,7 @@ loop: break; } - final Expression expression = expression(unaryExpression(), COMMARIGHT.getPrecedence(), true); - forNode = forNode.setInit(lc, expression); + init = expression(unaryExpression(), COMMARIGHT.getPrecedence(), true); break; } @@ -1268,26 +1268,27 @@ loop: // for (init; test; modify) // for each (init; test; modify) is invalid - if (forNode.isForEach()) { + if ((flags & ForNode.IS_FOR_EACH) != 0) { throw error(AbstractParser.message("for.each.without.in"), token); } expect(SEMICOLON); if (type != SEMICOLON) { - forNode = forNode.setTest(lc, joinPredecessorExpression()); + test = joinPredecessorExpression(); } expect(SEMICOLON); if (type != RPAREN) { - forNode = forNode.setModify(lc, joinPredecessorExpression()); + modify = joinPredecessorExpression(); } break; case IN: - forNode = forNode.setIsForIn(lc).setTest(lc, new JoinPredecessorExpression()); + flags |= ForNode.IS_FOR_IN; + test = new JoinPredecessorExpression(); if (vars != null) { // for (var i in obj) if (vars.size() == 1) { - forNode = forNode.setInit(lc, new IdentNode(vars.get(0).getName())); + init = new IdentNode(vars.get(0).getName()); } else { // for (var i, j in obj) is invalid throw error(AbstractParser.message("many.vars.in.for.in.loop"), vars.get(1).getToken()); @@ -1295,7 +1296,6 @@ loop: } else { // for (expr in obj) - final Node init = forNode.getInit(); assert init != null : "for..in init expression can not be null here"; // check if initial expression is a valid L-value @@ -1316,7 +1316,7 @@ loop: next(); // Get the collection expression. - forNode = forNode.setModify(lc, joinPredecessorExpression()); + modify = joinPredecessorExpression(); break; default: @@ -1327,37 +1327,27 @@ loop: expect(RPAREN); // Set the for body. - final Block body = getStatement(); - forNode = forNode.setBody(lc, body); - forNode.setFinish(body.getFinish()); - - appendStatement(forNode); + body = getStatement(); } finally { lc.pop(forNode); + if (vars != null) { + for (final VarNode var : vars) { + appendStatement(var); + } + } + if (body != null) { + appendStatement(new ForNode(forLine, forToken, body.getFinish(), body, (forNode.getFlags() | flags), init, test, modify)); + } if (outer != null) { - outer.setFinish(forNode.getFinish()); - outer = restoreBlock(outer); - appendStatement(new BlockStatement(startLine, outer)); + restoreBlock(outer); + appendStatement(new BlockStatement(startLine, new Block( + outer.getToken(), + body.getFinish(), + outer.getStatements()))); } } } - /** - * ... IterationStatement : - * ... - * Expression[NoIn]?; Expression? ; Expression? - * var VariableDeclarationList[NoIn]; Expression? ; Expression? - * LeftHandSideExpression in Expression - * var VariableDeclaration[NoIn] in Expression - * - * See 12.6 - * - * Parse the control section of a FOR statement. Also used for - * comprehensions. - * @param forNode Owning FOR. - */ - - /** * ...IterationStatement : * ... @@ -1371,25 +1361,26 @@ loop: private void whileStatement() { // Capture WHILE token. final long whileToken = token; + final int whileLine = line; // WHILE tested in caller. next(); - // Construct WHILE node. - WhileNode whileNode = new WhileNode(line, whileToken, Token.descPosition(whileToken), false); + final ParserContextLoopNode whileNode = new ParserContextLoopNode(); lc.push(whileNode); + JoinPredecessorExpression test = null; + Block body = null; + try { expect(LPAREN); - final int whileLine = line; - final JoinPredecessorExpression test = joinPredecessorExpression(); + test = joinPredecessorExpression(); expect(RPAREN); - final Block body = getStatement(); - appendStatement(whileNode = - new WhileNode(whileLine, whileToken, finish, false). - setTest(lc, test). - setBody(lc, body)); + body = getStatement(); } finally { lc.pop(whileNode); + if (body != null){ + appendStatement(new WhileNode(whileLine, whileToken, body.getFinish(), false, test, body)); + } } } @@ -1406,34 +1397,32 @@ loop: private void doStatement() { // Capture DO token. final long doToken = token; + int doLine = 0; // DO tested in the caller. next(); - WhileNode doWhileNode = new WhileNode(-1, doToken, Token.descPosition(doToken), true); + final ParserContextLoopNode doWhileNode = new ParserContextLoopNode(); lc.push(doWhileNode); + Block body = null; + JoinPredecessorExpression test = null; + try { // Get DO body. - final Block body = getStatement(); + body = getStatement(); expect(WHILE); expect(LPAREN); - final int doLine = line; - final JoinPredecessorExpression test = joinPredecessorExpression(); + doLine = line; + test = joinPredecessorExpression(); expect(RPAREN); if (type == SEMICOLON) { endOfLine(); } - doWhileNode.setFinish(finish); - - //line number is last - appendStatement(doWhileNode = - new WhileNode(doLine, doToken, finish, true). - setBody(lc, body). - setTest(lc, test)); } finally { lc.pop(doWhileNode); + appendStatement(new WhileNode(doLine, doToken, finish, true, test, body)); } } @@ -1452,7 +1441,7 @@ loop: // CONTINUE tested in caller. nextOrEOL(); - LabelNode labelNode = null; + ParserContextLabelNode labelNode = null; // SEMICOLON or label. switch (type) { @@ -1474,7 +1463,7 @@ loop: } final String labelName = labelNode == null ? null : labelNode.getLabelName(); - final LoopNode targetNode = lc.getContinueTo(labelName); + final ParserContextLoopNode targetNode = lc.getContinueTo(labelName); if (targetNode == null) { throw error(AbstractParser.message("illegal.continue.stmt"), continueToken); @@ -1500,7 +1489,7 @@ loop: // BREAK tested in caller. nextOrEOL(); - LabelNode labelNode = null; + ParserContextLabelNode labelNode = null; // SEMICOLON or label. switch (type) { @@ -1524,7 +1513,7 @@ loop: //either an explicit label - then get its node or just a "break" - get first breakable //targetNode is what we are breaking out from. final String labelName = labelNode == null ? null : labelNode.getLabelName(); - final BreakableNode targetNode = lc.getBreakable(labelName); + final ParserContextBreakableNode targetNode = lc.getBreakable(labelName); if (targetNode == null) { throw error(AbstractParser.message("illegal.break.stmt"), breakToken); } @@ -1632,20 +1621,17 @@ loop: throw error(AbstractParser.message("strict.no.with"), withToken); } - // Get WITH expression. - WithNode withNode = new WithNode(withLine, withToken, finish); - + Expression expression = null; + Block body = null; try { - lc.push(withNode); expect(LPAREN); - withNode = withNode.setExpression(lc, expression()); + expression = expression(); expect(RPAREN); - withNode = withNode.setBody(lc, getStatement()); + body = getStatement(); } finally { - lc.pop(withNode); + appendStatement(new WithNode(withLine, withToken, finish, expression, body)); } - appendStatement(withNode); } /** @@ -1677,19 +1663,22 @@ loop: next(); // Create and add switch statement. - SwitchNode switchNode = new SwitchNode(switchLine, switchToken, Token.descPosition(switchToken), null, new ArrayList(), null); + final ParserContextSwitchNode switchNode= new ParserContextSwitchNode(); lc.push(switchNode); + CaseNode defaultCase = null; + // Prepare to accumulate cases. + final List cases = new ArrayList<>(); + + Expression expression = null; + try { expect(LPAREN); - switchNode = switchNode.setExpression(lc, expression()); + expression = expression(); expect(RPAREN); expect(LBRACE); - // Prepare to accumulate cases. - final List cases = new ArrayList<>(); - CaseNode defaultCase = null; while (type != RBRACE) { // Prepare for next case. @@ -1720,7 +1709,6 @@ loop: // Get CASE body. final Block statements = getBlock(false); final CaseNode caseNode = new CaseNode(caseToken, finish, caseExpression, statements); - statements.setFinish(finish); if (caseExpression == null) { defaultCase = caseNode; @@ -1729,13 +1717,10 @@ loop: cases.add(caseNode); } - switchNode = switchNode.setCases(lc, cases, defaultCase); next(); - switchNode.setFinish(finish); - - appendStatement(switchNode); } finally { lc.pop(switchNode); + appendStatement(new SwitchNode(switchLine, switchToken, finish, expression, cases, defaultCase)); } } @@ -1759,15 +1744,17 @@ loop: throw error(AbstractParser.message("duplicate.label", ident.getName()), labelToken); } - LabelNode labelNode = new LabelNode(line, labelToken, finish, ident.getName(), null); + final ParserContextLabelNode labelNode = new ParserContextLabelNode(ident.getName()); + Block body = null; try { lc.push(labelNode); - labelNode = labelNode.setBody(lc, getStatement()); - labelNode.setFinish(finish); - appendStatement(labelNode); + body = getStatement(); } finally { - assert lc.peek() instanceof LabelNode; + assert lc.peek() instanceof ParserContextLabelNode; lc.pop(labelNode); + if (ident != null){ + appendStatement(new LabelNode(line, labelToken, finish, ident.getName(), body)); + } } } @@ -1835,8 +1822,7 @@ loop: // Container block needed to act as target for labeled break statements final int startLine = line; - Block outer = newBlock(); - + final ParserContextBlockNode outer = newBlock(); // Create try. try { @@ -1867,15 +1853,15 @@ loop: expect(RPAREN); - Block catchBlock = newBlock(); + final ParserContextBlockNode catchBlock = newBlock(); try { // Get CATCH body. final Block catchBody = getBlock(true); final CatchNode catchNode = new CatchNode(catchLine, catchToken, finish, exception, ifExpression, catchBody, false); appendStatement(catchNode); } finally { - catchBlock = restoreBlock(catchBlock); - catchBlocks.add(catchBlock); + restoreBlock(catchBlock); + catchBlocks.add(new Block(catchBlock.getToken(), finish, catchBlock.getFlags(), catchBlock.getStatements())); } // If unconditional catch then should to be the end. @@ -1897,19 +1883,15 @@ loop: throw error(AbstractParser.message("missing.catch.or.finally"), tryToken); } - final TryNode tryNode = new TryNode(tryLine, tryToken, Token.descPosition(tryToken), tryBody, catchBlocks, finallyStatements); + final TryNode tryNode = new TryNode(tryLine, tryToken, finish, tryBody, catchBlocks, finallyStatements); // Add try. assert lc.peek() == outer; appendStatement(tryNode); - - tryNode.setFinish(finish); - outer.setFinish(finish); - } finally { - outer = restoreBlock(outer); + restoreBlock(outer); } - appendStatement(new BlockStatement(startLine, outer)); + appendStatement(new BlockStatement(startLine, new Block(tryToken, finish, outer.getFlags(), outer.getStatements()))); } /** @@ -1927,7 +1909,7 @@ loop: // DEBUGGER tested in caller. next(); endOfLine(); - appendStatement(new ExpressionStatement(debuggerLine, debuggerToken, finish, new RuntimeNode(debuggerToken, finish, RuntimeNode.Request.DEBUGGER, new ArrayList()))); + appendStatement(new ExpressionStatement(debuggerLine, debuggerToken, finish, new RuntimeNode(debuggerToken, finish, RuntimeNode.Request.DEBUGGER, Collections.emptyList()))); } /** @@ -1954,7 +1936,7 @@ loop: case THIS: final String name = type.getName(); next(); - lc.setFlag(lc.getCurrentFunction(), FunctionNode.USES_THIS); + lc.getCurrentFunction().setFlag(FunctionNode.USES_THIS); return new IdentNode(primaryToken, finish, name); case IDENT: final IdentNode ident = getIdent(); @@ -2314,9 +2296,24 @@ loop: final IdentNode getNameNode = createIdentNode(((Node)getIdent).getToken(), finish, NameCodec.encode("get " + getterName)); expect(LPAREN); expect(RPAREN); - final FunctionNode functionNode = functionBody(getSetToken, getNameNode, new ArrayList(), FunctionNode.Kind.GETTER, functionLine); - return new PropertyFunction(getIdent, functionNode); + final ParserContextFunctionNode functionNode = createParserContextFunctionNode(getNameNode, getSetToken, FunctionNode.Kind.GETTER, functionLine, Collections.emptyList()); + lc.push(functionNode); + + final Block functionBody = functionBody(functionNode); + + lc.pop(functionNode); + + final FunctionNode function = createFunctionNode( + functionNode, + getSetToken, + getNameNode, + Collections.emptyList(), + FunctionNode.Kind.GETTER, + functionLine, + functionBody); + + return new PropertyFunction(getIdent, function); } private PropertyFunction propertySetterFunction(final long getSetToken, final int functionLine) { @@ -2338,9 +2335,25 @@ loop: if (argIdent != null) { parameters.add(argIdent); } - final FunctionNode functionNode = functionBody(getSetToken, setNameNode, parameters, FunctionNode.Kind.SETTER, functionLine); - return new PropertyFunction(setIdent, functionNode); + + final ParserContextFunctionNode functionNode = createParserContextFunctionNode(setNameNode, getSetToken, FunctionNode.Kind.SETTER, functionLine, parameters); + lc.push(functionNode); + + final Block functionBody = functionBody(functionNode); + + lc.pop(functionNode); + + final FunctionNode function = createFunctionNode( + functionNode, + getSetToken, + setNameNode, + parameters, + FunctionNode.Kind.SETTER, + functionLine, + functionBody); + + return new PropertyFunction(setIdent, function); } private static class PropertyFunction { @@ -2655,11 +2668,18 @@ loop: final List parameters = formalParameterList(); expect(RPAREN); - FunctionNode functionNode = functionBody(functionToken, name, parameters, FunctionNode.Kind.NORMAL, functionLine); + final ParserContextFunctionNode functionNode = createParserContextFunctionNode(name, functionToken, FunctionNode.Kind.NORMAL, functionLine, parameters); + lc.push(functionNode); + Block functionBody = null; + try{ + functionBody = functionBody(functionNode); + } finally { + lc.pop(functionNode); + } if (isStatement) { if (topLevel || useBlockScope()) { - functionNode = functionNode.setFlag(lc, FunctionNode.IS_DECLARED); + functionNode.setFlag(FunctionNode.IS_DECLARED); } else if (isStrictMode) { throw error(JSErrorType.SYNTAX_ERROR, AbstractParser.message("strict.no.func.decl.here"), functionToken); } else if (env._function_statement == ScriptEnvironment.FunctionStatementBehavior.ERROR) { @@ -2668,12 +2688,12 @@ loop: warning(JSErrorType.SYNTAX_ERROR, AbstractParser.message("no.func.decl.here.warn"), functionToken); } if (isArguments(name)) { - lc.setFlag(lc.getCurrentFunction(), FunctionNode.DEFINES_ARGUMENTS); + lc.getCurrentFunction().setFlag(FunctionNode.DEFINES_ARGUMENTS); } } if (isAnonymous) { - functionNode = functionNode.setFlag(lc, FunctionNode.IS_ANONYMOUS); + functionNode.setFlag(FunctionNode.IS_ANONYMOUS); } final int arity = parameters.size(); @@ -2687,7 +2707,7 @@ loop: String parameterName = parameter.getName(); if (isArguments(parameterName)) { - functionNode = functionNode.setFlag(lc, FunctionNode.DEFINES_ARGUMENTS); + functionNode.setFlag(FunctionNode.DEFINES_ARGUMENTS); } if (parametersSet.contains(parameterName)) { @@ -2705,17 +2725,26 @@ loop: } } else if (arity == 1) { if (isArguments(parameters.get(0))) { - functionNode = functionNode.setFlag(lc, FunctionNode.DEFINES_ARGUMENTS); + functionNode.setFlag(FunctionNode.DEFINES_ARGUMENTS); } } + final FunctionNode function = createFunctionNode( + functionNode, + functionToken, + name, + parameters, + FunctionNode.Kind.NORMAL, + functionLine, + functionBody); + if (isStatement) { int varFlags = VarNode.IS_STATEMENT; if (!topLevel && useBlockScope()) { // mark ES6 block functions as lexically scoped varFlags |= VarNode.IS_LET; } - final VarNode varNode = new VarNode(functionLine, functionToken, finish, name, functionNode, varFlags); + final VarNode varNode = new VarNode(functionLine, functionToken, finish, name, function, varFlags); if (topLevel) { functionDeclarations.add(varNode); } else if (useBlockScope()) { @@ -2725,7 +2754,7 @@ loop: } } - return functionNode; + return function; } private String getDefaultValidFunctionName(final int functionLine) { @@ -2832,15 +2861,19 @@ loop: * Parse function body. * @return function node (body.) */ - private FunctionNode functionBody(final long firstToken, final IdentNode ident, final List parameters, final FunctionNode.Kind kind, final int functionLine) { - FunctionNode functionNode = null; + private Block functionBody(final ParserContextFunctionNode functionNode) { long lastToken = 0L; + ParserContextBlockNode body = null; + final long bodyToken = token; + Block functionBody; + int bodyFinish = 0; + final boolean parseBody; Object endParserState = null; try { // Create a new function block. - functionNode = newFunctionNode(firstToken, ident, parameters, kind, functionLine); + body = newBlock(); assert functionNode != null; final int functionId = functionNode.getId(); parseBody = reparsedFunction == null || functionId <= reparsedFunction.getFunctionNodeId(); @@ -2856,6 +2889,7 @@ loop: // just expression as function body final Expression expr = assignmentExpression(true); lastToken = previousToken; + functionNode.setLastToken(previousToken); assert lc.getCurrentBlock() == lc.getFunctionBody(functionNode); // EOL uses length field to store the line number final int lastFinish = Token.descPosition(lastToken) + (Token.descType(lastToken) == EOL ? 0 : Token.descLength(lastToken)); @@ -2868,7 +2902,6 @@ loop: final ReturnNode returnNode = new ReturnNode(functionNode.getLineNumber(), expr.getToken(), lastFinish, expr); appendStatement(returnNode); } - functionNode.setFinish(lastFinish); } else { expectDontAdvance(LBRACE); if (parseBody || !skipFunctionBody(functionNode)) { @@ -2902,25 +2935,25 @@ loop: // we'll rather just restart parsing from this well-known, friendly token instead. } } + bodyFinish = finish; + functionNode.setLastToken(token); expect(RBRACE); - functionNode.setFinish(finish); } } finally { - functionNode = restoreFunctionNode(functionNode, lastToken); + restoreBlock(body); } // NOTE: we can only do alterations to the function node after restoreFunctionNode. if (parseBody) { - functionNode = functionNode.setEndParserState(lc, endParserState); - } else if (functionNode.getBody().getStatementCount() > 0){ + functionNode.setEndParserState(endParserState); + } else if (!body.getStatements().isEmpty()){ // This is to ensure the body is empty when !parseBody but we couldn't skip parsing it (see // skipFunctionBody() for possible reasons). While it is not strictly necessary for correctness to // enforce empty bodies in nested functions that were supposed to be skipped, we do assert it as // an invariant in few places in the compiler pipeline, so for consistency's sake we'll throw away // nested bodies early if we were supposed to skip 'em. - functionNode = functionNode.setBody(null, functionNode.getBody().setStatements(null, - Collections.emptyList())); + body.setStatements(Collections.emptyList()); } if (reparsedFunction != null) { @@ -2932,20 +2965,20 @@ loop: if (data != null) { // Data can be null if when we originally parsed the file, we removed the function declaration // as it was dead code. - functionNode = functionNode.setFlags(lc, data.getFunctionFlags()); + functionNode.setFlag(data.getFunctionFlags()); // This compensates for missing markEval() in case the function contains an inner function // that contains eval(), that now we didn't discover since we skipped the inner function. if (functionNode.hasNestedEval()) { assert functionNode.hasScopeBlock(); - functionNode = functionNode.setBody(lc, functionNode.getBody().setNeedsScope(null)); + body.setFlag(Block.NEEDS_SCOPE); } } } - printAST(functionNode); - return functionNode; + functionBody = new Block(bodyToken, bodyFinish, body.getFlags(), body.getStatements()); + return functionBody; } - private boolean skipFunctionBody(final FunctionNode functionNode) { + private boolean skipFunctionBody(final ParserContextFunctionNode functionNode) { if (reparsedFunction == null) { // Not reparsing, so don't skip any function body. return false; @@ -3008,13 +3041,13 @@ loop: } } - private void addFunctionDeclarations(final FunctionNode functionNode) { + private void addFunctionDeclarations(final ParserContextFunctionNode functionNode) { VarNode lastDecl = null; for (int i = functionDeclarations.size() - 1; i >= 0; i--) { Statement decl = functionDeclarations.get(i); if (lastDecl == null && decl instanceof VarNode) { decl = lastDecl = ((VarNode)decl).setFlag(VarNode.IS_LAST_FUNCTION_DECLARATION); - lc.setFlag(functionNode, FunctionNode.HAS_FUNCTION_DECLARATIONS); + functionNode.setFlag(FunctionNode.HAS_FUNCTION_DECLARATIONS); } prependStatement(decl); } @@ -3359,29 +3392,31 @@ loop: return "'JavaScript Parsing'"; } - private static void markEval(final LexicalContext lc) { - final Iterator iter = lc.getFunctions(); + private static void markEval(final ParserContext lc) { + final Iterator iter = lc.getFunctions(); boolean flaggedCurrentFn = false; while (iter.hasNext()) { - final FunctionNode fn = iter.next(); + final ParserContextFunctionNode fn = iter.next(); if (!flaggedCurrentFn) { - lc.setFlag(fn, FunctionNode.HAS_EVAL); + fn.setFlag(FunctionNode.HAS_EVAL); flaggedCurrentFn = true; } else { - lc.setFlag(fn, FunctionNode.HAS_NESTED_EVAL); + fn.setFlag(FunctionNode.HAS_NESTED_EVAL); } + final ParserContextBlockNode body = lc.getFunctionBody(fn); // NOTE: it is crucial to mark the body of the outer function as needing scope even when we skip // parsing a nested function. functionBody() contains code to compensate for the lack of invoking // this method when the parser skips a nested function. - lc.setBlockNeedsScope(lc.getFunctionBody(fn)); + body.setFlag(Block.NEEDS_SCOPE); + fn.setFlag(FunctionNode.HAS_SCOPE_BLOCK); } } private void prependStatement(final Statement statement) { - lc.prependStatement(statement); + lc.prependStatementToCurrentNode(statement); } private void appendStatement(final Statement statement) { - lc.appendStatement(statement); + lc.appendStatementToCurrentNode(statement); } } diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContext.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContext.java new file mode 100644 index 00000000000..7a90adfb0dc --- /dev/null +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContext.java @@ -0,0 +1,334 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.nashorn.internal.parser; + +import java.util.Iterator; +import java.util.NoSuchElementException; +import jdk.nashorn.internal.ir.Statement; + +/** + * A class that tracks the current lexical context of node visitation as a stack of {@code ParserContextNode} nodes. Has special + * methods to retrieve useful subsets of the context. + * + * This is implemented with a primitive array and a stack pointer, because it really makes a difference + * performance wise. None of the collection classes were optimal + */ + +class ParserContext { + + private ParserContextNode[] stack; + private int sp; + + private static final int INITIAL_DEPTH = 16; + + /** + * Constructs a ParserContext, + * initializes the stack + */ + public ParserContext(){ + this.sp = 0; + this.stack = new ParserContextNode[INITIAL_DEPTH]; + } + + /** + * Pushes a new block on top of the context, making it the innermost open block. + * @param node the new node + * @return The node that was pushed + */ + public T push(final T node) { + assert !contains(node); + if (sp == stack.length) { + final ParserContextNode[] newStack = new ParserContextNode[sp * 2]; + System.arraycopy(stack, 0, newStack, 0, sp); + stack = newStack; + } + stack[sp] = node; + sp++; + + return node; + } + + /** + * The topmost node on the stack + * @return The topmost node on the stack + */ + public ParserContextNode peek() { + return stack[sp - 1]; + } + + /** + * Removes and returns the topmost Node from the stack. + * @param node The node expected to be popped, used for sanity check + * @return The removed node + */ + public T pop(final T node) { + --sp; + @SuppressWarnings("unchecked") + final T popped = (T)stack[sp]; + stack[sp] = null; + assert node == popped; + + return popped; + } + + /** + * Tests if a node is on the stack. + * @param node The node to test + * @return true if stack contains node, false otherwise + */ + public boolean contains(final ParserContextNode node) { + for (int i = 0; i < sp; i++) { + if (stack[i] == node) { + return true; + } + } + return false; + } + + /** + * Returns the topmost {@link ParserContextBreakableNode} on the stack, null if none on stack + * @return Returns the topmost {@link ParserContextBreakableNode} on the stack, null if none on stack + */ + private ParserContextBreakableNode getBreakable() { + for (final NodeIterator iter = new NodeIterator<>(ParserContextBreakableNode.class, getCurrentFunction()); iter.hasNext(); ) { + final ParserContextBreakableNode next = iter.next(); + if (next.isBreakableWithoutLabel()) { + return next; + } + } + return null; + } + + + + /** + * Find the breakable node corresponding to this label. + * @param labelName name of the label to search for. If null, the closest breakable node will be returned + * unconditionally, e.g. a while loop with no label + * @return closest breakable node + */ + public ParserContextBreakableNode getBreakable(final String labelName) { + if (labelName != null) { + final ParserContextLabelNode foundLabel = findLabel(labelName); + if (foundLabel != null) { + // iterate to the nearest breakable to the foundLabel + ParserContextBreakableNode breakable = null; + for (final NodeIterator iter = new NodeIterator<>(ParserContextBreakableNode.class, foundLabel); iter.hasNext(); ) { + breakable = iter.next(); + } + return breakable; + } + return null; + } else { + return getBreakable(); + } + } + + /** + * Returns the loop node of the current loop, or null if not inside a loop + * @return loop noder + */ + public ParserContextLoopNode getCurrentLoop() { + final Iterator iter = new NodeIterator<>(ParserContextLoopNode.class, getCurrentFunction()); + return iter.hasNext() ? iter.next() : null; + } + + private ParserContextLoopNode getContinueTo() { + return getCurrentLoop(); + } + + /** + * Find the continue target node corresponding to this label. + * @param labelName label name to search for. If null the closest loop node will be returned unconditionally, e.g. a + * while loop with no label + * @return closest continue target node + */ + public ParserContextLoopNode getContinueTo(final String labelName) { + if (labelName != null) { + final ParserContextLabelNode foundLabel = findLabel(labelName); + if (foundLabel != null) { + // iterate to the nearest loop to the foundLabel + ParserContextLoopNode loop = null; + for (final NodeIterator iter = new NodeIterator<>(ParserContextLoopNode.class, foundLabel); iter.hasNext(); ) { + loop = iter.next(); + } + return loop; + } + return null; + } + return getContinueTo(); + } + + /** + * Get the function body of a function node on the stack. + * This will trigger an assertion if node isn't present + * @param functionNode function node + * @return body of function node + */ + public ParserContextBlockNode getFunctionBody(final ParserContextFunctionNode functionNode) { + for (int i = sp - 1; i >= 0 ; i--) { + if (stack[i] == functionNode) { + return (ParserContextBlockNode)stack[i + 1]; + } + } + throw new AssertionError(functionNode.getName() + " not on context stack"); + } + + /** + * Check the stack for a given label node by name + * @param name name of the label + * @return LabelNode if found, null otherwise + */ + public ParserContextLabelNode findLabel(final String name) { + for (final Iterator iter = new NodeIterator<>(ParserContextLabelNode.class, getCurrentFunction()); iter.hasNext(); ) { + final ParserContextLabelNode next = iter.next(); + if (next.getLabelName().equals(name)) { + return next; + } + } + return null; + } + + /** + * Prepends a statement to the current node. + * @param statement The statement to prepend + */ + public void prependStatementToCurrentNode(final Statement statement) { + assert statement != null; + stack[sp - 1].prependStatement(statement); + } + + /** + * Appends a statement to the current Node. + * @param statement The statement to append + */ + public void appendStatementToCurrentNode(final Statement statement) { + assert statement != null; + stack[sp - 1].appendStatement(statement); + } + + /** + * Returns the innermost function in the context. + * @return the innermost function in the context. + */ + public ParserContextFunctionNode getCurrentFunction() { + for (int i = sp - 1; i >= 0; i--) { + if (stack[i] instanceof ParserContextFunctionNode) { + return (ParserContextFunctionNode) stack[i]; + } + } + return null; + } + + /** + * Returns an iterator over all blocks in the context, with the top block (innermost lexical context) first. + * @return an iterator over all blocks in the context. + */ + public Iterator getBlocks() { + return new NodeIterator<>(ParserContextBlockNode.class); + } + + /** + * Returns the innermost block in the context. + * @return the innermost block in the context. + */ + public ParserContextBlockNode getCurrentBlock() { + return getBlocks().next(); + } + + /** + * The last statement added to the context + * @return The last statement added to the context + */ + public Statement getLastStatement() { + if (sp == 0) { + return null; + } + final ParserContextNode top = stack[sp - 1]; + final int s = top.getStatements().size(); + return s == 0 ? null : top.getStatements().get(s - 1); + } + + /** + * Returns an iterator over all functions in the context, with the top (innermost open) function first. + * @return an iterator over all functions in the context. + */ + public Iterator getFunctions() { + return new NodeIterator<>(ParserContextFunctionNode.class); + } + + private class NodeIterator implements Iterator { + private int index; + private T next; + private final Class clazz; + private ParserContextNode until; + + NodeIterator(final Class clazz) { + this(clazz, null); + } + + NodeIterator(final Class clazz, final ParserContextNode until) { + this.index = sp - 1; + this.clazz = clazz; + this.until = until; + this.next = findNext(); + } + + @Override + public boolean hasNext() { + return next != null; + } + + @Override + public T next() { + if (next == null) { + throw new NoSuchElementException(); + } + final T lnext = next; + next = findNext(); + return lnext; + } + + @SuppressWarnings("unchecked") + private T findNext() { + for (int i = index; i >= 0; i--) { + final Object node = stack[i]; + if (node == until) { + return null; + } + if (clazz.isAssignableFrom(node.getClass())) { + index = i - 1; + return (T)node; + } + } + return null; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } +} diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextBaseNode.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextBaseNode.java new file mode 100644 index 00000000000..262bc541d2e --- /dev/null +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextBaseNode.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.nashorn.internal.parser; + +import java.util.ArrayList; +import java.util.List; +import jdk.nashorn.internal.ir.Statement; + +/** + * Base class for parser context nodes + */ +abstract class ParserContextBaseNode implements ParserContextNode { + /** + * Flags for this node + */ + protected int flags; + + private List statements; + + /** + * Constructor + */ + public ParserContextBaseNode() { + this.statements = new ArrayList<>(); + } + + /** + * @return The flags for this node + */ + @Override + public int getFlags() { + return flags; + } + + /** + * Returns a single flag + * @param flag + * @return A single flag + */ + protected int getFlag(final int flag) { + return (flags & flag); + } + + /** + * @param flag + * @return the new flags + */ + @Override + public int setFlag(final int flag) { + flags |= flag; + return flags; + } + + /** + * @return The list of statements that belongs to this node + */ + @Override + public List getStatements() { + return statements; + } + + /** + * @param statements + */ + @Override + public void setStatements(final List statements) { + this.statements = statements; + } + + /** + * Adds a Statement at the end of the Statementlist + * @param statement The statement to add + */ + @Override + public void appendStatement(final Statement statement) { + this.statements.add(statement); + } + + /** + * Adds a statement at the begining of the statementlist + * @param statement The statement to add + */ + @Override + public void prependStatement(final Statement statement) { + this.statements.add(0, statement); + } +} diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextBlockNode.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextBlockNode.java new file mode 100644 index 00000000000..e87bd1293a9 --- /dev/null +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextBlockNode.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.nashorn.internal.parser; + +/** + * A ParserContextNode that represents a block that is currently being parsed + */ +class ParserContextBlockNode extends ParserContextBaseNode implements ParserContextBreakableNode { + + private final long token; + + /** + * Constructs a ParserContextBlockNode + * + * @param token The first token of the block + */ + public ParserContextBlockNode(final long token) { + this.token = token; + } + + @Override + public boolean isBreakableWithoutLabel() { + return false; + } + + /** + * Get token + * @return The first token of the block + */ + public long getToken() { + return token; + } + +} diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextBreakableNode.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextBreakableNode.java new file mode 100644 index 00000000000..cb0b46d9c14 --- /dev/null +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextBreakableNode.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.nashorn.internal.parser; + +import jdk.nashorn.internal.ir.BreakNode; + +/** + * An interface that is implemented by ParserContextNodes that can + * contain a {@link BreakNode} + */ +interface ParserContextBreakableNode extends ParserContextNode { + + /** + * Returns true if not i breakable without label, false otherwise + * @return Returns true if not i breakable without label, false otherwise + */ + boolean isBreakableWithoutLabel(); +} diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextFunctionNode.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextFunctionNode.java new file mode 100644 index 00000000000..5418caff915 --- /dev/null +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextFunctionNode.java @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.nashorn.internal.parser; + +import java.util.List; +import jdk.nashorn.internal.codegen.Namespace; +import jdk.nashorn.internal.ir.FunctionNode; +import jdk.nashorn.internal.ir.IdentNode; + +/** + * ParserContextNode that represents a function that is currently being parsed + */ +class ParserContextFunctionNode extends ParserContextBaseNode { + + /** Function name */ + private final String name; + + /** Function identifier node */ + private final IdentNode ident; + + /** Name space for function */ + private final Namespace namespace; + + /** Line number for function declaration */ + private final int line; + + /** Function node kind, see {@link FunctionNode#Kind} */ + private final FunctionNode.Kind kind; + + /** List of parameter identifiers for function */ + private final List parameters; + + /** Token for function start */ + private final long token; + + /** Last function token */ + private long lastToken; + + /** Opaque node for parser end state, see {@link Parser} */ + private Object endParserState; + + /** + * @param token The token for the function + * @param ident External function name + * @param name Internal name of the function + * @param namespace Function's namespace + * @param line The source line of the function + * @param kind Function kind + * @param parameters The parameters of the function + */ + public ParserContextFunctionNode(final long token, final IdentNode ident, final String name, final Namespace namespace, final int line, final FunctionNode.Kind kind, final List parameters) { + this.ident = ident; + this.namespace = namespace; + this.line = line; + this.kind = kind; + this.name = name; + this.parameters = parameters; + this.token = token; + } + + /** + * @return Internal name of the function + */ + public String getName() { + return name; + } + + /** + * @return The external identifier for the function + */ + public IdentNode getIdent() { + return ident; + } + + /** + * + * @return true if function is the program function + */ + public boolean isProgram() { + return getFlag(FunctionNode.IS_PROGRAM) != 0; + } + + /** + * @return if function in strict mode + */ + public boolean isStrict() { + return getFlag(FunctionNode.IS_STRICT) != 0; + } + + /** + * @return true if the function has nested evals + */ + public boolean hasNestedEval() { + return getFlag(FunctionNode.HAS_NESTED_EVAL) != 0; + } + + /** + * Returns true if any of the blocks in this function create their own scope. + * @return true if any of the blocks in this function create their own scope. + */ + public boolean hasScopeBlock() { + return getFlag(FunctionNode.HAS_SCOPE_BLOCK) != 0; + } + + /** + * Create a unique name in the namespace of this FunctionNode + * @param base prefix for name + * @return base if no collision exists, otherwise a name prefix with base + */ + public String uniqueName(final String base) { + return namespace.uniqueName(base); + } + + /** + * @return line number of the function + */ + public int getLineNumber() { + return line; + } + + /** + * @return The kind if function + */ + public FunctionNode.Kind getKind() { + return kind; + } + + /** + * Get parameters + * @return The parameters of the function + */ + public List getParameters() { + return parameters; + } + + /** + * Set last token + * @param token New last token + */ + public void setLastToken(final long token) { + this.lastToken = token; + + } + + /** + * @return lastToken Function's last token + */ + public long getLastToken() { + return lastToken; + } + + /** + * Returns the ParserState of when the parsing of this function was ended + * @return endParserState The end parser state + */ + public Object getEndParserState() { + return endParserState; + } + + /** + * Sets the ParserState of when the parsing of this function was ended + * @param endParserState The end parser state + */ + public void setEndParserState(final Object endParserState) { + this.endParserState = endParserState; + } + + /** + * Returns the if of this function + * @return The function id + */ + public int getId() { + return isProgram() ? -1 : Token.descPosition(token); + } +} diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextLabelNode.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextLabelNode.java new file mode 100644 index 00000000000..a5179107b3c --- /dev/null +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextLabelNode.java @@ -0,0 +1,52 @@ +/** +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.nashorn.internal.parser; + +/** + * ParserContextNode that represents a LabelNode + */ +class ParserContextLabelNode extends ParserContextBaseNode { + + /** Name for label */ + private final String name; + + /** + * Constructor + * + * @param name The name of the label + */ + public ParserContextLabelNode(final String name) { + this.name = name; + } + + /** + * Returns the name of the label + * @return name of label + */ + public String getLabelName() { + return name; + } +} diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextLoopNode.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextLoopNode.java new file mode 100644 index 00000000000..31e4b3ad5ea --- /dev/null +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextLoopNode.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.nashorn.internal.parser; + +/** + * A ParserContextNode that represents a loop that is being parsed + */ +class ParserContextLoopNode extends ParserContextBaseNode implements ParserContextBreakableNode { + + @Override + public boolean isBreakableWithoutLabel() { + return true; + } + +} diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextNode.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextNode.java new file mode 100644 index 00000000000..d44a106b8dd --- /dev/null +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextNode.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.nashorn.internal.parser; + +import java.util.List; +import jdk.nashorn.internal.ir.Statement; + +/** + * Used for keeping state when needed in the parser. + */ +interface ParserContextNode { + /** + * @return The flags for this node + */ + public int getFlags(); + + /** + * @param flag The flag to set + * @return All current flags after update + */ + public int setFlag(final int flag); + + /** + * @return The list of statements that belongs to this node + */ + public List getStatements(); + + /** + * @param statements The statement list + */ + public void setStatements(final List statements); + + /** + * Adds a Statement at the end of the Statementlist + * @param statement The statement to add + */ + public void appendStatement(final Statement statement); + + /** + * Adds a statement at the begining of the statementlist + * @param statement The statement to add + */ + public void prependStatement(final Statement statement); + +} diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextSwitchNode.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextSwitchNode.java new file mode 100644 index 00000000000..8c9c96a6a2d --- /dev/null +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextSwitchNode.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.nashorn.internal.parser; + +/** + * A ParserContextNode that represents a SwithcNode that is currently being parsed + */ +class ParserContextSwitchNode extends ParserContextBaseNode implements ParserContextBreakableNode { + + @Override + public boolean isBreakableWithoutLabel() { + return true; + } +} diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Context.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Context.java index e77c204afbb..ff543a36290 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Context.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Context.java @@ -483,7 +483,7 @@ public final class Context { final int cacheSize = env._class_cache_size; if (cacheSize > 0) { - classCache = new ClassCache(cacheSize); + classCache = new ClassCache(this, cacheSize); } if (env._persistent_cache) { @@ -1261,17 +1261,23 @@ public final class Context { * Cache for compiled script classes. */ @SuppressWarnings("serial") - private static class ClassCache extends LinkedHashMap { + @Logger(name="classcache") + private static class ClassCache extends LinkedHashMap implements Loggable { private final int size; private final ReferenceQueue> queue; + private final DebugLogger log; - ClassCache(final int size) { + ClassCache(final Context context, final int size) { super(size, 0.75f, true); this.size = size; this.queue = new ReferenceQueue<>(); + this.log = initLogger(context); } void cache(final Source source, final Class clazz) { + if (log.isEnabled()) { + log.info("Caching ", source, " in class cache"); + } put(source, new ClassReference(clazz, queue, source)); } @@ -1283,9 +1289,28 @@ public final class Context { @Override public ClassReference get(final Object key) { for (ClassReference ref; (ref = (ClassReference)queue.poll()) != null; ) { - remove(ref.source); + final Source source = ref.source; + if (log.isEnabled()) { + log.info("Evicting ", source, " from class cache."); + } + remove(source); } - return super.get(key); + + final ClassReference ref = super.get(key); + if (ref != null && log.isEnabled()) { + log.info("Retrieved class reference for ", ref.source, " from class cache"); + } + return ref; + } + + @Override + public DebugLogger initLogger(final Context context) { + return context.getLogger(getClass()); + } + + @Override + public DebugLogger getLogger() { + return log; } }