aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlana <none@none>2014-10-22 12:29:57 -0700
committerlana <none@none>2014-10-22 12:29:57 -0700
commit137cf71d0167701463f3e81bb84d03bddc06c229 (patch)
tree8c2b39e1f5d6491f7de4876327146825d1fe59d7
parent921a986600780ffa1e25e94af36239b57769dceb (diff)
parent2e7b8fc0e7d02fce056e97e50c1615e8ebc97e90 (diff)
downloadnashorn-137cf71d0167701463f3e81bb84d03bddc06c229.tar.gz
Merge
-rw-r--r--make/build-nasgen.xml2
-rw-r--r--make/build.xml49
-rw-r--r--src/jdk/nashorn/api/scripting/NashornScriptEngine.java28
-rw-r--r--src/jdk/nashorn/api/scripting/ScriptUtils.java20
-rw-r--r--src/jdk/nashorn/internal/codegen/ApplySpecialization.java4
-rw-r--r--src/jdk/nashorn/internal/codegen/AssignSymbols.java40
-rw-r--r--src/jdk/nashorn/internal/codegen/AstSerializer.java71
-rw-r--r--src/jdk/nashorn/internal/codegen/ClassEmitter.java8
-rw-r--r--src/jdk/nashorn/internal/codegen/CodeGenerator.java293
-rw-r--r--src/jdk/nashorn/internal/codegen/CompilationPhase.java210
-rw-r--r--src/jdk/nashorn/internal/codegen/CompileUnit.java33
-rw-r--r--src/jdk/nashorn/internal/codegen/Compiler.java219
-rw-r--r--src/jdk/nashorn/internal/codegen/FindScopeDepths.java7
-rw-r--r--src/jdk/nashorn/internal/codegen/Label.java11
-rw-r--r--src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java47
-rw-r--r--src/jdk/nashorn/internal/codegen/Lower.java3
-rw-r--r--src/jdk/nashorn/internal/codegen/MethodEmitter.java26
-rw-r--r--src/jdk/nashorn/internal/codegen/OptimisticTypesCalculator.java29
-rw-r--r--src/jdk/nashorn/internal/codegen/ProgramPoints.java2
-rw-r--r--src/jdk/nashorn/internal/codegen/ReplaceCompileUnits.java85
-rw-r--r--src/jdk/nashorn/internal/codegen/SplitIntoFunctions.java446
-rw-r--r--src/jdk/nashorn/internal/codegen/SplitMethodEmitter.java102
-rw-r--r--src/jdk/nashorn/internal/codegen/TypeEvaluator.java59
-rw-r--r--src/jdk/nashorn/internal/codegen/types/ArrayType.java1
-rw-r--r--src/jdk/nashorn/internal/codegen/types/BitwiseType.java1
-rw-r--r--src/jdk/nashorn/internal/codegen/types/BooleanType.java1
-rw-r--r--src/jdk/nashorn/internal/codegen/types/IntType.java1
-rw-r--r--src/jdk/nashorn/internal/codegen/types/LongType.java1
-rw-r--r--src/jdk/nashorn/internal/codegen/types/NumberType.java1
-rw-r--r--src/jdk/nashorn/internal/codegen/types/NumericType.java2
-rw-r--r--src/jdk/nashorn/internal/codegen/types/ObjectType.java1
-rw-r--r--src/jdk/nashorn/internal/codegen/types/Type.java33
-rw-r--r--src/jdk/nashorn/internal/ir/AccessNode.java2
-rw-r--r--src/jdk/nashorn/internal/ir/BaseNode.java1
-rw-r--r--src/jdk/nashorn/internal/ir/BinaryNode.java6
-rw-r--r--src/jdk/nashorn/internal/ir/Block.java2
-rw-r--r--src/jdk/nashorn/internal/ir/BlockLexicalContext.java10
-rw-r--r--src/jdk/nashorn/internal/ir/BlockStatement.java2
-rw-r--r--src/jdk/nashorn/internal/ir/BreakNode.java1
-rw-r--r--src/jdk/nashorn/internal/ir/BreakableStatement.java1
-rw-r--r--src/jdk/nashorn/internal/ir/CallNode.java5
-rw-r--r--src/jdk/nashorn/internal/ir/CaseNode.java2
-rw-r--r--src/jdk/nashorn/internal/ir/CatchNode.java2
-rw-r--r--src/jdk/nashorn/internal/ir/ContinueNode.java3
-rw-r--r--src/jdk/nashorn/internal/ir/EmptyNode.java1
-rw-r--r--src/jdk/nashorn/internal/ir/Expression.java2
-rw-r--r--src/jdk/nashorn/internal/ir/ExpressionStatement.java2
-rw-r--r--src/jdk/nashorn/internal/ir/ForNode.java2
-rw-r--r--src/jdk/nashorn/internal/ir/FunctionNode.java153
-rw-r--r--src/jdk/nashorn/internal/ir/GetSplitState.java70
-rw-r--r--src/jdk/nashorn/internal/ir/IdentNode.java2
-rw-r--r--src/jdk/nashorn/internal/ir/IfNode.java2
-rw-r--r--src/jdk/nashorn/internal/ir/IndexNode.java2
-rw-r--r--src/jdk/nashorn/internal/ir/JoinPredecessorExpression.java1
-rw-r--r--src/jdk/nashorn/internal/ir/JumpStatement.java1
-rw-r--r--src/jdk/nashorn/internal/ir/LabelNode.java2
-rw-r--r--src/jdk/nashorn/internal/ir/LexicalContext.java11
-rw-r--r--src/jdk/nashorn/internal/ir/LexicalContextExpression.java1
-rw-r--r--src/jdk/nashorn/internal/ir/LexicalContextStatement.java2
-rw-r--r--src/jdk/nashorn/internal/ir/LiteralNode.java23
-rw-r--r--src/jdk/nashorn/internal/ir/LoopNode.java2
-rw-r--r--src/jdk/nashorn/internal/ir/Node.java14
-rw-r--r--src/jdk/nashorn/internal/ir/ObjectNode.java1
-rw-r--r--src/jdk/nashorn/internal/ir/PropertyNode.java1
-rw-r--r--src/jdk/nashorn/internal/ir/ReturnNode.java2
-rw-r--r--src/jdk/nashorn/internal/ir/RuntimeNode.java1
-rw-r--r--src/jdk/nashorn/internal/ir/SetSplitState.java70
-rw-r--r--src/jdk/nashorn/internal/ir/SplitNode.java53
-rw-r--r--src/jdk/nashorn/internal/ir/SplitReturn.java64
-rw-r--r--src/jdk/nashorn/internal/ir/Statement.java1
-rw-r--r--src/jdk/nashorn/internal/ir/SwitchNode.java2
-rw-r--r--src/jdk/nashorn/internal/ir/Symbol.java17
-rw-r--r--src/jdk/nashorn/internal/ir/TernaryNode.java2
-rw-r--r--src/jdk/nashorn/internal/ir/ThrowNode.java2
-rw-r--r--src/jdk/nashorn/internal/ir/TryNode.java2
-rw-r--r--src/jdk/nashorn/internal/ir/UnaryNode.java2
-rw-r--r--src/jdk/nashorn/internal/ir/VarNode.java10
-rw-r--r--src/jdk/nashorn/internal/ir/WhileNode.java2
-rw-r--r--src/jdk/nashorn/internal/ir/WithNode.java2
-rw-r--r--src/jdk/nashorn/internal/ir/visitor/NodeVisitor.java63
-rw-r--r--src/jdk/nashorn/internal/objects/ArrayBufferView.java3
-rw-r--r--src/jdk/nashorn/internal/objects/NativeFloat32Array.java6
-rw-r--r--src/jdk/nashorn/internal/objects/NativeFloat64Array.java6
-rw-r--r--src/jdk/nashorn/internal/objects/NativeInt16Array.java16
-rw-r--r--src/jdk/nashorn/internal/objects/NativeInt32Array.java16
-rw-r--r--src/jdk/nashorn/internal/objects/NativeInt8Array.java16
-rw-r--r--src/jdk/nashorn/internal/objects/NativeJava.java6
-rw-r--r--src/jdk/nashorn/internal/objects/NativeJavaImporter.java24
-rw-r--r--src/jdk/nashorn/internal/objects/NativeUint16Array.java16
-rw-r--r--src/jdk/nashorn/internal/objects/NativeUint32Array.java13
-rw-r--r--src/jdk/nashorn/internal/objects/NativeUint8Array.java16
-rw-r--r--src/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java16
-rw-r--r--src/jdk/nashorn/internal/parser/Lexer.java8
-rw-r--r--src/jdk/nashorn/internal/runtime/AstDeserializer.java47
-rw-r--r--src/jdk/nashorn/internal/runtime/CompiledFunction.java14
-rw-r--r--src/jdk/nashorn/internal/runtime/Context.java13
-rw-r--r--src/jdk/nashorn/internal/runtime/GlobalConstants.java9
-rw-r--r--src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java79
-rw-r--r--src/jdk/nashorn/internal/runtime/WithObject.java13
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/ContinuousArrayData.java7
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/DeletedRangeArrayFilter.java12
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/IntArrayData.java15
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/LongArrayData.java19
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/NumberArrayData.java15
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/ObjectArrayData.java10
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java2
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java33
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/JavaAdapterClassLoader.java3
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/JavaAdapterServices.java3
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java91
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/NashornLinker.java2
-rw-r--r--src/jdk/nashorn/internal/runtime/resources/Messages.properties1
-rw-r--r--src/jdk/nashorn/internal/runtime/resources/mozilla_compat.js4
-rw-r--r--test/script/basic/JDK-8058610.js77
-rw-r--r--test/script/basic/JDK-8058610.js.EXPECTED18
-rw-r--r--test/script/basic/JDK-8060011.js58
-rw-r--r--test/script/basic/JDK-8060101.js54
-rw-r--r--test/script/basic/JDK-8061113.js35
-rw-r--r--test/script/basic/JDK-8061113.js.EXPECTED1
-rw-r--r--test/script/basic/convert.js2
-rw-r--r--test/script/nosecurity/JDK-8044798.js2
-rw-r--r--test/script/nosecurity/JDK-8060688.js56
-rw-r--r--test/src/jdk/nashorn/api/scripting/ScriptEngineSecurityTest.java36
-rw-r--r--test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java35
-rw-r--r--test/src/jdk/nashorn/internal/codegen/CompilerTest.java1
-rw-r--r--test/src/jdk/nashorn/internal/runtime/TrustedScriptEngineTest.java25
-rw-r--r--test/src/jdk/nashorn/internal/test/framework/ParallelTestRunner.java3
127 files changed, 2480 insertions, 940 deletions
diff --git a/make/build-nasgen.xml b/make/build-nasgen.xml
index 16094cc9..862a1999 100644
--- a/make/build-nasgen.xml
+++ b/make/build-nasgen.xml
@@ -25,7 +25,7 @@
<description>Builds and runs nasgen.</description>
<import file="build.xml"/>
- <target name="build-nasgen" depends="compile-asm">
+ <target name="build-nasgen" depends="prepare">
<ant inheritAll="false" dir="${basedir}/buildtools/nasgen"
antfile="build.xml" target="jar"/>
</target>
diff --git a/make/build.xml b/make/build.xml
index 9bb600dc..f6de5589 100644
--- a/make/build.xml
+++ b/make/build.xml
@@ -49,8 +49,6 @@
<condition property="git.executable" value="/usr/local/bin/git" else="git">
<available file="/usr/local/bin/git"/>
</condition>
- <!-- check if JDK already has ASM classes -->
- <available property="asm.available" classname="jdk.internal.org.objectweb.asm.Type"/>
<!-- check if testng.jar is avaiable -->
<available property="testng.available" file="${file.reference.testng.jar}"/>
<!-- check if Jemmy ang testng.jar are avaiable -->
@@ -78,8 +76,31 @@
<istrue value="${jfr}"/>
</condition>
</target>
+
+ <!-- check minimum ant version required to be 1.8.4 -->
+ <target name="check-ant-version">
+ <property name="ant.version.required" value="1.8.4"/>
+ <antversion property="ant.current.version" />
+ <fail message="The current ant version, ${ant.current.version}, is too old. Please use 1.8.4 or above.">
+ <condition>
+ <not>
+ <antversion atleast="${ant.version.required}"/>
+ </not>
+ </condition>
+ </fail>
+ </target>
+
+ <target name="check-java-version">
+ <!-- look for a Class that is available only in jdk1.8 or above -->
+ <!-- core/exposed API class is better than an implementation class -->
+ <available property="jdk1.8+" classname="java.util.stream.Stream"/>
+
+ <!-- need jdk1.8 or above -->
+ <fail message="Unsupported Java version: ${ant.java.version}. Please use Java version 1.8 or greater." unless="jdk1.8+">
+ </fail>
+ </target>
- <target name="init" depends="init-conditions, init-cc">
+ <target name="init" depends="check-ant-version, check-java-version, init-conditions, init-cc">
<!-- extends jvm args -->
<property name="run.test.jvmargs" value="${run.test.jvmargs.main} ${run.test.cc.jvmargs} ${jfr.options}"/>
<property name="run.test.jvmargs.octane" value="${run.test.jvmargs.octane.main} ${run.test.cc.jvmargs} ${jfr.options}"/>
@@ -107,19 +128,7 @@
<delete dir="${dist.dir}"/>
</target>
- <!-- do it only if ASM is not available -->
- <target name="compile-asm" depends="prepare" unless="asm.available">
- <javac srcdir="${jdk.asm.src.dir}"
- destdir="${build.classes.dir}"
- excludes="**/optimizer/* **/xml/* **/attrs/*"
- source="${javac.source}"
- target="${javac.target}"
- debug="${javac.debug}"
- encoding="${javac.encoding}"
- includeantruntime="false"/>
- </target>
-
- <target name="compile" depends="compile-asm" description="Compiles nashorn">
+ <target name="compile" depends="prepare" description="Compiles nashorn">
<javac srcdir="${src.dir}"
destdir="${build.classes.dir}"
classpath="${javac.classpath}"
@@ -407,7 +416,7 @@ grant codeBase "file:/${basedir}/test/script/markdown.js" {
<fileset id="test.nosecurity.classes" dir="${build.test.classes.dir}">
<include name="**/framework/ScriptTest.class"/>
</fileset>
- <testng outputdir="${build.nosecurity.test.results.dir}" classfilesetref="test.nosecurity.classes"
+ <testng outputdir="${build.nosecurity.test.results.dir}/${testResultsSubDir}" classfilesetref="test.nosecurity.classes"
verbose="${testng.verbose}" haltonfailure="true" useDefaultListeners="false" listeners="${testng.listeners}" workingDir="${basedir}">
<jvmarg line="${ext.class.path}"/>
<jvmarg line="${run.test.jvmargs} -Xmx${run.test.xmx} -Dbuild.dir=${build.dir}"/>
@@ -430,7 +439,7 @@ grant codeBase "file:/${basedir}/test/script/markdown.js" {
<target name="-test-security">
<delete dir="${build.dir}/nashorn_code_cache"/>
<property name="debug.test.jvmargs" value=""/>
- <testng outputdir="${build.test.results.dir}" classfilesetref="test.classes"
+ <testng outputdir="${build.test.results.dir}/${testResultsSubDir}" classfilesetref="test.classes"
verbose="${testng.verbose}" haltonfailure="true" useDefaultListeners="false" listeners="${testng.listeners}" workingDir="${basedir}">
<jvmarg line="${ext.class.path}"/>
<jvmarg line="${run.test.jvmargs} -Xmx${run.test.xmx} ${run.test.jvmsecurityargs} -Dbuild.dir=${build.dir}"/>
@@ -456,9 +465,11 @@ grant codeBase "file:/${basedir}/test/script/markdown.js" {
<echo message="Running test suite in OPTIMISTIC mode..."/>
<antcall target="-test-nosecurity" inheritRefs="true">
<param name="optimistic" value="true"/>
+ <param name="testResultsSubDir" value="optimistic"/>
</antcall>
<antcall target="-test-security" inheritRefs="true">
<param name="optimistic" value="true"/>
+ <param name="testResultsSubDir" value="optimistic"/>
</antcall>
</target>
@@ -466,9 +477,11 @@ grant codeBase "file:/${basedir}/test/script/markdown.js" {
<echo message="Running test suite in PESSIMISTIC mode..."/>
<antcall target="-test-nosecurity" inheritRefs="true">
<param name="optimistic" value="false"/>
+ <param name="testResultsSubDir" value="pessimistic"/>
</antcall>
<antcall target="-test-security" inheritRefs="true">
<param name="optimistic" value="false"/>
+ <param name="testResultsSubDir" value="pessimistic"/>
</antcall>
</target>
diff --git a/src/jdk/nashorn/api/scripting/NashornScriptEngine.java b/src/jdk/nashorn/api/scripting/NashornScriptEngine.java
index bbf1eecf..2f000954 100644
--- a/src/jdk/nashorn/api/scripting/NashornScriptEngine.java
+++ b/src/jdk/nashorn/api/scripting/NashornScriptEngine.java
@@ -229,6 +229,8 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
}
private <T> T getInterfaceInner(final Object thiz, final Class<T> clazz) {
+ assert !(thiz instanceof ScriptObject) : "raw ScriptObject not expected here";
+
if (clazz == null || !clazz.isInterface()) {
throw new IllegalArgumentException(getMessage("interface.class.expected"));
}
@@ -254,17 +256,6 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
if (! isOfContext(realGlobal, nashornContext)) {
throw new IllegalArgumentException(getMessage("script.object.from.another.engine"));
}
- } else if (thiz instanceof ScriptObject) {
- // called from script code.
- realSelf = (ScriptObject)thiz;
- realGlobal = Context.getGlobal();
- if (realGlobal == null) {
- throw new IllegalArgumentException(getMessage("no.current.nashorn.global"));
- }
-
- if (! isOfContext(realGlobal, nashornContext)) {
- throw new IllegalArgumentException(getMessage("script.object.from.another.engine"));
- }
}
if (realSelf == null) {
@@ -368,6 +359,7 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
private Object invokeImpl(final Object selfObject, final String name, final Object... args) throws ScriptException, NoSuchMethodException {
name.getClass(); // null check
+ assert !(selfObject instanceof ScriptObject) : "raw ScriptObject not expected here";
Global invokeGlobal = null;
ScriptObjectMirror selfMirror = null;
@@ -377,20 +369,6 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
throw new IllegalArgumentException(getMessage("script.object.from.another.engine"));
}
invokeGlobal = selfMirror.getHomeGlobal();
- } else if (selfObject instanceof ScriptObject) {
- // invokeMethod called from script code - in which case we may get 'naked' ScriptObject
- // Wrap it with oldGlobal to make a ScriptObjectMirror for the same.
- final Global oldGlobal = Context.getGlobal();
- invokeGlobal = oldGlobal;
- if (oldGlobal == null) {
- throw new IllegalArgumentException(getMessage("no.current.nashorn.global"));
- }
-
- if (! isOfContext(oldGlobal, nashornContext)) {
- throw new IllegalArgumentException(getMessage("script.object.from.another.engine"));
- }
-
- selfMirror = (ScriptObjectMirror)ScriptObjectMirror.wrap(selfObject, oldGlobal);
} else if (selfObject == null) {
// selfObject is null => global function call
final Global ctxtGlobal = getNashornGlobalFrom(context);
diff --git a/src/jdk/nashorn/api/scripting/ScriptUtils.java b/src/jdk/nashorn/api/scripting/ScriptUtils.java
index 4de2cbf5..b6c4c97e 100644
--- a/src/jdk/nashorn/api/scripting/ScriptUtils.java
+++ b/src/jdk/nashorn/api/scripting/ScriptUtils.java
@@ -75,11 +75,8 @@ public final class ScriptUtils {
* @param sync the object to synchronize on
* @return a synchronizing wrapper function
*/
- public static Object makeSynchronizedFunction(final Object func, final Object sync) {
- if (func instanceof ScriptFunction) {
- return ((ScriptFunction)func).makeSynchronizedFunction(sync);
- }
- throw typeError("not.a.function", ScriptRuntime.safeToString(func));
+ public static Object makeSynchronizedFunction(final ScriptFunction func, final Object sync) {
+ return func.makeSynchronizedFunction(unwrap(sync));
}
/**
@@ -88,12 +85,8 @@ public final class ScriptUtils {
* @param obj object to be wrapped
* @return wrapped object
*/
- public static Object wrap(final Object obj) {
- if (obj instanceof ScriptObject) {
- return ScriptObjectMirror.wrap(obj, Context.getGlobal());
- }
-
- return obj;
+ public static ScriptObjectMirror wrap(final ScriptObject obj) {
+ return (ScriptObjectMirror) ScriptObjectMirror.wrap(obj, Context.getGlobal());
}
/**
@@ -160,14 +153,15 @@ public final class ScriptUtils {
}
final LinkerServices linker = Bootstrap.getLinkerServices();
- final MethodHandle converter = linker.getTypeConverter(obj.getClass(), clazz);
+ final Object objToConvert = unwrap(obj);
+ final MethodHandle converter = linker.getTypeConverter(objToConvert.getClass(), clazz);
if (converter == null) {
// no supported conversion!
throw new UnsupportedOperationException("conversion not supported");
}
try {
- return converter.invoke(obj);
+ return converter.invoke(objToConvert);
} catch (final RuntimeException | Error e) {
throw e;
} catch (final Throwable t) {
diff --git a/src/jdk/nashorn/internal/codegen/ApplySpecialization.java b/src/jdk/nashorn/internal/codegen/ApplySpecialization.java
index b05475b6..cdfe34f2 100644
--- a/src/jdk/nashorn/internal/codegen/ApplySpecialization.java
+++ b/src/jdk/nashorn/internal/codegen/ApplySpecialization.java
@@ -27,6 +27,7 @@ package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS_VAR;
import static jdk.nashorn.internal.codegen.CompilerConstants.EXPLODED_ARGUMENT_PREFIX;
+
import java.lang.invoke.MethodType;
import java.util.ArrayDeque;
import java.util.ArrayList;
@@ -38,6 +39,7 @@ import jdk.nashorn.internal.ir.AccessNode;
import jdk.nashorn.internal.ir.CallNode;
import jdk.nashorn.internal.ir.Expression;
import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.Node;
@@ -321,7 +323,7 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
explodedArguments.pop();
- return newFunctionNode;
+ return newFunctionNode.setState(lc, CompilationState.BUILTINS_TRANSFORMED);
}
private static boolean isApply(final CallNode callNode) {
diff --git a/src/jdk/nashorn/internal/codegen/AssignSymbols.java b/src/jdk/nashorn/internal/codegen/AssignSymbols.java
index fa2b8a19..88fd89bb 100644
--- a/src/jdk/nashorn/internal/codegen/AssignSymbols.java
+++ b/src/jdk/nashorn/internal/codegen/AssignSymbols.java
@@ -76,7 +76,6 @@ import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.RuntimeNode;
import jdk.nashorn.internal.ir.RuntimeNode.Request;
-import jdk.nashorn.internal.ir.SplitNode;
import jdk.nashorn.internal.ir.Statement;
import jdk.nashorn.internal.ir.SwitchNode;
import jdk.nashorn.internal.ir.Symbol;
@@ -135,9 +134,6 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
if (!(functionNode.hasScopeBlock() || functionNode.needsParentScope())) {
functionNode.compilerConstant(SCOPE).setNeedsSlot(false);
}
- if (!functionNode.usesReturnSymbol()) {
- functionNode.compilerConstant(RETURN).setNeedsSlot(false);
- }
// Named function expressions that end up not referencing themselves won't need a local slot for the self symbol.
if(!functionNode.isDeclared() && !functionNode.usesSelfSymbol() && !functionNode.isAnonymous()) {
final Symbol selfSymbol = functionNode.getBody().getExistingSymbol(functionNode.getIdent().getName());
@@ -511,16 +507,6 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
thisProperties.push(new HashSet<String>());
- if (functionNode.isDeclared()) {
- // Can't use lc.getCurrentBlock() as we can have an outermost function in our lexical context that
- // is not a program - it is a function being compiled on-demand.
- final Iterator<Block> blocks = lc.getBlocks();
- if (blocks.hasNext()) {
- final IdentNode ident = functionNode.getIdent();
- defineSymbol(blocks.next(), ident.getName(), ident, IS_VAR | (functionNode.isAnonymous()? IS_INTERNAL : 0));
- }
- }
-
// Every function has a body, even the ones skipped on reparse (they have an empty one). We're
// asserting this as even for those, enterBlock() must be invoked to correctly process symbols that
// are used in them.
@@ -532,16 +518,36 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
@Override
public boolean enterVarNode(final VarNode varNode) {
start(varNode);
+ // Normally, a symbol assigned in a var statement is not live for its RHS. Since we also represent function
+ // declarations as VarNodes, they are exception to the rule, as they need to have the symbol visible to the
+ // body of the declared function for self-reference.
+ if (varNode.isFunctionDeclaration()) {
+ defineVarIdent(varNode);
+ }
return true;
}
@Override
public Node leaveVarNode(final VarNode varNode) {
- final IdentNode ident = varNode.getName();
- defineSymbol(lc.getCurrentBlock(), ident.getName(), ident, varNode.getSymbolFlags() | (lc.getCurrentFunction().isProgram() ? IS_SCOPE : 0));
+ if (!varNode.isFunctionDeclaration()) {
+ defineVarIdent(varNode);
+ }
return super.leaveVarNode(varNode);
}
+ private void defineVarIdent(final VarNode varNode) {
+ final IdentNode ident = varNode.getName();
+ final int flags;
+ if (varNode.isAnonymousFunctionDeclaration()) {
+ flags = IS_INTERNAL;
+ } else if (lc.getCurrentFunction().isProgram()) {
+ flags = IS_SCOPE;
+ } else {
+ flags = 0;
+ }
+ defineSymbol(lc.getCurrentBlock(), ident.getName(), ident, varNode.getSymbolFlags() | flags);
+ }
+
private Symbol exceptionSymbol() {
return newObjectInternal(EXCEPTION_PREFIX);
}
@@ -1004,7 +1010,7 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
boolean previousWasBlock = false;
for (final Iterator<LexicalContextNode> it = lc.getAllNodes(); it.hasNext();) {
final LexicalContextNode node = it.next();
- if (node instanceof FunctionNode || node instanceof SplitNode || isSplitArray(node)) {
+ if (node instanceof FunctionNode || isSplitArray(node)) {
// We reached the function boundary or a splitting boundary without seeing a definition for the symbol.
// It needs to be in scope.
return true;
diff --git a/src/jdk/nashorn/internal/codegen/AstSerializer.java b/src/jdk/nashorn/internal/codegen/AstSerializer.java
new file mode 100644
index 00000000..19197a26
--- /dev/null
+++ b/src/jdk/nashorn/internal/codegen/AstSerializer.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.nashorn.internal.codegen;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.util.Collections;
+import java.util.zip.Deflater;
+import java.util.zip.DeflaterOutputStream;
+import jdk.nashorn.internal.ir.Block;
+import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.ir.LexicalContext;
+import jdk.nashorn.internal.ir.Node;
+import jdk.nashorn.internal.ir.Statement;
+import jdk.nashorn.internal.ir.visitor.NodeVisitor;
+import jdk.nashorn.internal.runtime.options.Options;
+
+/**
+ * This static utility class performs serialization of FunctionNode ASTs to a byte array.
+ * The format is a standard Java serialization stream, deflated.
+ */
+final class AstSerializer {
+ // Experimentally, we concluded that compression level 4 gives a good tradeoff between serialization speed
+ // and size.
+ private static final int COMPRESSION_LEVEL = Options.getIntProperty("nashorn.serialize.compression", 4);
+ static byte[] serialize(final FunctionNode fn) {
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ try (final ObjectOutputStream oout = new ObjectOutputStream(new DeflaterOutputStream(out,
+ new Deflater(COMPRESSION_LEVEL)))) {
+ oout.writeObject(removeInnerFunctionBodies(fn));
+ } catch (final IOException e) {
+ throw new AssertionError("Unexpected exception serializing function", e);
+ }
+ return out.toByteArray();
+ }
+
+ private static FunctionNode removeInnerFunctionBodies(final FunctionNode fn) {
+ return (FunctionNode)fn.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
+ @Override
+ public Node leaveBlock(final Block block) {
+ if (lc.isFunctionBody() && lc.getFunction(block) != lc.getOutermostFunction()) {
+ return block.setStatements(lc, Collections.<Statement>emptyList());
+ }
+ return super.leaveBlock(block);
+ }
+ });
+ }
+}
diff --git a/src/jdk/nashorn/internal/codegen/ClassEmitter.java b/src/jdk/nashorn/internal/codegen/ClassEmitter.java
index 7a89e490..80bb61dd 100644
--- a/src/jdk/nashorn/internal/codegen/ClassEmitter.java
+++ b/src/jdk/nashorn/internal/codegen/ClassEmitter.java
@@ -51,6 +51,7 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.className;
import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor;
import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor;
import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
+
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.security.AccessController;
@@ -64,7 +65,6 @@ import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.util.TraceClassVisitor;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.FunctionNode;
-import jdk.nashorn.internal.ir.SplitNode;
import jdk.nashorn.internal.ir.debug.NashornClassReader;
import jdk.nashorn.internal.ir.debug.NashornTextifier;
import jdk.nashorn.internal.runtime.Context;
@@ -476,12 +476,6 @@ public class ClassEmitter implements Emitter {
methodsStarted.remove(method);
}
- SplitMethodEmitter method(final SplitNode splitNode, final String methodName, final Class<?> rtype, final Class<?>... ptypes) {
- methodCount++;
- methodNames.add(methodName);
- return new SplitMethodEmitter(this, methodVisitor(EnumSet.of(Flag.PUBLIC, Flag.STATIC), methodName, rtype, ptypes), splitNode);
- }
-
/**
* Add a new method to the class - defaults to public method
*
diff --git a/src/jdk/nashorn/internal/codegen/CodeGenerator.java b/src/jdk/nashorn/internal/codegen/CodeGenerator.java
index ae3ee694..d6743631 100644
--- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java
+++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java
@@ -34,9 +34,7 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.GET_MAP;
import static jdk.nashorn.internal.codegen.CompilerConstants.GET_STRING;
import static jdk.nashorn.internal.codegen.CompilerConstants.QUICK_PREFIX;
import static jdk.nashorn.internal.codegen.CompilerConstants.REGEX_PREFIX;
-import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
-import static jdk.nashorn.internal.codegen.CompilerConstants.SPLIT_ARRAY_ARG;
import static jdk.nashorn.internal.codegen.CompilerConstants.SPLIT_PREFIX;
import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
@@ -99,10 +97,10 @@ import jdk.nashorn.internal.ir.ExpressionStatement;
import jdk.nashorn.internal.ir.ForNode;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
+import jdk.nashorn.internal.ir.GetSplitState;
import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.IfNode;
import jdk.nashorn.internal.ir.IndexNode;
-import jdk.nashorn.internal.ir.JoinPredecessor;
import jdk.nashorn.internal.ir.JoinPredecessorExpression;
import jdk.nashorn.internal.ir.JumpStatement;
import jdk.nashorn.internal.ir.LabelNode;
@@ -121,7 +119,8 @@ import jdk.nashorn.internal.ir.PropertyNode;
import jdk.nashorn.internal.ir.ReturnNode;
import jdk.nashorn.internal.ir.RuntimeNode;
import jdk.nashorn.internal.ir.RuntimeNode.Request;
-import jdk.nashorn.internal.ir.SplitNode;
+import jdk.nashorn.internal.ir.SetSplitState;
+import jdk.nashorn.internal.ir.SplitReturn;
import jdk.nashorn.internal.ir.Statement;
import jdk.nashorn.internal.ir.SwitchNode;
import jdk.nashorn.internal.ir.Symbol;
@@ -493,8 +492,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
//walk up the chain from starting block and when we bump into the current function boundary, add the external
//information.
final FunctionNode fn = lc.getCurrentFunction();
- final int fnId = fn.getId();
- final int externalDepth = compiler.getScriptFunctionData(fnId).getExternalSymbolDepth(symbol.getName());
+ final int externalDepth = compiler.getScriptFunctionData(fn.getId()).getExternalSymbolDepth(symbol.getName());
//count the number of scopes from this place to the start of the function
@@ -554,10 +552,10 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
}
MethodEmitter loadBinaryOperands(final BinaryNode binaryNode) {
- return loadBinaryOperands(binaryNode.lhs(), binaryNode.rhs(), TypeBounds.UNBOUNDED.notWiderThan(binaryNode.getWidestOperandType()), false);
+ return loadBinaryOperands(binaryNode.lhs(), binaryNode.rhs(), TypeBounds.UNBOUNDED.notWiderThan(binaryNode.getWidestOperandType()), false, false);
}
- private MethodEmitter loadBinaryOperands(final Expression lhs, final Expression rhs, final TypeBounds explicitOperandBounds, final boolean baseAlreadyOnStack) {
+ private MethodEmitter loadBinaryOperands(final Expression lhs, final Expression rhs, final TypeBounds explicitOperandBounds, final boolean baseAlreadyOnStack, final boolean forceConversionSeparation) {
// ECMAScript 5.1 specification (sections 11.5-11.11 and 11.13) prescribes that when evaluating a binary
// expression "LEFT op RIGHT", the order of operations must be: LOAD LEFT, LOAD RIGHT, CONVERT LEFT, CONVERT
// RIGHT, EXECUTE OP. Unfortunately, doing it in this order defeats potential optimizations that arise when we
@@ -574,15 +572,34 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
final Type narrowestOperandType = Type.narrowest(Type.widest(lhs.getType(), rhs.getType()), explicitOperandBounds.widest);
final TypeBounds operandBounds = explicitOperandBounds.notNarrowerThan(narrowestOperandType);
if (noToPrimitiveConversion(lhs.getType(), explicitOperandBounds.widest) || rhs.isLocal()) {
- // Can reorder. Combine load and convert into single operations.
- loadExpression(lhs, operandBounds, baseAlreadyOnStack);
- loadExpression(rhs, operandBounds, false);
+ // Can reorder. We might still need to separate conversion, but at least we can do it with reordering
+ if (forceConversionSeparation) {
+ // Can reorder, but can't move conversion into the operand as the operation depends on operands
+ // exact types for its overflow guarantees. E.g. with {L}{%I}expr1 {L}* {L}{%I}expr2 we are not allowed
+ // to merge {L}{%I} into {%L}, as that can cause subsequent overflows; test for JDK-8058610 contains
+ // concrete cases where this could happen.
+ final TypeBounds safeConvertBounds = TypeBounds.UNBOUNDED.notNarrowerThan(narrowestOperandType);
+ loadExpression(lhs, safeConvertBounds, baseAlreadyOnStack);
+ method.convert(operandBounds.within(method.peekType()));
+ loadExpression(rhs, safeConvertBounds, false);
+ method.convert(operandBounds.within(method.peekType()));
+ } else {
+ // Can reorder and move conversion into the operand. Combine load and convert into single operations.
+ loadExpression(lhs, operandBounds, baseAlreadyOnStack);
+ loadExpression(rhs, operandBounds, false);
+ }
} else {
// Can't reorder. Load and convert separately.
final TypeBounds safeConvertBounds = TypeBounds.UNBOUNDED.notNarrowerThan(narrowestOperandType);
loadExpression(lhs, safeConvertBounds, baseAlreadyOnStack);
+ final Type lhsType = method.peekType();
loadExpression(rhs, safeConvertBounds, false);
- method.swap().convert(operandBounds.within(method.peekType())).swap().convert(operandBounds.within(method.peekType()));
+ final Type convertedLhsType = operandBounds.within(method.peekType());
+ if (convertedLhsType != lhsType) {
+ // Do it conditionally, so that if conversion is a no-op we don't introduce a SWAP, SWAP.
+ method.swap().convert(convertedLhsType).swap();
+ }
+ method.convert(operandBounds.within(method.peekType()));
}
assert Type.generic(method.peekType()) == operandBounds.narrowest;
assert Type.generic(method.peekType(1)) == operandBounds.narrowest;
@@ -633,19 +650,11 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
}
TypeBounds booleanToInt() {
- return maybeNew(booleanToInt(narrowest), booleanToInt(widest));
+ return maybeNew(CodeGenerator.booleanToInt(narrowest), CodeGenerator.booleanToInt(widest));
}
TypeBounds objectToNumber() {
- return maybeNew(objectToNumber(narrowest), objectToNumber(widest));
- }
-
- private static Type booleanToInt(final Type t) {
- return t == Type.BOOLEAN ? Type.INT : t;
- }
-
- private static Type objectToNumber(final Type t) {
- return t.isObject() ? Type.NUMBER : t;
+ return maybeNew(CodeGenerator.objectToNumber(narrowest), CodeGenerator.objectToNumber(widest));
}
Type within(final Type type) {
@@ -664,6 +673,14 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
}
}
+ private static Type booleanToInt(final Type t) {
+ return t == Type.BOOLEAN ? Type.INT : t;
+ }
+
+ private static Type objectToNumber(final Type t) {
+ return t.isObject() ? Type.NUMBER : t;
+ }
+
MethodEmitter loadExpressionAsType(final Expression expr, final Type type) {
if(type == Type.BOOLEAN) {
return loadExpressionAsBoolean(expr);
@@ -1048,6 +1065,13 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
}
@Override
+ public boolean enterGetSplitState(final GetSplitState getSplitState) {
+ method.loadScope();
+ method.invoke(Scope.GET_SPLIT_STATE);
+ return false;
+ }
+
+ @Override
public boolean enterDefault(final Node otherNode) {
// Must have handled all expressions that can legally be encountered.
throw new AssertionError(otherNode.getClass().getName());
@@ -1219,7 +1243,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
popScopesUntil(target);
final Label targetLabel = jump.getTargetLabel(target);
targetLabel.markAsBreakTarget();
- method.splitAwareGoto(lc, targetLabel, target);
+ method._goto(targetLabel);
return false;
}
@@ -2029,10 +2053,10 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
}
private void lineNumber(final int lineNumber) {
- if (lineNumber != lastLineNumber) {
+ if (lineNumber != lastLineNumber && lineNumber != Node.NO_LINE_NUMBER) {
method.lineNumber(lineNumber);
+ lastLineNumber = lineNumber;
}
- lastLineNumber = lineNumber;
}
int getLastLineNumber() {
@@ -2079,13 +2103,14 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
method.begin();
defineCommonSplitMethodParameters();
- defineSplitMethodParameter(3, arrayType);
+ defineSplitMethodParameter(CompilerConstants.SPLIT_ARRAY_ARG.slot(), arrayType);
- fixScopeSlot(currentFunction);
+ // NOTE: when this is no longer needed, SplitIntoFunctions will no longer have to add IS_SPLIT
+ // to synthetic functions, and FunctionNode.needsCallee() will no longer need to test for isSplit().
+ final int arraySlot = fixScopeSlot(currentFunction, 3);
lc.enterSplitNode();
- final int arraySlot = SPLIT_ARRAY_ARG.slot();
for (int i = arrayUnit.getLo(); i < arrayUnit.getHi(); i++) {
method.load(arrayType, arraySlot);
storeElement(nodes, elementType, postsets[i]);
@@ -2700,73 +2725,6 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
method.convert(newRuntimeNode.getType());
}
- @Override
- public boolean enterSplitNode(final SplitNode splitNode) {
- if(!method.isReachable()) {
- return false;
- }
-
- final CompileUnit splitCompileUnit = splitNode.getCompileUnit();
-
- final FunctionNode fn = lc.getCurrentFunction();
- final String className = splitCompileUnit.getUnitClassName();
- final String name = splitNode.getName();
-
- final Type returnType = fn.getReturnType();
-
- final Class<?> rtype = fn.getReturnType().getTypeClass();
- final boolean needsArguments = fn.needsArguments();
- final Class<?>[] ptypes = needsArguments ?
- new Class<?>[] {ScriptFunction.class, Object.class, ScriptObject.class, ScriptObject.class} :
- new Class<?>[] {ScriptFunction.class, Object.class, ScriptObject.class};
-
- final MethodEmitter caller = method;
- unit = lc.pushCompileUnit(splitCompileUnit);
-
- final Call splitCall = staticCallNoLookup(
- className,
- name,
- methodDescriptor(rtype, ptypes));
-
- final MethodEmitter splitEmitter =
- splitCompileUnit.getClassEmitter().method(
- splitNode,
- name,
- rtype,
- ptypes);
-
- pushMethodEmitter(splitEmitter);
- method.setFunctionNode(fn);
-
- assert fn.needsCallee() : "split function should require callee";
- caller.loadCompilerConstant(CALLEE);
- caller.loadCompilerConstant(THIS);
- caller.loadCompilerConstant(SCOPE);
- if (needsArguments) {
- caller.loadCompilerConstant(ARGUMENTS);
- }
- caller.invoke(splitCall);
- caller.storeCompilerConstant(RETURN, returnType);
-
- method.begin();
-
- defineCommonSplitMethodParameters();
- if(needsArguments) {
- defineSplitMethodParameter(3, ARGUMENTS);
- }
-
- // Copy scope to its target slot as first thing because the original slot could be used by return symbol.
- fixScopeSlot(fn);
-
- final int returnSlot = fn.compilerConstant(RETURN).getSlot(returnType);
- method.defineBlockLocalVariable(returnSlot, returnSlot + returnType.getSlots());
- method.loadUndefined(returnType);
- method.storeCompilerConstant(RETURN, returnType);
-
- lc.enterSplitNode();
- return true;
- }
-
private void defineCommonSplitMethodParameters() {
defineSplitMethodParameter(0, CALLEE);
defineSplitMethodParameter(1, THIS);
@@ -2782,114 +2740,40 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
method.onLocalStore(type, slot);
}
- private void fixScopeSlot(final FunctionNode functionNode) {
+ private int fixScopeSlot(final FunctionNode functionNode, final int extraSlot) {
// TODO hack to move the scope to the expected slot (needed because split methods reuse the same slots as the root method)
final int actualScopeSlot = functionNode.compilerConstant(SCOPE).getSlot(SCOPE_TYPE);
final int defaultScopeSlot = SCOPE.slot();
+ int newExtraSlot = extraSlot;
if (actualScopeSlot != defaultScopeSlot) {
- method.defineBlockLocalVariable(actualScopeSlot, actualScopeSlot + 1);
+ if (actualScopeSlot == extraSlot) {
+ newExtraSlot = extraSlot + 1;
+ method.defineBlockLocalVariable(newExtraSlot, newExtraSlot + 1);
+ method.load(Type.OBJECT, extraSlot);
+ method.storeHidden(Type.OBJECT, newExtraSlot);
+ } else {
+ method.defineBlockLocalVariable(actualScopeSlot, actualScopeSlot + 1);
+ }
method.load(SCOPE_TYPE, defaultScopeSlot);
method.storeCompilerConstant(SCOPE);
}
+ return newExtraSlot;
}
@Override
- public Node leaveSplitNode(final SplitNode splitNode) {
- assert method instanceof SplitMethodEmitter;
- lc.exitSplitNode();
- final boolean hasReturn = method.hasReturn();
- final SplitMethodEmitter splitMethod = ((SplitMethodEmitter)method);
- final List<Label> targets = splitMethod.getExternalTargets();
- final boolean hasControlFlow = hasReturn || !targets.isEmpty();
- final List<BreakableNode> targetNodes = splitMethod.getExternalTargetNodes();
- final Type returnType = lc.getCurrentFunction().getReturnType();
-
- try {
- // Wrap up this method.
-
- if(method.isReachable()) {
- if (hasControlFlow) {
- method.setSplitState(-1);
- }
- method.loadCompilerConstant(RETURN, returnType);
- method._return(returnType);
- }
- method.end();
-
- lc.releaseSlots();
-
- unit = lc.popCompileUnit(splitNode.getCompileUnit());
- popMethodEmitter();
-
- } catch (final Throwable t) {
- Context.printStackTrace(t);
- final VerifyError e = new VerifyError("Code generation bug in \"" + splitNode.getName() + "\": likely stack misaligned: " + t + " " + getCurrentSource().getName());
- e.initCause(t);
- throw e;
- }
-
- //no external jump targets or return in switch node
- if (!hasControlFlow) {
- return splitNode;
- }
-
- // Handle return from split method if there was one.
- final MethodEmitter caller = method;
- final int targetCount = targets.size();
-
- caller.loadScope();
- caller.invoke(Scope.GET_SPLIT_STATE);
-
- final Label breakLabel = new Label("no_split_state");
- // Split state is -1 for no split state, 0 for return, 1..n+1 for break/continue
-
- //the common case is that we don't need a switch
- if (targetCount == 0) {
- assert hasReturn;
- caller.ifne(breakLabel);
- //has to be zero
- caller.label(new Label("split_return"));
- caller.loadCompilerConstant(RETURN, returnType);
- caller._return(returnType);
- caller.label(breakLabel);
- } else {
- assert !targets.isEmpty();
-
- final int low = hasReturn ? 0 : 1;
- final int labelCount = targetCount + 1 - low;
- final Label[] labels = new Label[labelCount];
-
- for (int i = 0; i < labelCount; i++) {
- labels[i] = new Label(i == 0 ? "split_return" : "split_" + targets.get(i - 1));
- }
- caller.tableswitch(low, targetCount, breakLabel, labels);
- for (int i = low; i <= targetCount; i++) {
- caller.label(labels[i - low]);
- if (i == 0) {
- caller.loadCompilerConstant(RETURN, returnType);
- caller._return(returnType);
- } else {
- final BreakableNode targetNode = targetNodes.get(i - 1);
- final Label label = targets.get(i - 1);
- if (!lc.isExternalTarget(splitNode, targetNode)) {
- final JoinPredecessor jumpOrigin = splitNode.getJumpOrigin(label);
- if(jumpOrigin != null) {
- method.beforeJoinPoint(jumpOrigin);
- }
- popScopesUntil(targetNode);
- }
- caller.splitAwareGoto(lc, label, targetNode);
- }
- }
- caller.label(breakLabel);
+ public boolean enterSplitReturn(final SplitReturn splitReturn) {
+ if (method.isReachable()) {
+ method.loadUndefined(lc.getCurrentFunction().getReturnType())._return();
}
+ return false;
+ }
- // If split has a return and caller is itself a split method it needs to propagate the return.
- if (hasReturn) {
- caller.setHasReturn();
+ @Override
+ public boolean enterSetSplitState(final SetSplitState setSplitState) {
+ if (method.isReachable()) {
+ method.setSplitState(setSplitState.getState());
}
-
- return splitNode;
+ return false;
}
@Override
@@ -3678,13 +3562,15 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
void loadStack() {
final TypeBounds operandBounds;
final boolean isOptimistic = isValid(getProgramPoint());
+ boolean forceConversionSeparation = false;
if(isOptimistic) {
operandBounds = new TypeBounds(binaryNode.getType(), Type.OBJECT);
} else {
// Non-optimistic, non-FP +. Allow it to overflow.
operandBounds = new TypeBounds(binaryNode.getWidestOperandType(), Type.OBJECT);
+ forceConversionSeparation = binaryNode.getWidestOperationType().narrowerThan(resultBounds.widest);
}
- loadBinaryOperands(binaryNode.lhs(), binaryNode.rhs(), operandBounds, false);
+ loadBinaryOperands(binaryNode.lhs(), binaryNode.rhs(), operandBounds, false, forceConversionSeparation);
}
@Override
@@ -3795,12 +3681,21 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
@Override
protected void evaluate() {
final Expression lhs = assignNode.lhs();
- final Type widest = assignNode.isTokenType(TokenType.ASSIGN_ADD) ? Type.OBJECT : assignNode.getWidestOperationType();
+ final Expression rhs = assignNode.rhs();
+ final Type widestOperationType = assignNode.getWidestOperationType();
+ final Type widest = assignNode.isTokenType(TokenType.ASSIGN_ADD) ? Type.OBJECT : widestOperationType;
final TypeBounds bounds = new TypeBounds(assignNode.getType(), widest);
new OptimisticOperation(assignNode, bounds) {
@Override
void loadStack() {
- loadBinaryOperands(lhs, assignNode.rhs(), bounds, true);
+ final boolean forceConversionSeparation;
+ if (isValid(getProgramPoint()) || widestOperationType == Type.NUMBER) {
+ forceConversionSeparation = false;
+ } else {
+ final Type operandType = Type.widest(booleanToInt(objectToNumber(lhs.getType())), booleanToInt(objectToNumber(rhs.getType())));
+ forceConversionSeparation = operandType.narrowerThan(widestOperationType);
+ }
+ loadBinaryOperands(lhs, rhs, bounds, true, forceConversionSeparation);
}
@Override
void consumeStack() {
@@ -3823,7 +3718,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
@Override
protected void evaluate() {
- loadBinaryOperands(assignNode.lhs(), assignNode.rhs(), TypeBounds.UNBOUNDED.notWiderThan(assignNode.getWidestOperandType()), true);
+ loadBinaryOperands(assignNode.lhs(), assignNode.rhs(), TypeBounds.UNBOUNDED.notWiderThan(assignNode.getWidestOperandType()), true, false);
op();
}
}
@@ -3946,6 +3841,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
@Override
void loadStack() {
final TypeBounds operandBounds;
+ boolean forceConversionSeparation = false;
if(numericBounds.narrowest == Type.NUMBER) {
// Result should be double always. Propagate it into the operands so we don't have lots of I2D
// and L2D after operand evaluation.
@@ -3963,9 +3859,10 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
// Non-optimistic, non-FP subtraction or multiplication. Allow them to overflow.
operandBounds = new TypeBounds(Type.narrowest(node.getWidestOperandType(),
numericBounds.widest), Type.NUMBER);
+ forceConversionSeparation = node.getWidestOperationType().narrowerThan(numericBounds.widest);
}
}
- loadBinaryOperands(node.lhs(), node.rhs(), operandBounds, false);
+ loadBinaryOperands(node.lhs(), node.rhs(), operandBounds, false, forceConversionSeparation);
}
@Override
@@ -4379,11 +4276,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
private void newFunctionObject(final FunctionNode functionNode, final boolean addInitializer) {
assert lc.peek() == functionNode;
- final int fnId = functionNode.getId();
-
- final RecompilableScriptFunctionData data = compiler.getScriptFunctionData(fnId);
-
- assert data != null : functionNode.getName() + " has no data";
+ final RecompilableScriptFunctionData data = compiler.getScriptFunctionData(functionNode.getId());
if (functionNode.isProgram() && !compiler.isOnDemandCompilation()) {
final CompileUnit fnUnit = functionNode.getCompileUnit();
diff --git a/src/jdk/nashorn/internal/codegen/CompilationPhase.java b/src/jdk/nashorn/internal/codegen/CompilationPhase.java
index 1ba77a7c..2a1d2661 100644
--- a/src/jdk/nashorn/internal/codegen/CompilationPhase.java
+++ b/src/jdk/nashorn/internal/codegen/CompilationPhase.java
@@ -38,12 +38,11 @@ import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SCOPE_DEPTHS
import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SPLIT;
import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SYMBOLS_ASSIGNED;
import static jdk.nashorn.internal.runtime.logging.DebugLogger.quote;
+
import java.io.PrintWriter;
-import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.LinkedHashMap;
-import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
@@ -53,10 +52,7 @@ import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LiteralNode;
-import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
-import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit;
import jdk.nashorn.internal.ir.Node;
-import jdk.nashorn.internal.ir.SplitNode;
import jdk.nashorn.internal.ir.debug.ASTWriter;
import jdk.nashorn.internal.ir.debug.PrintVisitor;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
@@ -81,7 +77,7 @@ enum CompilationPhase {
PARSED)) {
@Override
FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
- return (FunctionNode)fn.accept(new FoldConstants(compiler));
+ return transformFunction(fn, new FoldConstants(compiler));
}
@Override
@@ -104,7 +100,7 @@ enum CompilationPhase {
CONSTANT_FOLDED)) {
@Override
FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
- return (FunctionNode)fn.accept(new Lower(compiler));
+ return transformFunction(fn, new Lower(compiler));
}
@Override
@@ -118,23 +114,6 @@ enum CompilationPhase {
* optimistic ops a program point so that an UnwarrantedException knows from where
* a guess went wrong when creating the continuation to roll back this execution
*/
- PROGRAM_POINT_PHASE(
- EnumSet.of(
- INITIALIZED,
- PARSED,
- CONSTANT_FOLDED,
- LOWERED)) {
- @Override
- FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
- return (FunctionNode)fn.accept(new ProgramPoints());
- }
-
- @Override
- public String toString() {
- return "'Program Point Calculation'";
- }
- },
-
TRANSFORM_BUILTINS_PHASE(
EnumSet.of(
INITIALIZED,
@@ -144,13 +123,7 @@ enum CompilationPhase {
//we only do this if we have a param type map, otherwise this is not a specialized recompile
@Override
FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
- final FunctionNode newFunctionNode = (FunctionNode)fn.accept(new ApplySpecialization(compiler));
- return (FunctionNode)newFunctionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
- @Override
- public Node leaveFunctionNode(final FunctionNode node) {
- return node.setState(lc, BUILTINS_TRANSFORMED);
- }
- });
+ return setStates(transformFunction(fn, new ApplySpecialization(compiler)), BUILTINS_TRANSFORMED);
}
@Override
@@ -177,7 +150,7 @@ enum CompilationPhase {
FunctionNode newFunctionNode;
//ensure elementTypes, postsets and presets exist for splitter and arraynodes
- newFunctionNode = (FunctionNode)fn.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
+ newFunctionNode = transformFunction(fn, new NodeVisitor<LexicalContext>(new LexicalContext()) {
@Override
public LiteralNode<?> leaveLiteralNode(final LiteralNode<?> literalNode) {
return literalNode.initialize(lc);
@@ -185,7 +158,7 @@ enum CompilationPhase {
});
newFunctionNode = new Splitter(compiler, newFunctionNode, outermostCompileUnit).split(newFunctionNode, true);
-
+ newFunctionNode = transformFunction(newFunctionNode, new SplitIntoFunctions(compiler));
assert newFunctionNode.getCompileUnit() == outermostCompileUnit : "fn=" + fn.getName() + ", fn.compileUnit (" + newFunctionNode.getCompileUnit() + ") != " + outermostCompileUnit;
assert newFunctionNode.isStrict() == compiler.isStrict() : "functionNode.isStrict() != compiler.isStrict() for " + quote(newFunctionNode.getName());
@@ -198,6 +171,52 @@ enum CompilationPhase {
}
},
+ PROGRAM_POINT_PHASE(
+ EnumSet.of(
+ INITIALIZED,
+ PARSED,
+ CONSTANT_FOLDED,
+ LOWERED,
+ BUILTINS_TRANSFORMED,
+ SPLIT)) {
+ @Override
+ FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
+ return transformFunction(fn, new ProgramPoints());
+ }
+
+ @Override
+ public String toString() {
+ return "'Program Point Calculation'";
+ }
+ },
+
+ SERIALIZE_SPLIT_PHASE(
+ EnumSet.of(
+ INITIALIZED,
+ PARSED,
+ CONSTANT_FOLDED,
+ LOWERED,
+ BUILTINS_TRANSFORMED,
+ SPLIT)) {
+ @Override
+ FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
+ return transformFunction(fn, new NodeVisitor<LexicalContext>(new LexicalContext()) {
+ @Override
+ public boolean enterFunctionNode(final FunctionNode functionNode) {
+ if (functionNode.isSplit()) {
+ compiler.serializeAst(functionNode);
+ }
+ return true;
+ }
+ });
+ }
+
+ @Override
+ public String toString() {
+ return "'Serialize Split Functions'";
+ }
+ },
+
SYMBOL_ASSIGNMENT_PHASE(
EnumSet.of(
INITIALIZED,
@@ -208,7 +227,7 @@ enum CompilationPhase {
SPLIT)) {
@Override
FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
- return (FunctionNode)fn.accept(new AssignSymbols(compiler));
+ return transformFunction(fn, new AssignSymbols(compiler));
}
@Override
@@ -228,7 +247,7 @@ enum CompilationPhase {
SYMBOLS_ASSIGNED)) {
@Override
FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
- return (FunctionNode)fn.accept(new FindScopeDepths(compiler));
+ return transformFunction(fn, new FindScopeDepths(compiler));
}
@Override
@@ -250,7 +269,7 @@ enum CompilationPhase {
@Override
FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
if (compiler.useOptimisticTypes()) {
- return (FunctionNode)fn.accept(new OptimisticTypesCalculator(compiler));
+ return transformFunction(fn, new OptimisticTypesCalculator(compiler));
}
return setStates(fn, OPTIMISTIC_TYPES_ASSIGNED);
}
@@ -274,8 +293,7 @@ enum CompilationPhase {
OPTIMISTIC_TYPES_ASSIGNED)) {
@Override
FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
- final FunctionNode newFunctionNode = (FunctionNode)fn.accept(new LocalVariableTypesCalculator(compiler));
-
+ final FunctionNode newFunctionNode = transformFunction(fn, new LocalVariableTypesCalculator(compiler));
final ScriptEnvironment senv = compiler.getScriptEnvironment();
final PrintWriter err = senv.getErr();
@@ -330,13 +348,7 @@ enum CompilationPhase {
for (final CompileUnit oldUnit : compiler.getCompileUnits()) {
assert map.get(oldUnit) == null;
- final StringBuilder sb = new StringBuilder(compiler.nextCompileUnitName());
- if (phases.isRestOfCompilation()) {
- sb.append("$restOf");
- }
- //it's ok to not copy the initCount, methodCount and clinitCount here, as codegen is what
- //fills those out anyway. Thus no need for a copy constructor
- final CompileUnit newUnit = compiler.createCompileUnit(sb.toString(), oldUnit.getWeight());
+ final CompileUnit newUnit = createNewCompileUnit(compiler, phases);
log.fine("Creating new compile unit ", oldUnit, " => ", newUnit);
map.put(oldUnit, newUnit);
assert newUnit != null;
@@ -350,65 +362,80 @@ enum CompilationPhase {
//replace old compile units in function nodes, if any are assigned,
//for example by running the splitter on this function node in a previous
//partial code generation
- final FunctionNode newFunctionNode = (FunctionNode)fn.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
+ final FunctionNode newFunctionNode = transformFunction(fn, new ReplaceCompileUnits() {
@Override
- public Node leaveFunctionNode(final FunctionNode node) {
- final CompileUnit oldUnit = node.getCompileUnit();
- assert oldUnit != null : "no compile unit in function node";
-
- final CompileUnit newUnit = map.get(oldUnit);
- assert newUnit != null : "old unit has no mapping to new unit " + oldUnit;
-
- log.fine("Replacing compile unit: ", oldUnit, " => ", newUnit, " in ", quote(node.getName()));
- return node.setCompileUnit(lc, newUnit).setState(lc, CompilationState.COMPILE_UNITS_REUSED);
+ CompileUnit getReplacement(CompileUnit original) {
+ return map.get(original);
}
@Override
- public Node leaveSplitNode(final SplitNode node) {
- final CompileUnit oldUnit = node.getCompileUnit();
- assert oldUnit != null : "no compile unit in function node";
+ public Node leaveDefault(final Node node) {
+ return node.ensureUniqueLabels(lc);
+ }
+ });
- final CompileUnit newUnit = map.get(oldUnit);
- assert newUnit != null : "old unit has no mapping to new unit " + oldUnit;
+ return newFunctionNode;
+ }
- log.fine("Replacing compile unit: ", oldUnit, " => ", newUnit, " in ", quote(node.getName()));
- return node.setCompileUnit(lc, newUnit);
- }
+ @Override
+ public String toString() {
+ return "'Reuse Compile Units'";
+ }
+ },
+
+ REINITIALIZE_SERIALIZED(
+ EnumSet.of(
+ INITIALIZED,
+ PARSED,
+ CONSTANT_FOLDED,
+ LOWERED,
+ BUILTINS_TRANSFORMED,
+ SPLIT)) {
+ @Override
+ FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) {
+ final Set<CompileUnit> unitSet = CompileUnit.createCompileUnitSet();
+ final Map<CompileUnit, CompileUnit> unitMap = new HashMap<>();
+
+ // Ensure that the FunctionNode's compile unit is the first in the list of new units. Install phase
+ // will use that as the root class.
+ createCompileUnit(fn.getCompileUnit(), unitSet, unitMap, compiler, phases);
+ final FunctionNode newFn = transformFunction(fn, new ReplaceCompileUnits() {
@Override
- public Node leaveLiteralNode(final LiteralNode<?> node) {
- if (node instanceof ArrayLiteralNode) {
- final ArrayLiteralNode aln = (ArrayLiteralNode)node;
- if (aln.getUnits() == null) {
- return node;
- }
- final List<ArrayUnit> newArrayUnits = new ArrayList<>();
- for (final ArrayUnit au : aln.getUnits()) {
- final CompileUnit newUnit = map.get(au.getCompileUnit());
- assert newUnit != null;
- newArrayUnits.add(new ArrayUnit(newUnit, au.getLo(), au.getHi()));
- }
- return aln.setUnits(lc, newArrayUnits);
+ CompileUnit getReplacement(final CompileUnit oldUnit) {
+ final CompileUnit existing = unitMap.get(oldUnit);
+ if (existing != null) {
+ return existing;
}
- return node;
+ return createCompileUnit(oldUnit, unitSet, unitMap, compiler, phases);
}
@Override
- public Node leaveDefault(final Node node) {
- return node.ensureUniqueLabels(lc);
- }
+ public Node leaveFunctionNode(final FunctionNode fn2) {
+ return super.leaveFunctionNode(
+ // restore flags for deserialized nested function nodes
+ compiler.getScriptFunctionData(fn2.getId()).restoreFlags(lc, fn2));
+ };
});
+ compiler.replaceCompileUnits(unitSet);
+ return newFn;
+ }
- return newFunctionNode;
+ private CompileUnit createCompileUnit(final CompileUnit oldUnit, final Set<CompileUnit> unitSet,
+ final Map<CompileUnit, CompileUnit> unitMap, final Compiler compiler, final CompilationPhases phases) {
+ final CompileUnit newUnit = createNewCompileUnit(compiler, phases);
+ unitMap.put(oldUnit, newUnit);
+ unitSet.add(newUnit);
+ return newUnit;
}
@Override
public String toString() {
- return "'Reuse Compile Units'";
+ return "'Deserialize'";
}
},
- /**
+ /**
* Bytecode generation:
*
* Generate the byte code class(es) resulting from the compiled FunctionNode
@@ -443,7 +470,7 @@ enum CompilationPhase {
try {
// Explicitly set BYTECODE_GENERATED here; it can not be set in case of skipping codegen for :program
// in the lazy + optimistic world. See CodeGenerator.skipFunction().
- newFunctionNode = ((FunctionNode)newFunctionNode.accept(codegen)).setState(null, BYTECODE_GENERATED);
+ newFunctionNode = transformFunction(newFunctionNode, codegen).setState(null, BYTECODE_GENERATED);
codegen.generateScopeCalls();
} catch (final VerifyError e) {
if (senv._verify_code || senv._print_code) {
@@ -615,7 +642,7 @@ enum CompilationPhase {
if (!AssertsEnabled.assertsEnabled()) {
return functionNode;
}
- return (FunctionNode)functionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
+ return transformFunction(functionNode, new NodeVisitor<LexicalContext>(new LexicalContext()) {
@Override
public Node leaveFunctionNode(final FunctionNode fn) {
return fn.setState(lc, state);
@@ -701,4 +728,17 @@ enum CompilationPhase {
return end(compiler, transform(compiler, phases, begin(compiler, functionNode)));
}
+ private static FunctionNode transformFunction(final FunctionNode fn, final NodeVisitor<?> visitor) {
+ return (FunctionNode) fn.accept(visitor);
+ }
+
+ private static CompileUnit createNewCompileUnit(final Compiler compiler, final CompilationPhases phases) {
+ final StringBuilder sb = new StringBuilder(compiler.nextCompileUnitName());
+ if (phases.isRestOfCompilation()) {
+ sb.append("$restOf");
+ }
+ //it's ok to not copy the initCount, methodCount and clinitCount here, as codegen is what
+ //fills those out anyway. Thus no need for a copy constructor
+ return compiler.createCompileUnit(sb.toString(), 0);
+ }
}
diff --git a/src/jdk/nashorn/internal/codegen/CompileUnit.java b/src/jdk/nashorn/internal/codegen/CompileUnit.java
index 2d3cd2be..13e9d716 100644
--- a/src/jdk/nashorn/internal/codegen/CompileUnit.java
+++ b/src/jdk/nashorn/internal/codegen/CompileUnit.java
@@ -25,24 +25,31 @@
package jdk.nashorn.internal.codegen;
+import java.io.Serializable;
import java.util.Set;
import java.util.TreeSet;
+import jdk.nashorn.internal.ir.CompileUnitHolder;
/**
- * Used to track split class compilation.
- */
-public final class CompileUnit implements Comparable<CompileUnit> {
+ * Used to track split class compilation. Note that instances of the class are serializable, but all fields are
+ * transient, making the serialized version of the class only useful for tracking the referential topology of other
+ * AST nodes referencing the same or different compile units. We do want to preserve this topology though as
+ * {@link CompileUnitHolder}s in a deserialized AST will undergo reinitialization.
+ */
+public final class CompileUnit implements Comparable<CompileUnit>, Serializable {
+ private static final long serialVersionUID = 1L;
+
/** Current class name */
- private final String className;
+ private transient final String className;
/** Current class generator */
- private ClassEmitter classEmitter;
+ private transient ClassEmitter classEmitter;
- private long weight;
+ private transient long weight;
- private Class<?> clazz;
+ private transient Class<?> clazz;
- private boolean isUsed;
+ private transient boolean isUsed;
private static int emittedUnitCount;
@@ -122,14 +129,6 @@ public final class CompileUnit implements Comparable<CompileUnit> {
}
/**
- * Get the current weight of the compile unit.
- * @return the unit's weight
- */
- long getWeight() {
- return weight;
- }
-
- /**
* Check if this compile unit can hold {@code weight} more units of weight
* @param w weight to check if can be added
* @return true if weight fits in this compile unit
@@ -155,7 +154,7 @@ public final class CompileUnit implements Comparable<CompileUnit> {
}
private static String shortName(final String name) {
- return name.lastIndexOf('/') == -1 ? name : name.substring(name.lastIndexOf('/') + 1);
+ return name == null ? null : name.lastIndexOf('/') == -1 ? name : name.substring(name.lastIndexOf('/') + 1);
}
@Override
diff --git a/src/jdk/nashorn/internal/codegen/Compiler.java b/src/jdk/nashorn/internal/codegen/Compiler.java
index 3a0cc6bf..196862f0 100644
--- a/src/jdk/nashorn/internal/codegen/Compiler.java
+++ b/src/jdk/nashorn/internal/codegen/Compiler.java
@@ -32,15 +32,16 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
import static jdk.nashorn.internal.runtime.logging.DebugLogger.quote;
+
import java.io.File;
import java.lang.invoke.MethodType;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -159,119 +160,147 @@ public final class Compiler implements Loggable {
*/
private static final int COMPILE_UNIT_NAME_BUFFER_SIZE = 32;
+ private final Map<Integer, byte[]> serializedAsts = new HashMap<>();
+
/**
* Compilation phases that a compilation goes through
*/
public static class CompilationPhases implements Iterable<CompilationPhase> {
- /** Singleton that describes a standard eager compilation - this includes code installation */
- public final static CompilationPhases COMPILE_ALL = new CompilationPhases(
- "Compile all",
- new CompilationPhase[] {
- CompilationPhase.CONSTANT_FOLDING_PHASE,
- CompilationPhase.LOWERING_PHASE,
- CompilationPhase.PROGRAM_POINT_PHASE,
- CompilationPhase.TRANSFORM_BUILTINS_PHASE,
- CompilationPhase.SPLITTING_PHASE,
- CompilationPhase.SYMBOL_ASSIGNMENT_PHASE,
- CompilationPhase.SCOPE_DEPTH_COMPUTATION_PHASE,
- CompilationPhase.OPTIMISTIC_TYPE_ASSIGNMENT_PHASE,
- CompilationPhase.LOCAL_VARIABLE_TYPE_CALCULATION_PHASE,
- CompilationPhase.BYTECODE_GENERATION_PHASE,
- CompilationPhase.INSTALL_PHASE
- });
-
- /** Compile all for a rest of method */
- public final static CompilationPhases COMPILE_ALL_RESTOF =
- COMPILE_ALL.setDescription("Compile all, rest of").addAfter(CompilationPhase.LOCAL_VARIABLE_TYPE_CALCULATION_PHASE, CompilationPhase.REUSE_COMPILE_UNITS_PHASE);
-
- /** Singleton that describes a standard eager compilation, but no installation, for example used by --compile-only */
- public final static CompilationPhases COMPILE_ALL_NO_INSTALL =
- COMPILE_ALL.
- removeLast().
- setDescription("Compile without install");
+ /**
+ * Singleton that describes compilation up to the phase where a function can be serialized.
+ */
+ private final static CompilationPhases COMPILE_UPTO_SERIALIZABLE = new CompilationPhases(
+ "Common initial phases",
+ CompilationPhase.CONSTANT_FOLDING_PHASE,
+ CompilationPhase.LOWERING_PHASE,
+ CompilationPhase.TRANSFORM_BUILTINS_PHASE,
+ CompilationPhase.SPLITTING_PHASE,
+ CompilationPhase.PROGRAM_POINT_PHASE,
+ CompilationPhase.SERIALIZE_SPLIT_PHASE
+ );
+
+ private final static CompilationPhases COMPILE_SERIALIZABLE_UPTO_BYTECODE = new CompilationPhases(
+ "After common phases, before bytecode generator",
+ CompilationPhase.SYMBOL_ASSIGNMENT_PHASE,
+ CompilationPhase.SCOPE_DEPTH_COMPUTATION_PHASE,
+ CompilationPhase.OPTIMISTIC_TYPE_ASSIGNMENT_PHASE,
+ CompilationPhase.LOCAL_VARIABLE_TYPE_CALCULATION_PHASE
+ );
- /** Singleton that describes compilation up to the CodeGenerator, but not actually generating code */
- public final static CompilationPhases COMPILE_UPTO_BYTECODE =
- COMPILE_ALL.
- removeLast().
- removeLast().
- setDescription("Compile upto bytecode");
+ /**
+ * Singleton that describes additional steps to be taken after deserializing, all the way up to (but not
+ * including) generating and installing code.
+ */
+ public final static CompilationPhases RECOMPILE_SERIALIZED_UPTO_BYTECODE = new CompilationPhases(
+ "Recompile serialized function up to bytecode",
+ CompilationPhase.REINITIALIZE_SERIALIZED,
+ COMPILE_SERIALIZABLE_UPTO_BYTECODE
+ );
/**
* Singleton that describes back end of method generation, given that we have generated the normal
* method up to CodeGenerator as in {@link CompilationPhases#COMPILE_UPTO_BYTECODE}
*/
- public final static CompilationPhases COMPILE_FROM_BYTECODE = new CompilationPhases(
+ public final static CompilationPhases GENERATE_BYTECODE_AND_INSTALL = new CompilationPhases(
"Generate bytecode and install",
- new CompilationPhase[] {
- CompilationPhase.BYTECODE_GENERATION_PHASE,
- CompilationPhase.INSTALL_PHASE
- });
+ CompilationPhase.BYTECODE_GENERATION_PHASE,
+ CompilationPhase.INSTALL_PHASE
+ );
+
+ /** Singleton that describes compilation up to the CodeGenerator, but not actually generating code */
+ public final static CompilationPhases COMPILE_UPTO_BYTECODE = new CompilationPhases(
+ "Compile upto bytecode",
+ COMPILE_UPTO_SERIALIZABLE,
+ COMPILE_SERIALIZABLE_UPTO_BYTECODE);
+
+ /** Singleton that describes a standard eager compilation, but no installation, for example used by --compile-only */
+ public final static CompilationPhases COMPILE_ALL_NO_INSTALL = new CompilationPhases(
+ "Compile without install",
+ COMPILE_UPTO_BYTECODE,
+ CompilationPhase.BYTECODE_GENERATION_PHASE);
+
+ /** Singleton that describes a standard eager compilation - this includes code installation */
+ public final static CompilationPhases COMPILE_ALL = new CompilationPhases(
+ "Full eager compilation",
+ COMPILE_UPTO_BYTECODE,
+ GENERATE_BYTECODE_AND_INSTALL);
+
+ /** Singleton that describes a full compilation - this includes code installation - from serialized state*/
+ public final static CompilationPhases COMPILE_ALL_SERIALIZED = new CompilationPhases(
+ "Eager compilation from serializaed state",
+ RECOMPILE_SERIALIZED_UPTO_BYTECODE,
+ GENERATE_BYTECODE_AND_INSTALL);
/**
* Singleton that describes restOf method generation, given that we have generated the normal
* method up to CodeGenerator as in {@link CompilationPhases#COMPILE_UPTO_BYTECODE}
*/
- public final static CompilationPhases COMPILE_FROM_BYTECODE_RESTOF =
- COMPILE_FROM_BYTECODE.
- addFirst(CompilationPhase.REUSE_COMPILE_UNITS_PHASE).
- setDescription("Generate bytecode and install - RestOf method");
+ public final static CompilationPhases GENERATE_BYTECODE_AND_INSTALL_RESTOF = new CompilationPhases(
+ "Generate bytecode and install - RestOf method",
+ CompilationPhase.REUSE_COMPILE_UNITS_PHASE,
+ GENERATE_BYTECODE_AND_INSTALL);
+
+ /** Compile all for a rest of method */
+ public final static CompilationPhases COMPILE_ALL_RESTOF = new CompilationPhases(
+ "Compile all, rest of",
+ COMPILE_UPTO_BYTECODE,
+ GENERATE_BYTECODE_AND_INSTALL_RESTOF);
+
+ /** Compile from serialized for a rest of method */
+ public final static CompilationPhases COMPILE_SERIALIZED_RESTOF = new CompilationPhases(
+ "Compile serialized, rest of",
+ RECOMPILE_SERIALIZED_UPTO_BYTECODE,
+ GENERATE_BYTECODE_AND_INSTALL_RESTOF);
private final List<CompilationPhase> phases;
private final String desc;
private CompilationPhases(final String desc, final CompilationPhase... phases) {
- this.desc = desc;
+ this(desc, Arrays.asList(phases));
+ }
- final List<CompilationPhase> newPhases = new LinkedList<>();
- newPhases.addAll(Arrays.asList(phases));
- this.phases = Collections.unmodifiableList(newPhases);
+ private CompilationPhases(final String desc, final CompilationPhases base, final CompilationPhase... phases) {
+ this(desc, concat(base.phases, Arrays.asList(phases)));
}
- @Override
- public String toString() {
- return "'" + desc + "' " + phases.toString();
+ private CompilationPhases(final String desc, final CompilationPhase first, final CompilationPhases rest) {
+ this(desc, concat(Collections.singletonList(first), rest.phases));
}
- private CompilationPhases setDescription(final String desc) {
- return new CompilationPhases(desc, phases.toArray(new CompilationPhase[phases.size()]));
+ private CompilationPhases(final String desc, final CompilationPhases base) {
+ this(desc, base.phases);
}
- private CompilationPhases removeLast() {
- final LinkedList<CompilationPhase> list = new LinkedList<>(phases);
- list.removeLast();
- return new CompilationPhases(desc, list.toArray(new CompilationPhase[list.size()]));
+ private CompilationPhases(final String desc, final CompilationPhases... bases) {
+ this(desc, concatPhases(bases));
}
- private CompilationPhases addFirst(final CompilationPhase phase) {
- if (phases.contains(phase)) {
- return this;
- }
- final LinkedList<CompilationPhase> list = new LinkedList<>(phases);
- list.addFirst(phase);
- return new CompilationPhases(desc, list.toArray(new CompilationPhase[list.size()]));
+ private CompilationPhases(final String desc, final List<CompilationPhase> phases) {
+ this.desc = desc;
+ this.phases = phases;
}
- @SuppressWarnings("unused") //TODO I'll use this soon
- private CompilationPhases replace(final CompilationPhase phase, final CompilationPhase newPhase) {
- final LinkedList<CompilationPhase> list = new LinkedList<>();
- for (final CompilationPhase p : phases) {
- list.add(p == phase ? newPhase : p);
+ private static List<CompilationPhase> concatPhases(final CompilationPhases[] bases) {
+ final ArrayList<CompilationPhase> l = new ArrayList<>();
+ for(final CompilationPhases base: bases) {
+ l.addAll(base.phases);
}
- return new CompilationPhases(desc, list.toArray(new CompilationPhase[list.size()]));
+ l.trimToSize();
+ return l;
}
- private CompilationPhases addAfter(final CompilationPhase phase, final CompilationPhase newPhase) {
- final LinkedList<CompilationPhase> list = new LinkedList<>();
- for (final CompilationPhase p : phases) {
- list.add(p);
- if (p == phase) {
- list.add(newPhase);
- }
- }
- return new CompilationPhases(desc, list.toArray(new CompilationPhase[list.size()]));
+ private static <T> List<T> concat(final List<T> l1, final List<T> l2) {
+ final ArrayList<T> l = new ArrayList<>(l1);
+ l.addAll(l2);
+ l.trimToSize();
+ return l;
+ }
+
+ @Override
+ public String toString() {
+ return "'" + desc + "' " + phases.toString();
}
boolean contains(final CompilationPhase phase) {
@@ -284,7 +313,7 @@ public final class Compiler implements Loggable {
}
boolean isRestOfCompilation() {
- return this == COMPILE_ALL_RESTOF || this == COMPILE_FROM_BYTECODE_RESTOF;
+ return this == COMPILE_ALL_RESTOF || this == GENERATE_BYTECODE_AND_INSTALL_RESTOF || this == COMPILE_SERIALIZED_RESTOF;
}
String getDesc() {
@@ -410,10 +439,29 @@ public final class Compiler implements Loggable {
baseName = baseName + installer.getUniqueScriptId();
}
- final String mangled = NameCodec.encode(baseName);
+ // ASM's bytecode verifier does not allow JVM allowed safe escapes using '\' as escape char.
+ // While ASM accepts such escapes for method names, field names, it enforces Java identifier
+ // for class names. Workaround that ASM bug here by replacing JVM 'dangerous' chars with '_'
+ // rather than safe encoding using '\'.
+ final String mangled = env._verify_code? replaceDangerChars(baseName) : NameCodec.encode(baseName);
return mangled != null ? mangled : baseName;
}
+ private static final String DANGEROUS_CHARS = "\\/.;:$[]<>";
+ private static String replaceDangerChars(final String name) {
+ final int len = name.length();
+ final StringBuilder buf = new StringBuilder();
+ for (int i = 0; i < len; i++) {
+ final char ch = name.charAt(i);
+ if (DANGEROUS_CHARS.indexOf(ch) != -1) {
+ buf.append('_');
+ } else {
+ buf.append(ch);
+ }
+ }
+ return buf.toString();
+ }
+
private String firstCompileUnitName() {
final StringBuilder sb = new StringBuilder(SCRIPTS_PACKAGE).
append('/').
@@ -730,6 +778,14 @@ public final class Compiler implements Loggable {
compileUnits.addAll(newUnits);
}
+ void serializeAst(final FunctionNode fn) {
+ serializedAsts.put(fn.getId(), AstSerializer.serialize(fn));
+ }
+
+ byte[] removeSerializedAst(final int fnId) {
+ return serializedAsts.remove(fnId);
+ }
+
CompileUnit findUnit(final long weight) {
for (final CompileUnit unit : compileUnits) {
if (unit.canHold(weight)) {
@@ -752,7 +808,10 @@ public final class Compiler implements Loggable {
}
RecompilableScriptFunctionData getScriptFunctionData(final int functionId) {
- return compiledFunction == null ? null : compiledFunction.getScriptFunctionData(functionId);
+ assert compiledFunction != null;
+ final RecompilableScriptFunctionData fn = compiledFunction.getScriptFunctionData(functionId);
+ assert fn != null : functionId;
+ return fn;
}
boolean isGlobalSymbol(final FunctionNode fn, final String name) {
diff --git a/src/jdk/nashorn/internal/codegen/FindScopeDepths.java b/src/jdk/nashorn/internal/codegen/FindScopeDepths.java
index 4c1db02b..431244dc 100644
--- a/src/jdk/nashorn/internal/codegen/FindScopeDepths.java
+++ b/src/jdk/nashorn/internal/codegen/FindScopeDepths.java
@@ -187,7 +187,6 @@ final class FindScopeDepths extends NodeVisitor<LexicalContext> implements Logga
if (compiler.isOnDemandCompilation()) {
final RecompilableScriptFunctionData data = compiler.getScriptFunctionData(newFunctionNode.getId());
- assert data != null : newFunctionNode.getName() + " lacks data";
if (data.inDynamicContext()) {
log.fine("Reviving scriptfunction ", quote(name), " as defined in previous (now lost) dynamic scope.");
newFunctionNode = newFunctionNode.setInDynamicContext(lc);
@@ -202,7 +201,7 @@ final class FindScopeDepths extends NodeVisitor<LexicalContext> implements Logga
//create recompilable scriptfunctiondata
final int fnId = newFunctionNode.getId();
- final Map<Integer, RecompilableScriptFunctionData> nestedFunctions = fnIdToNestedFunctions.get(fnId);
+ final Map<Integer, RecompilableScriptFunctionData> nestedFunctions = fnIdToNestedFunctions.remove(fnId);
assert nestedFunctions != null;
// Generate the object class and property map in case this function is ever used as constructor
@@ -212,8 +211,8 @@ final class FindScopeDepths extends NodeVisitor<LexicalContext> implements Logga
new AllocatorDescriptor(newFunctionNode.getThisProperties()),
nestedFunctions,
externalSymbolDepths.get(fnId),
- internalSymbols.get(fnId)
- );
+ internalSymbols.get(fnId),
+ compiler.removeSerializedAst(fnId));
if (lc.getOutermostFunction() != newFunctionNode) {
final FunctionNode parentFn = lc.getParentFunction(newFunctionNode);
diff --git a/src/jdk/nashorn/internal/codegen/Label.java b/src/jdk/nashorn/internal/codegen/Label.java
index 7c86abf0..e4b02498 100644
--- a/src/jdk/nashorn/internal/codegen/Label.java
+++ b/src/jdk/nashorn/internal/codegen/Label.java
@@ -24,6 +24,7 @@
*/
package jdk.nashorn.internal.codegen;
+import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
@@ -39,7 +40,9 @@ import jdk.nashorn.internal.codegen.types.Type;
*
* see -Dnashorn.codegen.debug, --log=codegen
*/
-public final class Label {
+public final class Label implements Serializable {
+ private static final long serialVersionUID = 1L;
+
//byte code generation evaluation type stack for consistency check
//and correct opcode selection. one per label as a label may be a
//join point
@@ -491,7 +494,7 @@ public final class Label {
private final String name;
/** Type stack at this label */
- private Label.Stack stack;
+ private transient Label.Stack stack;
/** ASM representation of this label */
private jdk.internal.org.objectweb.asm.Label label;
@@ -500,9 +503,9 @@ public final class Label {
private final int id;
/** Is this label reachable (anything ever jumped to it)? */
- private boolean reachable;
+ private transient boolean reachable;
- private boolean breakTarget;
+ private transient boolean breakTarget;
/**
* Constructor
diff --git a/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java b/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java
index 491ef2ca..ea073623 100644
--- a/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java
+++ b/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java
@@ -72,7 +72,7 @@ import jdk.nashorn.internal.ir.PropertyNode;
import jdk.nashorn.internal.ir.ReturnNode;
import jdk.nashorn.internal.ir.RuntimeNode;
import jdk.nashorn.internal.ir.RuntimeNode.Request;
-import jdk.nashorn.internal.ir.SplitNode;
+import jdk.nashorn.internal.ir.SplitReturn;
import jdk.nashorn.internal.ir.Statement;
import jdk.nashorn.internal.ir.SwitchNode;
import jdk.nashorn.internal.ir.Symbol;
@@ -361,10 +361,6 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
// Synthetic return node that we must insert at the end of the function if it's end is reachable.
private ReturnNode syntheticReturn;
- // Topmost current split node (if any)
- private SplitNode topSplit;
- private boolean split;
-
private boolean alreadyEnteredTopLevelFunction;
// LvarType and conversion information gathered during the top-down pass; applied to nodes in the bottom-up pass.
@@ -477,22 +473,7 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
return false;
}
final BreakableNode target = jump.getTarget(lc);
- return splitAwareJumpToLabel(jump, target, jump.getTargetLabel(target));
- }
-
- private boolean splitAwareJumpToLabel(final JumpStatement jumpStatement, final BreakableNode target, final Label targetLabel) {
- final JoinPredecessor jumpOrigin;
- if(topSplit != null && lc.isExternalTarget(topSplit, target)) {
- // If the jump target is outside the topmost split node, then we'll create a synthetic jump origin in the
- // split node.
- jumpOrigin = new JoinPredecessorExpression();
- topSplit.addJump(jumpOrigin, targetLabel);
- } else {
- // Otherwise, the original jump statement is the jump origin
- jumpOrigin = jumpStatement;
- }
-
- jumpToLabel(jumpOrigin, targetLabel, getBreakTargetTypes(target));
+ jumpToLabel(jump, jump.getTargetLabel(target), getBreakTargetTypes(target));
doesNotContinueSequentially();
return false;
}
@@ -703,18 +684,9 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
}
@Override
- public boolean enterSplitNode(final SplitNode splitNode) {
- if(!reachable) {
- return false;
- }
- // Need to visit inside of split nodes. While it's true that they don't have local variables, we need to visit
- // breaks, continues, and returns in them.
- if(topSplit == null) {
- topSplit = splitNode;
- }
- split = true;
- setType(getCompilerConstantSymbol(lc.getCurrentFunction(), CompilerConstants.RETURN), LvarType.UNDEFINED);
- return true;
+ public boolean enterSplitReturn(final SplitReturn splitReturn) {
+ doesNotContinueSequentially();
+ return false;
}
@Override
@@ -1116,15 +1088,6 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
if(returnType.isUnknown()) {
returnType = Type.OBJECT;
}
-
- if(split) {
- // If the function is split, the ":return" symbol is used and needs a slot. Note we can't mark the return
- // symbol as used in enterSplitNode, as we don't know the final return type of the function earlier than
- // here.
- final Symbol retSymbol = getCompilerConstantSymbol(lc.getCurrentFunction(), CompilerConstants.RETURN);
- retSymbol.setHasSlotFor(returnType);
- retSymbol.setNeedsSlot(true);
- }
}
private void createSyntheticReturn(final Block body) {
diff --git a/src/jdk/nashorn/internal/codegen/Lower.java b/src/jdk/nashorn/internal/codegen/Lower.java
index dddf6976..724c6f1e 100644
--- a/src/jdk/nashorn/internal/codegen/Lower.java
+++ b/src/jdk/nashorn/internal/codegen/Lower.java
@@ -352,8 +352,6 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
private Node spliceFinally(final TryNode tryNode, final List<ThrowNode> rethrows, final Block finallyBody) {
assert tryNode.getFinallyBody() == null;
- final LexicalContext lowerLc = lc;
-
final TryNode newTryNode = (TryNode)tryNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
final List<Node> insideTry = new ArrayList<>();
@@ -406,7 +404,6 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
//still in the try block, store it in a result value and return it afterwards
resultNode = new IdentNode(Lower.this.compilerConstant(RETURN));
newStatements.add(new ExpressionStatement(returnNode.getLineNumber(), returnNode.getToken(), returnNode.getFinish(), new BinaryNode(Token.recast(returnNode.getToken(), TokenType.ASSIGN), resultNode, expr)));
- lowerLc.setFlag(lowerLc.getCurrentFunction(), FunctionNode.USES_RETURN_SYMBOL);
} else {
resultNode = null;
}
diff --git a/src/jdk/nashorn/internal/codegen/MethodEmitter.java b/src/jdk/nashorn/internal/codegen/MethodEmitter.java
index 400d5219..87bb297f 100644
--- a/src/jdk/nashorn/internal/codegen/MethodEmitter.java
+++ b/src/jdk/nashorn/internal/codegen/MethodEmitter.java
@@ -71,6 +71,7 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.PRIMITIVE_FIELD_TYPE;
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_OPTIMISTIC;
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_PROGRAM_POINT_SHIFT;
+
import java.io.PrintStream;
import java.lang.reflect.Array;
import java.util.Collection;
@@ -88,17 +89,16 @@ import jdk.nashorn.internal.codegen.types.ArrayType;
import jdk.nashorn.internal.codegen.types.BitwiseType;
import jdk.nashorn.internal.codegen.types.NumericType;
import jdk.nashorn.internal.codegen.types.Type;
-import jdk.nashorn.internal.ir.BreakableNode;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.JoinPredecessor;
-import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.LocalVariableConversion;
import jdk.nashorn.internal.ir.RuntimeNode;
import jdk.nashorn.internal.ir.Symbol;
import jdk.nashorn.internal.ir.TryNode;
import jdk.nashorn.internal.objects.Global;
+import jdk.nashorn.internal.objects.NativeArray;
import jdk.nashorn.internal.runtime.ArgumentSetter;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.Debug;
@@ -1663,19 +1663,6 @@ public class MethodEmitter implements Emitter {
}
/**
- * Goto, possibly when splitting is taking place. If
- * a splitNode exists, we need to handle the case that the
- * jump target is another method
- *
- * @param label destination label
- * @param targetNode the node to which the destination label belongs (the label is normally a break or continue
- * label)
- */
- void splitAwareGoto(final LexicalContext lc, final Label label, final BreakableNode targetNode) {
- _goto(label);
- }
-
- /**
* Perform a comparison of two number types that are popped from the stack
*
* @param isCmpG is this a dcmpg semantic, false if it's a dcmpl semantic
@@ -2126,7 +2113,14 @@ public class MethodEmitter implements Emitter {
int pos = 0;
for (int i = argCount - 1; i >= 0; i--) {
- paramTypes[i] = stack.peek(pos++);
+ Type pt = stack.peek(pos++);
+ // "erase" specific ScriptObject subtype info - except for NativeArray.
+ // NativeArray is used for array/List/Deque conversion for Java calls.
+ if (ScriptObject.class.isAssignableFrom(pt.getTypeClass()) &&
+ !NativeArray.class.isAssignableFrom(pt.getTypeClass())) {
+ pt = Type.SCRIPT_OBJECT;
+ }
+ paramTypes[i] = pt;
}
final String descriptor = Type.getMethodDescriptor(returnType, paramTypes);
for (int i = 0; i < argCount; i++) {
diff --git a/src/jdk/nashorn/internal/codegen/OptimisticTypesCalculator.java b/src/jdk/nashorn/internal/codegen/OptimisticTypesCalculator.java
index ac402e93..c8029070 100644
--- a/src/jdk/nashorn/internal/codegen/OptimisticTypesCalculator.java
+++ b/src/jdk/nashorn/internal/codegen/OptimisticTypesCalculator.java
@@ -30,7 +30,6 @@ import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
import java.util.ArrayDeque;
import java.util.BitSet;
import java.util.Deque;
-import jdk.nashorn.internal.IntDeque;
import jdk.nashorn.internal.ir.AccessNode;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.CallNode;
@@ -49,7 +48,6 @@ import jdk.nashorn.internal.ir.LoopNode;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.Optimistic;
import jdk.nashorn.internal.ir.PropertyNode;
-import jdk.nashorn.internal.ir.SplitNode;
import jdk.nashorn.internal.ir.Symbol;
import jdk.nashorn.internal.ir.TernaryNode;
import jdk.nashorn.internal.ir.UnaryNode;
@@ -70,8 +68,6 @@ final class OptimisticTypesCalculator extends NodeVisitor<LexicalContext> {
// Per-function bit set of program points that must never be optimistic.
final Deque<BitSet> neverOptimistic = new ArrayDeque<>();
- // Per-function depth of split nodes
- final IntDeque splitDepth = new IntDeque();
OptimisticTypesCalculator(final Compiler compiler) {
super(new LexicalContext());
@@ -155,7 +151,6 @@ final class OptimisticTypesCalculator extends NodeVisitor<LexicalContext> {
return false;
}
neverOptimistic.push(new BitSet());
- splitDepth.push(0);
return true;
}
@@ -190,19 +185,6 @@ final class OptimisticTypesCalculator extends NodeVisitor<LexicalContext> {
}
@Override
- public boolean enterSplitNode(final SplitNode splitNode) {
- splitDepth.getAndIncrement();
- return true;
- }
-
- @Override
- public Node leaveSplitNode(final SplitNode splitNode) {
- final int depth = splitDepth.decrementAndGet();
- assert depth >= 0;
- return splitNode;
- }
-
- @Override
public boolean enterVarNode(final VarNode varNode) {
tagNeverOptimistic(varNode.getName());
return true;
@@ -226,16 +208,11 @@ final class OptimisticTypesCalculator extends NodeVisitor<LexicalContext> {
@Override
public Node leaveFunctionNode(final FunctionNode functionNode) {
neverOptimistic.pop();
- final int lastSplitDepth = splitDepth.pop();
- assert lastSplitDepth == 0;
return functionNode.setState(lc, CompilationState.OPTIMISTIC_TYPES_ASSIGNED);
}
@Override
public Node leaveIdentNode(final IdentNode identNode) {
- if(inSplitNode()) {
- return identNode;
- }
final Symbol symbol = identNode.getSymbol();
if(symbol == null) {
assert identNode.isPropertyName();
@@ -256,7 +233,7 @@ final class OptimisticTypesCalculator extends NodeVisitor<LexicalContext> {
private Expression leaveOptimistic(final Optimistic opt) {
final int pp = opt.getProgramPoint();
- if(isValid(pp) && !inSplitNode() && !neverOptimistic.peek().get(pp)) {
+ if(isValid(pp) && !neverOptimistic.peek().get(pp)) {
return (Expression)opt.setType(compiler.getOptimisticType(opt));
}
return (Expression)opt;
@@ -277,8 +254,4 @@ final class OptimisticTypesCalculator extends NodeVisitor<LexicalContext> {
tagNeverOptimistic(test.getExpression());
}
}
-
- private boolean inSplitNode() {
- return splitDepth.peek() > 0;
- }
}
diff --git a/src/jdk/nashorn/internal/codegen/ProgramPoints.java b/src/jdk/nashorn/internal/codegen/ProgramPoints.java
index c04546b2..4606989e 100644
--- a/src/jdk/nashorn/internal/codegen/ProgramPoints.java
+++ b/src/jdk/nashorn/internal/codegen/ProgramPoints.java
@@ -85,7 +85,7 @@ class ProgramPoints extends NodeVisitor<LexicalContext> {
@Override
public boolean enterVarNode(final VarNode varNode) {
- noProgramPoint.add(varNode.getAssignmentDest());
+ noProgramPoint.add(varNode.getName());
return true;
}
diff --git a/src/jdk/nashorn/internal/codegen/ReplaceCompileUnits.java b/src/jdk/nashorn/internal/codegen/ReplaceCompileUnits.java
new file mode 100644
index 00000000..57f9e681
--- /dev/null
+++ b/src/jdk/nashorn/internal/codegen/ReplaceCompileUnits.java
@@ -0,0 +1,85 @@
+/*
+ * 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.codegen;
+
+import java.util.ArrayList;
+import java.util.List;
+import jdk.nashorn.internal.ir.CompileUnitHolder;
+import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
+import jdk.nashorn.internal.ir.LexicalContext;
+import jdk.nashorn.internal.ir.LiteralNode;
+import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
+import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit;
+import jdk.nashorn.internal.ir.Node;
+import jdk.nashorn.internal.ir.visitor.NodeVisitor;
+
+/**
+ * Base class for a node visitor that replaces {@link CompileUnit}s in {@link CompileUnitHolder}s.
+ */
+abstract class ReplaceCompileUnits extends NodeVisitor<LexicalContext> {
+ ReplaceCompileUnits() {
+ super(new LexicalContext());
+ }
+
+ /**
+ * Override to provide a replacement for an old compile unit.
+ * @param oldUnit the old compile unit to replace
+ * @return the compile unit's replacement.
+ */
+ abstract CompileUnit getReplacement(final CompileUnit oldUnit);
+
+ CompileUnit getExistingReplacement(final CompileUnitHolder node) {
+ final CompileUnit oldUnit = node.getCompileUnit();
+ assert oldUnit != null;
+
+ final CompileUnit newUnit = getReplacement(oldUnit);
+ assert newUnit != null;
+
+ return newUnit;
+ }
+
+ @Override
+ public Node leaveFunctionNode(final FunctionNode node) {
+ return node.setCompileUnit(lc, getExistingReplacement(node)).setState(lc, CompilationState.COMPILE_UNITS_REUSED);
+ }
+
+ @Override
+ public Node leaveLiteralNode(final LiteralNode<?> node) {
+ if (node instanceof ArrayLiteralNode) {
+ final ArrayLiteralNode aln = (ArrayLiteralNode)node;
+ if (aln.getUnits() == null) {
+ return node;
+ }
+ final List<ArrayUnit> newArrayUnits = new ArrayList<>();
+ for (final ArrayUnit au : aln.getUnits()) {
+ newArrayUnits.add(new ArrayUnit(getExistingReplacement(au), au.getLo(), au.getHi()));
+ }
+ return aln.setUnits(lc, newArrayUnits);
+ }
+ return node;
+ }
+}
diff --git a/src/jdk/nashorn/internal/codegen/SplitIntoFunctions.java b/src/jdk/nashorn/internal/codegen/SplitIntoFunctions.java
new file mode 100644
index 00000000..9933274d
--- /dev/null
+++ b/src/jdk/nashorn/internal/codegen/SplitIntoFunctions.java
@@ -0,0 +1,446 @@
+/*
+ * 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.codegen;
+
+import static jdk.nashorn.internal.ir.Node.NO_FINISH;
+import static jdk.nashorn.internal.ir.Node.NO_LINE_NUMBER;
+import static jdk.nashorn.internal.ir.Node.NO_TOKEN;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.List;
+import java.util.Objects;
+import jdk.nashorn.internal.ir.AccessNode;
+import jdk.nashorn.internal.ir.BinaryNode;
+import jdk.nashorn.internal.ir.Block;
+import jdk.nashorn.internal.ir.BlockLexicalContext;
+import jdk.nashorn.internal.ir.BreakNode;
+import jdk.nashorn.internal.ir.CallNode;
+import jdk.nashorn.internal.ir.CaseNode;
+import jdk.nashorn.internal.ir.ContinueNode;
+import jdk.nashorn.internal.ir.Expression;
+import jdk.nashorn.internal.ir.ExpressionStatement;
+import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.ir.GetSplitState;
+import jdk.nashorn.internal.ir.IdentNode;
+import jdk.nashorn.internal.ir.IfNode;
+import jdk.nashorn.internal.ir.JumpStatement;
+import jdk.nashorn.internal.ir.LiteralNode;
+import jdk.nashorn.internal.ir.Node;
+import jdk.nashorn.internal.ir.ReturnNode;
+import jdk.nashorn.internal.ir.SetSplitState;
+import jdk.nashorn.internal.ir.SplitNode;
+import jdk.nashorn.internal.ir.SplitReturn;
+import jdk.nashorn.internal.ir.Statement;
+import jdk.nashorn.internal.ir.SwitchNode;
+import jdk.nashorn.internal.ir.VarNode;
+import jdk.nashorn.internal.ir.visitor.NodeVisitor;
+import jdk.nashorn.internal.parser.Token;
+import jdk.nashorn.internal.parser.TokenType;
+
+/**
+ * A node visitor that replaces {@link SplitNode}s with anonymous function invocations and some additional constructs
+ * to support control flow across splits. By using this transformation, split functions are translated into ordinary
+ * JavaScript functions with nested anonymous functions. The transformations however introduce several AST nodes that
+ * have no JavaScript source representations ({@link GetSplitState}, {@link SetSplitState}, and {@link SplitReturn}),
+ * and therefore such function is no longer reparseable from its source. For that reason, split functions and their
+ * fragments are serialized in-memory and deserialized when they need to be recompiled either for deoptimization or
+ * for type specialization.
+ * NOTE: all {@code leave*()} methods for statements are returning their input nodes. That way, they will not mutate
+ * the original statement list in the block containing the statement, which is fine, as it'll be replaced by the
+ * lexical context when the block is left. If we returned something else (e.g. null), we'd cause a mutation in the
+ * enclosing block's statement list that is otherwise overwritten later anyway.
+ */
+final class SplitIntoFunctions extends NodeVisitor<BlockLexicalContext> {
+ private static final int FALLTHROUGH_STATE = -1;
+ private static final int RETURN_STATE = 0;
+ private static final int BREAK_STATE = 1;
+ private static final int FIRST_JUMP_STATE = 2;
+
+ private static final String THIS_NAME = CompilerConstants.THIS.symbolName();
+ private static final String RETURN_NAME = CompilerConstants.RETURN.symbolName();
+ // Used as the name of the formal parameter for passing the current value of :return symbol into a split fragment.
+ private static final String RETURN_PARAM_NAME = RETURN_NAME + "-in";
+
+ private final Deque<FunctionState> functionStates = new ArrayDeque<>();
+ private final Deque<SplitState> splitStates = new ArrayDeque<>();
+ private final Namespace namespace;
+
+ private boolean artificialBlock = false;
+
+ // -1 is program; we need to use negative ones
+ private int nextFunctionId = -2;
+
+ public SplitIntoFunctions(final Compiler compiler) {
+ super(new BlockLexicalContext() {
+ @Override
+ protected Block afterSetStatements(Block block) {
+ for(Statement stmt: block.getStatements()) {
+ assert !(stmt instanceof SplitNode);
+ }
+ return block;
+ }
+ });
+ namespace = new Namespace(compiler.getScriptEnvironment().getNamespace());
+ }
+
+ @Override
+ public boolean enterFunctionNode(final FunctionNode functionNode) {
+ functionStates.push(new FunctionState(functionNode));
+ return true;
+ }
+
+ @Override
+ public Node leaveFunctionNode(final FunctionNode functionNode) {
+ functionStates.pop();
+ return functionNode;
+ }
+
+ @Override
+ protected Node leaveDefault(final Node node) {
+ if (node instanceof Statement) {
+ appendStatement((Statement)node);
+ }
+ return node;
+ }
+
+ @Override
+ public boolean enterSplitNode(final SplitNode splitNode) {
+ getCurrentFunctionState().splitDepth++;
+ splitStates.push(new SplitState(splitNode));
+ return true;
+ }
+
+ @Override
+ public Node leaveSplitNode(final SplitNode splitNode) {
+ // Replace the split node with an anonymous function expression call.
+
+ final FunctionState fnState = getCurrentFunctionState();
+
+ final String name = splitNode.getName();
+ Block body = splitNode.getBody();
+ final int firstLineNumber = body.getFirstStatementLineNumber();
+ final long token = body.getToken();
+ final int finish = body.getFinish();
+
+ final FunctionNode originalFn = fnState.fn;
+ assert originalFn == lc.getCurrentFunction();
+ final boolean isProgram = originalFn.isProgram();
+
+ // Change SplitNode({...}) into "function () { ... }", or "function (:return-in) () { ... }" (for program)
+ final long newFnToken = Token.toDesc(TokenType.FUNCTION, nextFunctionId--, 0);
+ final FunctionNode fn = new FunctionNode(
+ originalFn.getSource(),
+ body.getFirstStatementLineNumber(),
+ newFnToken,
+ finish,
+ NO_TOKEN,
+ namespace,
+ createIdent(name),
+ originalFn.getName() + "$" + name,
+ isProgram ? Collections.singletonList(createReturnParamIdent()) : Collections.<IdentNode>emptyList(),
+ FunctionNode.Kind.NORMAL,
+ // We only need IS_SPLIT conservatively, in case it contains any array units so that we force
+ // the :callee's existence, to force :scope to never be in a slot lower than 2. This is actually
+ // quite a horrible hack to do with CodeGenerator.fixScopeSlot not trampling other parameters
+ // and should go away once we no longer have array unit handling in codegen. Note however that
+ // we still use IS_SPLIT as the criteria in CompilationPhase.SERIALIZE_SPLIT_PHASE.
+ FunctionNode.IS_ANONYMOUS | FunctionNode.USES_ANCESTOR_SCOPE | FunctionNode.IS_SPLIT
+ )
+ .setBody(lc, body)
+ .setCompileUnit(lc, splitNode.getCompileUnit())
+ .copyCompilationState(lc, originalFn);
+
+ // Call the function:
+ // either "(function () { ... }).call(this)"
+ // or "(function (:return-in) { ... }).call(this, :return)"
+ // NOTE: Function.call() has optimized linking that basically does a pass-through to the function being invoked.
+ // NOTE: CompilationPhase.PROGRAM_POINT_PHASE happens after this, so these calls are subject to optimistic
+ // assumptions on their return value (when they return a value), as they should be.
+ final IdentNode thisIdent = createIdent(THIS_NAME);
+ final CallNode callNode = new CallNode(firstLineNumber, token, finish, new AccessNode(NO_TOKEN, NO_FINISH, fn, "call"),
+ isProgram ? Arrays.<Expression>asList(thisIdent, createReturnIdent())
+ : Collections.<Expression>singletonList(thisIdent),
+ false);
+
+ final SplitState splitState = splitStates.pop();
+ fnState.splitDepth--;
+
+ final Expression callWithReturn;
+ final boolean hasReturn = splitState.hasReturn;
+ if (hasReturn && fnState.splitDepth > 0) {
+ final SplitState parentSplit = splitStates.peek();
+ if (parentSplit != null) {
+ // Propagate hasReturn to parent split
+ parentSplit.hasReturn = true;
+ }
+ }
+ if (hasReturn || isProgram) {
+ // capture return value: ":return = (function () { ... })();"
+ callWithReturn = new BinaryNode(Token.recast(token, TokenType.ASSIGN), createReturnIdent(), callNode);
+ } else {
+ // no return value, just call : "(function () { ... })();"
+ callWithReturn = callNode;
+ }
+ appendStatement(new ExpressionStatement(firstLineNumber, token, finish, callWithReturn));
+
+ Statement splitStateHandler;
+
+ final List<JumpStatement> jumpStatements = splitState.jumpStatements;
+ final int jumpCount = jumpStatements.size();
+ // There are jumps (breaks or continues) that need to be propagated outside the split node. We need to
+ // set up a switch statement for them:
+ // switch(:scope.getScopeState()) { ... }
+ if (jumpCount > 0) {
+ final List<CaseNode> cases = new ArrayList<>(jumpCount + (hasReturn ? 1 : 0));
+ if (hasReturn) {
+ // If the split node also contained a return, we'll slip it as a case in the switch statement
+ addCase(cases, RETURN_STATE, createReturnFromSplit());
+ }
+ int i = FIRST_JUMP_STATE;
+ for (final JumpStatement jump: jumpStatements) {
+ addCase(cases, i++, enblockAndVisit(jump));
+ }
+ splitStateHandler = new SwitchNode(NO_LINE_NUMBER, token, finish, GetSplitState.INSTANCE, cases, null);
+ } else {
+ splitStateHandler = null;
+ }
+
+ // As the switch statement itself is breakable, an unlabelled break can't be in the switch statement,
+ // so we need to test for it separately.
+ if (splitState.hasBreak) {
+ // if(:scope.getScopeState() == Scope.BREAK) { break; }
+ splitStateHandler = makeIfStateEquals(firstLineNumber, token, finish, BREAK_STATE,
+ enblockAndVisit(new BreakNode(NO_LINE_NUMBER, token, finish, null)), splitStateHandler);
+ }
+
+ // Finally, if the split node had a return statement, but there were no external jumps, we didn't have
+ // the switch statement to handle the return, so we need a separate if for it.
+ if (hasReturn && jumpCount == 0) {
+ // if (:scope.getScopeState() == Scope.RETURN) { return :return; }
+ splitStateHandler = makeIfStateEquals(NO_LINE_NUMBER, token, finish, RETURN_STATE,
+ createReturnFromSplit(), splitStateHandler);
+ }
+
+ if (splitStateHandler != null) {
+ appendStatement(splitStateHandler);
+ }
+
+ return splitNode;
+ }
+
+ private static void addCase(final List<CaseNode> cases, final int i, final Block body) {
+ cases.add(new CaseNode(NO_TOKEN, NO_FINISH, intLiteral(i), body));
+ }
+
+ private static LiteralNode<Number> intLiteral(final int i) {
+ return LiteralNode.newInstance(NO_TOKEN, NO_FINISH, i);
+ }
+
+ private static Block createReturnFromSplit() {
+ return new Block(NO_TOKEN, NO_FINISH, createReturnReturn());
+ }
+
+ private static ReturnNode createReturnReturn() {
+ return new ReturnNode(NO_LINE_NUMBER, NO_TOKEN, NO_FINISH, createReturnIdent());
+ }
+
+ private static IdentNode createReturnIdent() {
+ return createIdent(RETURN_NAME);
+ }
+
+ private static IdentNode createReturnParamIdent() {
+ return createIdent(RETURN_PARAM_NAME);
+ }
+
+ private static IdentNode createIdent(final String name) {
+ return new IdentNode(NO_TOKEN, NO_FINISH, name);
+ }
+
+ private Block enblockAndVisit(final JumpStatement jump) {
+ artificialBlock = true;
+ final Block block = (Block)new Block(NO_TOKEN, NO_FINISH, jump).accept(this);
+ artificialBlock = false;
+ return block;
+ }
+
+ private static IfNode makeIfStateEquals(final int lineNumber, final long token, final int finish,
+ final int value, final Block pass, final Statement fail) {
+ return new IfNode(lineNumber, token, finish,
+ new BinaryNode(Token.recast(token, TokenType.EQ_STRICT),
+ GetSplitState.INSTANCE, intLiteral(value)),
+ pass,
+ fail == null ? null : new Block(NO_TOKEN, NO_FINISH, fail));
+ }
+
+ @Override
+ public boolean enterVarNode(VarNode varNode) {
+ if (!inSplitNode()) {
+ return super.enterVarNode(varNode);
+ }
+ assert !varNode.isBlockScoped(); //TODO: we must handle these too, but we currently don't
+
+ final Expression init = varNode.getInit();
+ if (varNode.isAnonymousFunctionDeclaration()) {
+ // We ain't moving anonymous function declarations.
+ return super.enterVarNode(varNode);
+ }
+
+ // Move a declaration-only var statement to the top of the outermost function.
+ getCurrentFunctionState().varStatements.add(varNode.setInit(null));
+ // If it had an initializer, replace it with an assignment expression statement. Note that "var" is a
+ // statement, so it doesn't contribute to :return of the programs, therefore we are _not_ adding a
+ // ":return = ..." assignment around the original assignment.
+ if (init != null) {
+ final long token = Token.recast(varNode.getToken(), TokenType.ASSIGN);
+ new ExpressionStatement(varNode.getLineNumber(), token, varNode.getFinish(),
+ new BinaryNode(token, varNode.getName(), varNode.getInit())).accept(this);
+ }
+
+ return false;
+ }
+
+ @Override
+ public Node leaveBlock(final Block block) {
+ if (!artificialBlock) {
+ if (lc.isFunctionBody()) {
+ // Prepend declaration-only var statements to the top of the statement list.
+ lc.prependStatements(getCurrentFunctionState().varStatements);
+ } else if (lc.isSplitBody()) {
+ appendSplitReturn(FALLTHROUGH_STATE, NO_LINE_NUMBER);
+ if (getCurrentFunctionState().fn.isProgram()) {
+ // If we're splitting the program, make sure every shard ends with "return :return" and
+ // begins with ":return = :return-in;".
+ lc.prependStatement(new ExpressionStatement(NO_LINE_NUMBER, NO_TOKEN, NO_FINISH,
+ new BinaryNode(Token.toDesc(TokenType.ASSIGN, 0, 0), createReturnIdent(), createReturnParamIdent())));
+ }
+ }
+ }
+ return block;
+ }
+
+ @Override
+ public Node leaveBreakNode(final BreakNode breakNode) {
+ return leaveJumpNode(breakNode);
+ }
+
+ @Override
+ public Node leaveContinueNode(final ContinueNode continueNode) {
+ return leaveJumpNode(continueNode);
+ }
+
+ private JumpStatement leaveJumpNode(final JumpStatement jump) {
+ if (inSplitNode()) {
+ final SplitState splitState = getCurrentSplitState();
+ final SplitNode splitNode = splitState.splitNode;
+ if (lc.isExternalTarget(splitNode, jump.getTarget(lc))) {
+ appendSplitReturn(splitState.getSplitStateIndex(jump), jump.getLineNumber());
+ return jump;
+ }
+ }
+ appendStatement(jump);
+ return jump;
+ }
+
+ private void appendSplitReturn(final int splitState, final int lineNumber) {
+ appendStatement(new SetSplitState(splitState, lineNumber));
+ if (getCurrentFunctionState().fn.isProgram()) {
+ // If we're splitting the program, make sure every fragment passes back :return
+ appendStatement(createReturnReturn());
+ } else {
+ appendStatement(SplitReturn.INSTANCE);
+ }
+ }
+
+ @Override
+ public Node leaveReturnNode(final ReturnNode returnNode) {
+ if(inSplitNode()) {
+ appendStatement(new SetSplitState(RETURN_STATE, returnNode.getLineNumber()));
+ getCurrentSplitState().hasReturn = true;
+ }
+ appendStatement(returnNode);
+ return returnNode;
+ }
+
+ private void appendStatement(final Statement statement) {
+ lc.appendStatement(statement);
+ }
+
+ private boolean inSplitNode() {
+ return getCurrentFunctionState().splitDepth > 0;
+ }
+
+ private FunctionState getCurrentFunctionState() {
+ return functionStates.peek();
+ }
+
+ private SplitState getCurrentSplitState() {
+ return splitStates.peek();
+ }
+
+ private static class FunctionState {
+ final FunctionNode fn;
+ final List<Statement> varStatements = new ArrayList<>();
+ int splitDepth;
+
+ FunctionState(final FunctionNode fn) {
+ this.fn = fn;
+ }
+ }
+
+ private static class SplitState {
+ final SplitNode splitNode;
+ boolean hasReturn;
+ boolean hasBreak;
+
+ final List<JumpStatement> jumpStatements = new ArrayList<>();
+
+ int getSplitStateIndex(final JumpStatement jump) {
+ if (jump instanceof BreakNode && jump.getLabelName() == null) {
+ // Unlabelled break is a special case
+ hasBreak = true;
+ return BREAK_STATE;
+ }
+
+ int i = 0;
+ for(final JumpStatement exJump: jumpStatements) {
+ if (jump.getClass() == exJump.getClass() && Objects.equals(jump.getLabelName(), exJump.getLabelName())) {
+ return i + FIRST_JUMP_STATE;
+ }
+ ++i;
+ }
+ jumpStatements.add(jump);
+ return i + FIRST_JUMP_STATE;
+ }
+
+ SplitState(final SplitNode splitNode) {
+ this.splitNode = splitNode;
+ }
+ }
+}
diff --git a/src/jdk/nashorn/internal/codegen/SplitMethodEmitter.java b/src/jdk/nashorn/internal/codegen/SplitMethodEmitter.java
deleted file mode 100644
index 792a6255..00000000
--- a/src/jdk/nashorn/internal/codegen/SplitMethodEmitter.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package jdk.nashorn.internal.codegen;
-
-import java.util.ArrayList;
-import java.util.List;
-import jdk.internal.org.objectweb.asm.MethodVisitor;
-import jdk.nashorn.internal.codegen.types.Type;
-import jdk.nashorn.internal.ir.BreakableNode;
-import jdk.nashorn.internal.ir.LexicalContext;
-import jdk.nashorn.internal.ir.SplitNode;
-
-/**
- * Emitter used for splitting methods. Needs to keep track of if there are jump targets
- * outside the current split node. All external jump targets encountered at method
- * emission are logged, and {@code CodeGenerator#leaveSplitNode(SplitNode)} creates
- * an appropriate jump table when the SplitNode has been iterated through
- */
-public class SplitMethodEmitter extends MethodEmitter {
-
- private final SplitNode splitNode;
-
- private final List<Label> externalTargets = new ArrayList<>();
- /**
- * In addition to external target labels, we need to track the target breakables too as the code generator needs to
- * be able to correctly pop the scopes to the target, see {@link CodeGenerator#leaveSplitNode(SplitNode)}. Note that
- * this is only used within CodeGenerator, which doesn't mutate the AST, so keeping pointers to other nodes is not
- * incorrect.
- */
- private final List<BreakableNode> externalTargetNodes = new ArrayList<>();
-
- SplitMethodEmitter(final ClassEmitter classEmitter, final MethodVisitor mv, final SplitNode splitNode) {
- super(classEmitter, mv);
- this.splitNode = splitNode;
- }
-
- @Override
- void splitAwareGoto(final LexicalContext lc, final Label label, final BreakableNode targetNode) {
- assert splitNode != null;
- final int index = findExternalTarget(lc, label, targetNode);
- if (index >= 0) {
- setSplitState(index + 1); // 0 is ordinary return
- final Type retType = functionNode.getReturnType();
- loadUndefined(retType);
- _return(retType);
- } else {
- super.splitAwareGoto(lc, label, targetNode);
- }
- }
-
- private int findExternalTarget(final LexicalContext lc, final Label label, final BreakableNode targetNode) {
- final int index = externalTargets.indexOf(label);
-
- if (index >= 0) {
- return index;
- }
-
- if (lc.isExternalTarget(splitNode, targetNode)) {
- externalTargets.add(label);
- externalTargetNodes.add(targetNode);
- return externalTargets.size() - 1;
- }
- return -1;
- }
-
- @Override
- MethodEmitter registerReturn() {
- setHasReturn();
- return setSplitState(0);
- }
-
- final List<Label> getExternalTargets() {
- return externalTargets;
- }
-
- final List<BreakableNode> getExternalTargetNodes() {
- return externalTargetNodes;
- }
-}
diff --git a/src/jdk/nashorn/internal/codegen/TypeEvaluator.java b/src/jdk/nashorn/internal/codegen/TypeEvaluator.java
index 11a23ecd..4f3bc07f 100644
--- a/src/jdk/nashorn/internal/codegen/TypeEvaluator.java
+++ b/src/jdk/nashorn/internal/codegen/TypeEvaluator.java
@@ -29,16 +29,22 @@ import static jdk.nashorn.internal.runtime.Property.NOT_CONFIGURABLE;
import static jdk.nashorn.internal.runtime.Property.NOT_ENUMERABLE;
import static jdk.nashorn.internal.runtime.Property.NOT_WRITABLE;
+import java.lang.invoke.MethodType;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.AccessNode;
+import jdk.nashorn.internal.ir.CallNode;
import jdk.nashorn.internal.ir.Expression;
+import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.IndexNode;
import jdk.nashorn.internal.ir.Optimistic;
+import jdk.nashorn.internal.objects.ArrayBufferView;
import jdk.nashorn.internal.objects.NativeArray;
import jdk.nashorn.internal.runtime.FindProperty;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.Property;
+import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
+import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.ScriptRuntime;
@@ -47,6 +53,13 @@ import jdk.nashorn.internal.runtime.ScriptRuntime;
* Used during recompilation.
*/
final class TypeEvaluator {
+ /**
+ * Type signature for invocation of functions without parameters: we must pass (callee, this) of type
+ * (ScriptFunction, Object) respectively. We also use Object as the return type (we must pass something,
+ * but it'll be ignored; it can't be void, though).
+ */
+ private static final MethodType EMPTY_INVOCATION_TYPE = MethodType.methodType(Object.class, ScriptFunction.class, Object.class);
+
private final Compiler compiler;
private final ScriptObject runtimeScope;
@@ -190,30 +203,46 @@ final class TypeEvaluator {
return null;
}
return getPropertyType(runtimeScope, ((IdentNode)expr).getName());
- }
-
- if (expr instanceof AccessNode) {
+ } else if (expr instanceof AccessNode) {
final AccessNode accessNode = (AccessNode)expr;
final Object base = evaluateSafely(accessNode.getBase());
if (!(base instanceof ScriptObject)) {
return null;
}
return getPropertyType((ScriptObject)base, accessNode.getProperty());
- }
-
- if (expr instanceof IndexNode) {
+ } else if (expr instanceof IndexNode) {
final IndexNode indexNode = (IndexNode)expr;
final Object base = evaluateSafely(indexNode.getBase());
- if(!(base instanceof NativeArray)) {
- // We only know how to deal with NativeArray. TODO: maybe manage buffers too
- return null;
+ if(base instanceof NativeArray || base instanceof ArrayBufferView) {
+ // NOTE: optimistic array getters throw UnwarrantedOptimismException based on the type of their
+ // underlying array storage, not based on values of individual elements. Thus, a LongArrayData will
+ // throw UOE for every optimistic int linkage attempt, even if the long value being returned in the
+ // first invocation would be representable as int. That way, we can presume that the array's optimistic
+ // type is the most optimistic type for which an element getter has a chance of executing successfully.
+ return ((ScriptObject)base).getArray().getOptimisticType();
+ }
+ } else if (expr instanceof CallNode) {
+ // Currently, we'll only try to guess the return type of immediately invoked function expressions with no
+ // parameters, that is (function() { ... })(). We could do better, but these are all heuristics and we can
+ // gradually introduce them as needed. An easy one would be to do the same for .call(this) idiom.
+ final CallNode callExpr = (CallNode)expr;
+ final Expression fnExpr = callExpr.getFunction();
+ if (fnExpr instanceof FunctionNode) {
+ final FunctionNode fn = (FunctionNode)fnExpr;
+ if (callExpr.getArgs().isEmpty()) {
+ final RecompilableScriptFunctionData data = compiler.getScriptFunctionData(fn.getId());
+ if (data != null) {
+ final Type returnType = Type.typeFor(data.getReturnType(EMPTY_INVOCATION_TYPE, runtimeScope));
+ if (returnType == Type.BOOLEAN) {
+ // We don't have optimistic booleans. In fact, optimistic call sites getting back boolean
+ // currently deoptimize all the way to Object.
+ return Type.OBJECT;
+ }
+ assert returnType == Type.INT || returnType == Type.LONG || returnType == Type.NUMBER || returnType == Type.OBJECT;
+ return returnType;
+ }
+ }
}
- // NOTE: optimistic array getters throw UnwarrantedOptimismException based on the type of their underlying
- // array storage, not based on values of individual elements. Thus, a LongArrayData will throw UOE for every
- // optimistic int linkage attempt, even if the long value being returned in the first invocation would be
- // representable as int. That way, we can presume that the array's optimistic type is the most optimistic
- // type for which an element getter has a chance of executing successfully.
- return ((NativeArray)base).getArray().getOptimisticType();
}
return null;
diff --git a/src/jdk/nashorn/internal/codegen/types/ArrayType.java b/src/jdk/nashorn/internal/codegen/types/ArrayType.java
index b6701df4..442e3955 100644
--- a/src/jdk/nashorn/internal/codegen/types/ArrayType.java
+++ b/src/jdk/nashorn/internal/codegen/types/ArrayType.java
@@ -37,6 +37,7 @@ import jdk.internal.org.objectweb.asm.MethodVisitor;
* This is an array type, i.e. OBJECT_ARRAY, NUMBER_ARRAY.
*/
public class ArrayType extends ObjectType implements BytecodeArrayOps {
+ private static final long serialVersionUID = 1L;
/**
* Constructor
diff --git a/src/jdk/nashorn/internal/codegen/types/BitwiseType.java b/src/jdk/nashorn/internal/codegen/types/BitwiseType.java
index dbd0aa14..45fa5276 100644
--- a/src/jdk/nashorn/internal/codegen/types/BitwiseType.java
+++ b/src/jdk/nashorn/internal/codegen/types/BitwiseType.java
@@ -29,6 +29,7 @@ package jdk.nashorn.internal.codegen.types;
* This class represents a numeric type that can be used for bit operations.
*/
public abstract class BitwiseType extends NumericType implements BytecodeBitwiseOps {
+ private static final long serialVersionUID = 1L;
/**
* Constructor
diff --git a/src/jdk/nashorn/internal/codegen/types/BooleanType.java b/src/jdk/nashorn/internal/codegen/types/BooleanType.java
index 4510102b..234dbdba 100644
--- a/src/jdk/nashorn/internal/codegen/types/BooleanType.java
+++ b/src/jdk/nashorn/internal/codegen/types/BooleanType.java
@@ -69,6 +69,7 @@ import jdk.nashorn.internal.codegen.CompilerConstants;
* The boolean type class
*/
public final class BooleanType extends Type {
+ private static final long serialVersionUID = 1L;
private static final CompilerConstants.Call VALUE_OF = staticCallNoLookup(Boolean.class, "valueOf", Boolean.class, boolean.class);
private static final CompilerConstants.Call TO_STRING = staticCallNoLookup(Boolean.class, "toString", String.class, boolean.class);
diff --git a/src/jdk/nashorn/internal/codegen/types/IntType.java b/src/jdk/nashorn/internal/codegen/types/IntType.java
index b6421860..dfa77467 100644
--- a/src/jdk/nashorn/internal/codegen/types/IntType.java
+++ b/src/jdk/nashorn/internal/codegen/types/IntType.java
@@ -60,6 +60,7 @@ import jdk.nashorn.internal.codegen.CompilerConstants;
* Type class: INT
*/
class IntType extends BitwiseType {
+ private static final long serialVersionUID = 1L;
private static final CompilerConstants.Call TO_STRING = staticCallNoLookup(Integer.class, "toString", String.class, int.class);
private static final CompilerConstants.Call VALUE_OF = staticCallNoLookup(Integer.class, "valueOf", Integer.class, int.class);
diff --git a/src/jdk/nashorn/internal/codegen/types/LongType.java b/src/jdk/nashorn/internal/codegen/types/LongType.java
index 107a2794..7c3b2675 100644
--- a/src/jdk/nashorn/internal/codegen/types/LongType.java
+++ b/src/jdk/nashorn/internal/codegen/types/LongType.java
@@ -54,6 +54,7 @@ import jdk.nashorn.internal.runtime.JSType;
* Type class: LONG
*/
class LongType extends BitwiseType {
+ private static final long serialVersionUID = 1L;
private static final CompilerConstants.Call VALUE_OF = staticCallNoLookup(Long.class, "valueOf", Long.class, long.class);
diff --git a/src/jdk/nashorn/internal/codegen/types/NumberType.java b/src/jdk/nashorn/internal/codegen/types/NumberType.java
index e5f14321..924880b1 100644
--- a/src/jdk/nashorn/internal/codegen/types/NumberType.java
+++ b/src/jdk/nashorn/internal/codegen/types/NumberType.java
@@ -46,6 +46,7 @@ import jdk.nashorn.internal.codegen.CompilerConstants;
import jdk.nashorn.internal.runtime.JSType;
class NumberType extends NumericType {
+ private static final long serialVersionUID = 1L;
private static final CompilerConstants.Call VALUE_OF = staticCallNoLookup(Double.class, "valueOf", Double.class, double.class);
diff --git a/src/jdk/nashorn/internal/codegen/types/NumericType.java b/src/jdk/nashorn/internal/codegen/types/NumericType.java
index 597ff578..d536cc7d 100644
--- a/src/jdk/nashorn/internal/codegen/types/NumericType.java
+++ b/src/jdk/nashorn/internal/codegen/types/NumericType.java
@@ -29,6 +29,8 @@ package jdk.nashorn.internal.codegen.types;
* This is a numeric type, i.e. NUMBER, LONG, INT, INT32.
*/
public abstract class NumericType extends Type implements BytecodeNumericOps {
+ private static final long serialVersionUID = 1L;
+
/**
* Constructor
*
diff --git a/src/jdk/nashorn/internal/codegen/types/ObjectType.java b/src/jdk/nashorn/internal/codegen/types/ObjectType.java
index 0ccf6644..1538489e 100644
--- a/src/jdk/nashorn/internal/codegen/types/ObjectType.java
+++ b/src/jdk/nashorn/internal/codegen/types/ObjectType.java
@@ -47,6 +47,7 @@ import jdk.nashorn.internal.runtime.Undefined;
* contain a class that is a more specialized object
*/
class ObjectType extends Type {
+ private static final long serialVersionUID = 1L;
protected ObjectType() {
this(Object.class);
diff --git a/src/jdk/nashorn/internal/codegen/types/Type.java b/src/jdk/nashorn/internal/codegen/types/Type.java
index 9e5e5a24..332ee55e 100644
--- a/src/jdk/nashorn/internal/codegen/types/Type.java
+++ b/src/jdk/nashorn/internal/codegen/types/Type.java
@@ -47,9 +47,11 @@ import static jdk.internal.org.objectweb.asm.Opcodes.T_DOUBLE;
import static jdk.internal.org.objectweb.asm.Opcodes.T_INT;
import static jdk.internal.org.objectweb.asm.Opcodes.T_LONG;
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
+
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
+import java.io.Serializable;
import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
@@ -88,19 +90,20 @@ import jdk.nashorn.internal.runtime.linker.Bootstrap;
* INTs rather than OBJECTs
*/
-public abstract class Type implements Comparable<Type>, BytecodeOps {
+public abstract class Type implements Comparable<Type>, BytecodeOps, Serializable {
+ private static final long serialVersionUID = 1L;
/** Human readable name for type */
- private final String name;
+ private transient final String name;
/** Descriptor for type */
- private final String descriptor;
+ private transient final String descriptor;
/** The "weight" of the type. Used for picking widest/least specific common type */
- private final int weight;
+ private transient final int weight;
/** How many bytecode slots does this type occupy */
- private final int slots;
+ private transient final int slots;
/** The class for this type */
private final Class<?> clazz;
@@ -113,7 +116,7 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
Collections.synchronizedMap(new WeakHashMap<Class<?>, jdk.internal.org.objectweb.asm.Type>());
/** Internal ASM type for this Type - computed once at construction */
- private final jdk.internal.org.objectweb.asm.Type internalType;
+ private transient final jdk.internal.org.objectweb.asm.Type internalType;
/** Weights are used to decide which types are "wider" than other types */
protected static final int MIN_WEIGHT = -1;
@@ -934,6 +937,8 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
* This is the singleton for integer arrays
*/
public static final ArrayType INT_ARRAY = new ArrayType(int[].class) {
+ private static final long serialVersionUID = 1L;
+
@Override
public void astore(final MethodVisitor method) {
method.visitInsn(IASTORE);
@@ -961,6 +966,8 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
* This is the singleton for long arrays
*/
public static final ArrayType LONG_ARRAY = new ArrayType(long[].class) {
+ private static final long serialVersionUID = 1L;
+
@Override
public void astore(final MethodVisitor method) {
method.visitInsn(LASTORE);
@@ -988,6 +995,8 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
* This is the singleton for numeric arrays
*/
public static final ArrayType NUMBER_ARRAY = new ArrayType(double[].class) {
+ private static final long serialVersionUID = 1L;
+
@Override
public void astore(final MethodVisitor method) {
method.visitInsn(DASTORE);
@@ -1022,6 +1031,8 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
/** This type, always an object type, just a toString override */
public static final Type THIS = new ObjectType() {
+ private static final long serialVersionUID = 1L;
+
@Override
public String toString() {
return "this";
@@ -1030,6 +1041,8 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
/** Scope type, always an object type, just a toString override */
public static final Type SCOPE = new ObjectType() {
+ private static final long serialVersionUID = 1L;
+
@Override
public String toString() {
return "scope";
@@ -1041,6 +1054,7 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
}
private abstract static class ValueLessType extends Type {
+ private static final long serialVersionUID = 1L;
ValueLessType(final String name) {
super(name, Unknown.class, MIN_WEIGHT, 1);
@@ -1092,6 +1106,8 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
* inference. It has the minimum type width
*/
public static final Type UNKNOWN = new ValueLessType("<unknown>") {
+ private static final long serialVersionUID = 1L;
+
@Override
public String getDescriptor() {
return "<unknown>";
@@ -1108,6 +1124,7 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
* inference. It has the minimum type width
*/
public static final Type SLOT_2 = new ValueLessType("<slot_2>") {
+ private static final long serialVersionUID = 1L;
@Override
public String getDescriptor() {
@@ -1124,4 +1141,8 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
cache.put(type.getTypeClass(), type);
return type;
}
+
+ protected final Object readResolve() {
+ return Type.typeFor(clazz);
+ }
}
diff --git a/src/jdk/nashorn/internal/ir/AccessNode.java b/src/jdk/nashorn/internal/ir/AccessNode.java
index b354c1f6..315ee395 100644
--- a/src/jdk/nashorn/internal/ir/AccessNode.java
+++ b/src/jdk/nashorn/internal/ir/AccessNode.java
@@ -34,6 +34,8 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
*/
@Immutable
public final class AccessNode extends BaseNode {
+ private static final long serialVersionUID = 1L;
+
/** Property name. */
private final String property;
diff --git a/src/jdk/nashorn/internal/ir/BaseNode.java b/src/jdk/nashorn/internal/ir/BaseNode.java
index 160833ec..35479a7a 100644
--- a/src/jdk/nashorn/internal/ir/BaseNode.java
+++ b/src/jdk/nashorn/internal/ir/BaseNode.java
@@ -39,6 +39,7 @@ import jdk.nashorn.internal.ir.annotations.Immutable;
*/
@Immutable
public abstract class BaseNode extends Expression implements FunctionCall, Optimistic {
+ private static final long serialVersionUID = 1L;
/** Base Node. */
protected final Expression base;
diff --git a/src/jdk/nashorn/internal/ir/BinaryNode.java b/src/jdk/nashorn/internal/ir/BinaryNode.java
index f32f6325..c2456363 100644
--- a/src/jdk/nashorn/internal/ir/BinaryNode.java
+++ b/src/jdk/nashorn/internal/ir/BinaryNode.java
@@ -43,6 +43,8 @@ import jdk.nashorn.internal.parser.TokenType;
*/
@Immutable
public final class BinaryNode extends Expression implements Assignment<Expression>, Optimistic {
+ private static final long serialVersionUID = 1L;
+
// Placeholder for "undecided optimistic ADD type". Unfortunately, we can't decide the type of ADD during optimistic
// type calculation as it can have local variables as its operands that will decide its ultimate type.
private static final Type OPTIMISTIC_UNDECIDED_TYPE = Type.typeFor(new Object(){/*empty*/}.getClass());
@@ -56,8 +58,8 @@ public final class BinaryNode extends Expression implements Assignment<Expressio
private final Type type;
- private Type cachedType;
- private Object cachedTypeFunction;
+ private transient Type cachedType;
+ private transient Object cachedTypeFunction;
@Ignore
private static final Set<TokenType> CAN_OVERFLOW =
diff --git a/src/jdk/nashorn/internal/ir/Block.java b/src/jdk/nashorn/internal/ir/Block.java
index 86a84ca6..12e4fcd8 100644
--- a/src/jdk/nashorn/internal/ir/Block.java
+++ b/src/jdk/nashorn/internal/ir/Block.java
@@ -42,6 +42,8 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
*/
@Immutable
public class Block extends Node implements BreakableNode, Terminal, Flags<Block> {
+ private static final long serialVersionUID = 1L;
+
/** List of statements */
protected final List<Statement> statements;
diff --git a/src/jdk/nashorn/internal/ir/BlockLexicalContext.java b/src/jdk/nashorn/internal/ir/BlockLexicalContext.java
index d437069e..7e16b5e6 100644
--- a/src/jdk/nashorn/internal/ir/BlockLexicalContext.java
+++ b/src/jdk/nashorn/internal/ir/BlockLexicalContext.java
@@ -109,6 +109,16 @@ public class BlockLexicalContext extends LexicalContext {
}
/**
+ * Prepend a list of statement to the block being generated
+ * @param statements a list of statements to prepend
+ */
+ public void prependStatements(final List<Statement> statements) {
+ assert statements != null;
+ sstack.peek().addAll(0, statements);
+ }
+
+
+ /**
* Get the last statement that was emitted into a block
* @return the last statement emitted
*/
diff --git a/src/jdk/nashorn/internal/ir/BlockStatement.java b/src/jdk/nashorn/internal/ir/BlockStatement.java
index 6b1c155c..6f6a1d23 100644
--- a/src/jdk/nashorn/internal/ir/BlockStatement.java
+++ b/src/jdk/nashorn/internal/ir/BlockStatement.java
@@ -32,6 +32,8 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
* Represents a block used as a statement.
*/
public class BlockStatement extends Statement {
+ private static final long serialVersionUID = 1L;
+
/** Block to execute. */
private final Block block;
diff --git a/src/jdk/nashorn/internal/ir/BreakNode.java b/src/jdk/nashorn/internal/ir/BreakNode.java
index 7a607922..4cbf4c02 100644
--- a/src/jdk/nashorn/internal/ir/BreakNode.java
+++ b/src/jdk/nashorn/internal/ir/BreakNode.java
@@ -34,6 +34,7 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
*/
@Immutable
public final class BreakNode extends JumpStatement {
+ private static final long serialVersionUID = 1L;
/**
* Constructor
diff --git a/src/jdk/nashorn/internal/ir/BreakableStatement.java b/src/jdk/nashorn/internal/ir/BreakableStatement.java
index e0a67b16..7e1afaf0 100644
--- a/src/jdk/nashorn/internal/ir/BreakableStatement.java
+++ b/src/jdk/nashorn/internal/ir/BreakableStatement.java
@@ -32,6 +32,7 @@ import jdk.nashorn.internal.ir.annotations.Immutable;
@Immutable
abstract class BreakableStatement extends LexicalContextStatement implements BreakableNode {
+ private static final long serialVersionUID = 1L;
/** break label. */
protected final Label breakLabel;
diff --git a/src/jdk/nashorn/internal/ir/CallNode.java b/src/jdk/nashorn/internal/ir/CallNode.java
index 189a419a..72eab68b 100644
--- a/src/jdk/nashorn/internal/ir/CallNode.java
+++ b/src/jdk/nashorn/internal/ir/CallNode.java
@@ -27,6 +27,7 @@ package jdk.nashorn.internal.ir;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
+import java.io.Serializable;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
@@ -40,6 +41,7 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
*/
@Immutable
public final class CallNode extends LexicalContextExpression implements Optimistic {
+ private static final long serialVersionUID = 1L;
/** Function identifier or function body. */
private final Expression function;
@@ -64,7 +66,8 @@ public final class CallNode extends LexicalContextExpression implements Optimist
/**
* Arguments to be passed to builtin {@code eval} function
*/
- public static class EvalArgs {
+ public static class EvalArgs implements Serializable {
+ private static final long serialVersionUID = 1L;
private final List<Expression> args;
/** location string for the eval call */
diff --git a/src/jdk/nashorn/internal/ir/CaseNode.java b/src/jdk/nashorn/internal/ir/CaseNode.java
index aae6d71f..48f96184 100644
--- a/src/jdk/nashorn/internal/ir/CaseNode.java
+++ b/src/jdk/nashorn/internal/ir/CaseNode.java
@@ -37,6 +37,8 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
*/
@Immutable
public final class CaseNode extends Node implements JoinPredecessor, Labels, Terminal {
+ private static final long serialVersionUID = 1L;
+
/** Test expression. */
private final Expression test;
diff --git a/src/jdk/nashorn/internal/ir/CatchNode.java b/src/jdk/nashorn/internal/ir/CatchNode.java
index 1a496dda..80ca3a50 100644
--- a/src/jdk/nashorn/internal/ir/CatchNode.java
+++ b/src/jdk/nashorn/internal/ir/CatchNode.java
@@ -33,6 +33,8 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
*/
@Immutable
public final class CatchNode extends Statement {
+ private static final long serialVersionUID = 1L;
+
/** Exception identifier. */
private final IdentNode exception;
diff --git a/src/jdk/nashorn/internal/ir/ContinueNode.java b/src/jdk/nashorn/internal/ir/ContinueNode.java
index ce621791..c58531bd 100644
--- a/src/jdk/nashorn/internal/ir/ContinueNode.java
+++ b/src/jdk/nashorn/internal/ir/ContinueNode.java
@@ -34,6 +34,8 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
*/
@Immutable
public class ContinueNode extends JumpStatement {
+ private static final long serialVersionUID = 1L;
+
/**
* Constructor
*
@@ -80,4 +82,3 @@ public class ContinueNode extends JumpStatement {
return ((LoopNode)target).getContinueLabel();
}
}
-
diff --git a/src/jdk/nashorn/internal/ir/EmptyNode.java b/src/jdk/nashorn/internal/ir/EmptyNode.java
index c0e3c2c0..2c9d976d 100644
--- a/src/jdk/nashorn/internal/ir/EmptyNode.java
+++ b/src/jdk/nashorn/internal/ir/EmptyNode.java
@@ -33,6 +33,7 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
*/
@Immutable
public final class EmptyNode extends Statement {
+ private static final long serialVersionUID = 1L;
/**
* Constructor
diff --git a/src/jdk/nashorn/internal/ir/Expression.java b/src/jdk/nashorn/internal/ir/Expression.java
index c6b22760..78523412 100644
--- a/src/jdk/nashorn/internal/ir/Expression.java
+++ b/src/jdk/nashorn/internal/ir/Expression.java
@@ -35,6 +35,8 @@ import jdk.nashorn.internal.runtime.UnwarrantedOptimismException;
*
*/
public abstract class Expression extends Node {
+ private static final long serialVersionUID = 1L;
+
static final String OPT_IDENTIFIER = "%";
private static final Function<Symbol, Type> UNKNOWN_LOCALS = new Function<Symbol, Type>() {
diff --git a/src/jdk/nashorn/internal/ir/ExpressionStatement.java b/src/jdk/nashorn/internal/ir/ExpressionStatement.java
index 45870ff0..b8e54280 100644
--- a/src/jdk/nashorn/internal/ir/ExpressionStatement.java
+++ b/src/jdk/nashorn/internal/ir/ExpressionStatement.java
@@ -35,6 +35,8 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
*/
@Immutable
public final class ExpressionStatement extends Statement {
+ private static final long serialVersionUID = 1L;
+
/** Expression to execute. */
private final Expression expression;
diff --git a/src/jdk/nashorn/internal/ir/ForNode.java b/src/jdk/nashorn/internal/ir/ForNode.java
index 9b4cb6d6..2847947c 100644
--- a/src/jdk/nashorn/internal/ir/ForNode.java
+++ b/src/jdk/nashorn/internal/ir/ForNode.java
@@ -33,6 +33,8 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
*/
@Immutable
public final class ForNode extends LoopNode {
+ private static final long serialVersionUID = 1L;
+
/** Initialize expression for an ordinary for statement, or the LHS expression receiving iterated-over values in a
* for-in statement. */
private final Expression init;
diff --git a/src/jdk/nashorn/internal/ir/FunctionNode.java b/src/jdk/nashorn/internal/ir/FunctionNode.java
index 1bbc7ab0..0806fea9 100644
--- a/src/jdk/nashorn/internal/ir/FunctionNode.java
+++ b/src/jdk/nashorn/internal/ir/FunctionNode.java
@@ -57,6 +57,8 @@ import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
*/
@Immutable
public final class FunctionNode extends LexicalContextExpression implements Flags<FunctionNode>, CompileUnitHolder {
+ private static final long serialVersionUID = 1L;
+
/** Type used for all FunctionNodes */
public static final Type FUNCTION_TYPE = Type.typeFor(ScriptFunction.class);
@@ -107,7 +109,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
}
/** Source of entity. */
- private final Source source;
+ private transient final Source source;
/**
* Opaque object representing parser state at the end of the function. Used when reparsing outer functions
@@ -141,7 +143,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
private final long lastToken;
/** Method's namespace. */
- private final Namespace namespace;
+ private transient final Namespace namespace;
/** Current compilation state */
@Ignore
@@ -207,31 +209,23 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
/** Are we vararg, but do we just pass the arguments along to apply or call */
public static final int HAS_APPLY_TO_CALL_SPECIALIZATION = 1 << 12;
- /** Does this function explicitly use the {@link CompilerConstants#RETURN} symbol? Some functions are known to
- * always use the return symbol, namely a function that is a program (as it must track its last executed expression
- * statement's value) as well as functions that are split (to communicate return values from inner to outer
- * partitions). Other functions normally don't use the return symbol (so we optimize away its slot), except in some
- * very special cases, e.g. when containing a return statement in a finally block. These special cases set this
- * flag. */
- public static final int USES_RETURN_SYMBOL = 1 << 13;
-
/**
* Is this function the top-level program?
*/
- public static final int IS_PROGRAM = 1 << 14;
+ public static final int IS_PROGRAM = 1 << 13;
/**
* Flag indicating whether this function uses the local variable symbol for itself. Only named function expressions
* can have this flag set if they reference themselves (e.g. "(function f() { return f })". Declared functions will
* use the symbol in their parent scope instead when they reference themselves by name.
*/
- public static final int USES_SELF_SYMBOL = 1 << 15;
+ public static final int USES_SELF_SYMBOL = 1 << 14;
/** Does this function use the "this" keyword? */
- public static final int USES_THIS = 1 << 16;
+ public static final int USES_THIS = 1 << 15;
/** Is this declared in a dynamic context */
- public static final int IN_DYNAMIC_CONTEXT = 1 << 17;
+ public static final int IN_DYNAMIC_CONTEXT = 1 << 16;
/**
* The following flags are derived from directive comments within this function.
@@ -239,28 +233,28 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
*/
/** parser, print parse tree */
- public static final int IS_PRINT_PARSE = 1 << 18;
+ public static final int IS_PRINT_PARSE = 1 << 17;
/** parser, print lower parse tree */
- public static final int IS_PRINT_LOWER_PARSE = 1 << 19;
+ public static final int IS_PRINT_LOWER_PARSE = 1 << 18;
/** parser, print AST */
- public static final int IS_PRINT_AST = 1 << 20;
+ public static final int IS_PRINT_AST = 1 << 19;
/** parser, print lower AST */
- public static final int IS_PRINT_LOWER_AST = 1 << 21;
+ public static final int IS_PRINT_LOWER_AST = 1 << 20;
/** parser, print symbols */
- public static final int IS_PRINT_SYMBOLS = 1 << 22;
+ public static final int IS_PRINT_SYMBOLS = 1 << 21;
// callsite tracing, profiling within this function
/** profile callsites in this function? */
- public static final int IS_PROFILE = 1 << 23;
+ public static final int IS_PROFILE = 1 << 22;
/** trace callsite enterexit in this function? */
- public static final int IS_TRACE_ENTEREXIT = 1 << 24;
+ public static final int IS_TRACE_ENTEREXIT = 1 << 23;
/** trace callsite misses in this function? */
- public static final int IS_TRACE_MISSES = 1 << 25;
+ public static final int IS_TRACE_MISSES = 1 << 24;
/** trace callsite values in this function? */
- public static final int IS_TRACE_VALUES = 1 << 26;
+ public static final int IS_TRACE_VALUES = 1 << 25;
/**
* Whether this function needs the callee {@link ScriptFunction} instance passed to its code as a
@@ -268,7 +262,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
* Rather, it is always calculated (see {@link #needsCallee()}). {@link RecompilableScriptFunctionData}
* will, however, cache the value of this flag.
*/
- public static final int NEEDS_CALLEE = 1 << 27;
+ public static final int NEEDS_CALLEE = 1 << 26;
/** extension callsite flags mask */
public static final int EXTENSION_CALLSITE_FLAGS = IS_PRINT_PARSE |
@@ -285,8 +279,8 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
/** Does this function potentially need "arguments"? Note that this is not a full test, as further negative check of REDEFINES_ARGS is needed. */
private static final int MAYBE_NEEDS_ARGUMENTS = USES_ARGUMENTS | HAS_EVAL;
- /** Does this function need the parent scope? It needs it if either it or its descendants use variables from it, or have a deep eval. */
- private static final int NEEDS_PARENT_SCOPE = USES_ANCESTOR_SCOPE | HAS_DEEP_EVAL;
+ /** Does this function need the parent scope? It needs it if either it or its descendants use variables from it, or have a deep eval, or it's the program. */
+ public static final int NEEDS_PARENT_SCOPE = USES_ANCESTOR_SCOPE | HAS_DEEP_EVAL | IS_PROGRAM;
/** What is the return type of this function? */
private Type returnType = Type.UNKNOWN;
@@ -350,7 +344,8 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
final Block body,
final List<IdentNode> parameters,
final int thisProperties,
- final Class<?> rootClass) {
+ final Class<?> rootClass,
+ final Source source, Namespace namespace) {
super(functionNode);
this.endParserState = endParserState;
@@ -365,11 +360,11 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
this.parameters = parameters;
this.thisProperties = thisProperties;
this.rootClass = rootClass;
+ this.source = source;
+ this.namespace = namespace;
// the fields below never change - they are final and assigned in constructor
- this.source = functionNode.source;
this.ident = functionNode.ident;
- this.namespace = functionNode.namespace;
this.kind = functionNode.kind;
this.firstToken = functionNode.firstToken;
}
@@ -435,6 +430,39 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
}
/**
+ * Sets the source and namespace for this function. It can only set a non-null source and namespace for a function
+ * that currently has both a null source and a null namespace. This is used to re-set the source and namespace for
+ * a deserialized function node.
+ * @param source the source for the function.
+ * @param namespace the namespace for the function
+ * @return a new function node with the set source and namespace
+ * @throws IllegalArgumentException if the specified source or namespace is null
+ * @throws IllegalStateException if the function already has either a source or namespace set.
+ */
+ public FunctionNode initializeDeserialized(final Source source, final Namespace namespace) {
+ if (source == null || namespace == null) {
+ throw new IllegalArgumentException();
+ } else if (this.source == source && this.namespace == namespace) {
+ return this;
+ } else if (this.source != null || this.namespace != null) {
+ throw new IllegalStateException();
+ }
+ return new FunctionNode(
+ this,
+ lastToken,
+ endParserState,
+ flags,
+ name,
+ returnType,
+ compileUnit,
+ compilationState,
+ body,
+ parameters,
+ thisProperties,
+ rootClass, source, namespace);
+ }
+
+ /**
* Get the unique ID for this function within the script file.
* @return the id
*/
@@ -535,6 +563,28 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
}
final EnumSet<CompilationState> newState = EnumSet.copyOf(this.compilationState);
newState.add(state);
+ return setCompilationState(lc, newState);
+ }
+
+ /**
+ * Copy a compilation state from an original function to this function. Used when creating synthetic
+ * function nodes by the splitter.
+ *
+ * @param lc lexical context
+ * @param original the original function node to copy compilation state from
+ * @return function node or a new one if state was changed
+ */
+ public FunctionNode copyCompilationState(final LexicalContext lc, final FunctionNode original) {
+ final EnumSet<CompilationState> origState = original.compilationState;
+ if (!AssertsEnabled.assertsEnabled() || this.compilationState.containsAll(origState)) {
+ return this;
+ }
+ final EnumSet<CompilationState> newState = EnumSet.copyOf(this.compilationState);
+ newState.addAll(origState);
+ return setCompilationState(lc, newState);
+ }
+
+ private FunctionNode setCompilationState(final LexicalContext lc, final EnumSet<CompilationState> compilationState) {
return Node.replaceInLexicalContext(
lc,
this,
@@ -546,13 +596,14 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
name,
returnType,
compileUnit,
- newState,
+ compilationState,
body,
parameters,
thisProperties,
- rootClass));
+ rootClass, source, namespace));
}
+
/**
* Create a unique name in the namespace of this FunctionNode
* @param base prefix for name
@@ -622,7 +673,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
body,
parameters,
thisProperties,
- rootClass));
+ rootClass, source, namespace));
}
@Override
@@ -697,18 +748,11 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
* @return true if the function's generated Java method needs a {@code callee} parameter.
*/
public boolean needsCallee() {
+ // NOTE: we only need isSplit() here to ensure that :scope can never drop below slot 2 for splitting array units.
return needsParentScope() || usesSelfSymbol() || isSplit() || (needsArguments() && !isStrict()) || hasOptimisticApplyToCall();
}
/**
- * Check if this function uses the return symbol
- * @return true if uses the return symbol
- */
- public boolean usesReturnSymbol() {
- return isProgram() || isSplit() || getFlag(USES_RETURN_SYMBOL);
- }
-
- /**
* Return {@code true} if this function makes use of the {@code this} object.
*
* @return true if function uses {@code this} object
@@ -770,7 +814,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
body,
parameters,
thisProperties,
- rootClass));
+ rootClass, source, namespace));
}
/**
@@ -838,7 +882,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
* @return true if the function needs parent scope.
*/
public boolean needsParentScope() {
- return getFlag(NEEDS_PARENT_SCOPE) || isProgram();
+ return getFlag(NEEDS_PARENT_SCOPE);
}
/**
@@ -866,7 +910,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
body,
parameters,
thisProperties,
- rootClass));
+ rootClass, source, namespace));
}
/**
@@ -927,7 +971,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
body,
parameters,
thisProperties,
- rootClass));
+ rootClass, source, namespace));
}
/**
@@ -962,7 +1006,10 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
compilationState,
body,
parameters,
- thisProperties, rootClass));
+ thisProperties,
+ rootClass,
+ source,
+ namespace));
}
/**
@@ -998,7 +1045,9 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
body,
parameters,
thisProperties,
- rootClass));
+ rootClass,
+ source,
+ namespace));
}
/**
@@ -1012,9 +1061,9 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
}
/**
- * Checks if this function is a sub-function generated by splitting a larger one
+ * Checks if this function is split into several smaller fragments.
*
- * @return true if this function is split from a larger one
+ * @return true if this function is split into several smaller fragments.
*/
public boolean isSplit() {
return getFlag(IS_SPLIT);
@@ -1064,7 +1113,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
body,
parameters,
thisProperties,
- rootClass));
+ rootClass, source, namespace));
}
/**
@@ -1143,7 +1192,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
body,
parameters,
thisProperties,
- rootClass
+ rootClass, source, namespace
));
}
@@ -1191,7 +1240,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
body,
parameters,
thisProperties,
- rootClass));
+ rootClass, source, namespace));
}
/**
@@ -1247,6 +1296,6 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
body,
parameters,
thisProperties,
- rootClass));
+ rootClass, source, namespace));
}
}
diff --git a/src/jdk/nashorn/internal/ir/GetSplitState.java b/src/jdk/nashorn/internal/ir/GetSplitState.java
new file mode 100644
index 00000000..bf4a4c01
--- /dev/null
+++ b/src/jdk/nashorn/internal/ir/GetSplitState.java
@@ -0,0 +1,70 @@
+/*
+ * 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.ir;
+
+import java.util.function.Function;
+import jdk.nashorn.internal.codegen.CompilerConstants;
+import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.ir.visitor.NodeVisitor;
+import jdk.nashorn.internal.runtime.Scope;
+
+/**
+ * Synthetic AST node that represents loading of the scope object and invocation of the {@link Scope#getSplitState()}
+ * method on it. It has no JavaScript source representation and only occurs in synthetic functions created by
+ * the split-into-functions transformation.
+ */
+public final class GetSplitState extends Expression {
+ private static final long serialVersionUID = 1L;
+
+ /** The sole instance of this AST node. */
+ public final static GetSplitState INSTANCE = new GetSplitState();
+
+ private GetSplitState() {
+ super(NO_TOKEN, NO_FINISH);
+ }
+
+ @Override
+ public Type getType(final Function<Symbol, Type> localVariableTypes) {
+ return Type.INT;
+ }
+
+ @Override
+ public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
+ return visitor.enterGetSplitState(this) ? visitor.leaveGetSplitState(this) : this;
+ }
+
+ @Override
+ public void toString(final StringBuilder sb, final boolean printType) {
+ if (printType) {
+ sb.append("{I}");
+ }
+ sb.append(CompilerConstants.SCOPE.symbolName()).append('.').append(Scope.GET_SPLIT_STATE.name()).append("()");
+ }
+
+ private Object readResolve() {
+ return INSTANCE;
+ }
+}
diff --git a/src/jdk/nashorn/internal/ir/IdentNode.java b/src/jdk/nashorn/internal/ir/IdentNode.java
index 98349ff2..7059734d 100644
--- a/src/jdk/nashorn/internal/ir/IdentNode.java
+++ b/src/jdk/nashorn/internal/ir/IdentNode.java
@@ -42,6 +42,8 @@ import jdk.nashorn.internal.parser.TokenType;
*/
@Immutable
public final class IdentNode extends Expression implements PropertyKey, FunctionCall, Optimistic, JoinPredecessor {
+ private static final long serialVersionUID = 1L;
+
private static final int PROPERTY_NAME = 1 << 0;
private static final int INITIALIZED_HERE = 1 << 1;
private static final int FUNCTION = 1 << 2;
diff --git a/src/jdk/nashorn/internal/ir/IfNode.java b/src/jdk/nashorn/internal/ir/IfNode.java
index 4561389c..cdebaad9 100644
--- a/src/jdk/nashorn/internal/ir/IfNode.java
+++ b/src/jdk/nashorn/internal/ir/IfNode.java
@@ -33,6 +33,8 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
*/
@Immutable
public final class IfNode extends Statement implements JoinPredecessor {
+ private static final long serialVersionUID = 1L;
+
/** Test expression. */
private final Expression test;
diff --git a/src/jdk/nashorn/internal/ir/IndexNode.java b/src/jdk/nashorn/internal/ir/IndexNode.java
index 3d3c316a..72df1f96 100644
--- a/src/jdk/nashorn/internal/ir/IndexNode.java
+++ b/src/jdk/nashorn/internal/ir/IndexNode.java
@@ -33,6 +33,8 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
*/
@Immutable
public final class IndexNode extends BaseNode {
+ private static final long serialVersionUID = 1L;
+
/** Property index. */
private final Expression index;
diff --git a/src/jdk/nashorn/internal/ir/JoinPredecessorExpression.java b/src/jdk/nashorn/internal/ir/JoinPredecessorExpression.java
index 3242de23..cfb0086b 100644
--- a/src/jdk/nashorn/internal/ir/JoinPredecessorExpression.java
+++ b/src/jdk/nashorn/internal/ir/JoinPredecessorExpression.java
@@ -33,6 +33,7 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
* A wrapper for an expression that is in a position to be a join predecessor.
*/
public class JoinPredecessorExpression extends Expression implements JoinPredecessor {
+ private static final long serialVersionUID = 1L;
private final Expression expression;
private final LocalVariableConversion conversion;
diff --git a/src/jdk/nashorn/internal/ir/JumpStatement.java b/src/jdk/nashorn/internal/ir/JumpStatement.java
index 5888a836..07707171 100644
--- a/src/jdk/nashorn/internal/ir/JumpStatement.java
+++ b/src/jdk/nashorn/internal/ir/JumpStatement.java
@@ -31,6 +31,7 @@ import jdk.nashorn.internal.codegen.Label;
* Common base class for jump statements (e.g. {@code break} and {@code continue}).
*/
public abstract class JumpStatement extends Statement implements JoinPredecessor {
+ private static final long serialVersionUID = 1L;
private final String labelName;
private final LocalVariableConversion conversion;
diff --git a/src/jdk/nashorn/internal/ir/LabelNode.java b/src/jdk/nashorn/internal/ir/LabelNode.java
index 17b97aa2..744ba54b 100644
--- a/src/jdk/nashorn/internal/ir/LabelNode.java
+++ b/src/jdk/nashorn/internal/ir/LabelNode.java
@@ -35,6 +35,8 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
*/
@Immutable
public final class LabelNode extends LexicalContextStatement implements JoinPredecessor {
+ private static final long serialVersionUID = 1L;
+
/** Label ident. */
private final String labelName;
diff --git a/src/jdk/nashorn/internal/ir/LexicalContext.java b/src/jdk/nashorn/internal/ir/LexicalContext.java
index 8048ea31..2b59a07c 100644
--- a/src/jdk/nashorn/internal/ir/LexicalContext.java
+++ b/src/jdk/nashorn/internal/ir/LexicalContext.java
@@ -440,6 +440,14 @@ public class LexicalContext {
}
/**
+ * Is the topmost lexical context element body of a SplitNode?
+ * @return true if it's the body of a split node.
+ */
+ public boolean isSplitBody() {
+ return sp >= 2 && stack[sp - 1] instanceof Block && stack[sp - 2] instanceof SplitNode;
+ }
+
+ /**
* Get the parent function for a function in the lexical context
* @param functionNode function for which to get parent
* @return parent function of functionNode or null if none (e.g. if functionNode is the program)
@@ -472,9 +480,6 @@ public class LexicalContext {
final LexicalContextNode node = iter.next();
if (node == until) {
break;
- } else if (node instanceof SplitNode) {
- // Don't bother popping scopes if we're going to do a return from a split method anyway.
- return 0;
}
assert !(node instanceof FunctionNode); // Can't go outside current function
if (node instanceof WithNode || node instanceof Block && ((Block)node).needsScope()) {
diff --git a/src/jdk/nashorn/internal/ir/LexicalContextExpression.java b/src/jdk/nashorn/internal/ir/LexicalContextExpression.java
index b259a2a3..e9a88cc8 100644
--- a/src/jdk/nashorn/internal/ir/LexicalContextExpression.java
+++ b/src/jdk/nashorn/internal/ir/LexicalContextExpression.java
@@ -28,6 +28,7 @@ package jdk.nashorn.internal.ir;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
abstract class LexicalContextExpression extends Expression implements LexicalContextNode {
+ private static final long serialVersionUID = 1L;
LexicalContextExpression(final LexicalContextExpression expr) {
super(expr);
diff --git a/src/jdk/nashorn/internal/ir/LexicalContextStatement.java b/src/jdk/nashorn/internal/ir/LexicalContextStatement.java
index c7bbfb2f..6a65fad2 100644
--- a/src/jdk/nashorn/internal/ir/LexicalContextStatement.java
+++ b/src/jdk/nashorn/internal/ir/LexicalContextStatement.java
@@ -28,6 +28,8 @@ package jdk.nashorn.internal.ir;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
abstract class LexicalContextStatement extends Statement implements LexicalContextNode {
+ private static final long serialVersionUID = 1L;
+
/**
* Constructor
*
diff --git a/src/jdk/nashorn/internal/ir/LiteralNode.java b/src/jdk/nashorn/internal/ir/LiteralNode.java
index 42a9cf4c..6aee20cd 100644
--- a/src/jdk/nashorn/internal/ir/LiteralNode.java
+++ b/src/jdk/nashorn/internal/ir/LiteralNode.java
@@ -25,6 +25,7 @@
package jdk.nashorn.internal.ir;
+import java.io.Serializable;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -49,6 +50,8 @@ import jdk.nashorn.internal.runtime.Undefined;
*/
@Immutable
public abstract class LiteralNode<T> extends Expression implements PropertyKey {
+ private static final long serialVersionUID = 1L;
+
/** Literal value */
protected final T value;
@@ -270,6 +273,8 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
* @param <T> the literal type
*/
public static class PrimitiveLiteralNode<T> extends LiteralNode<T> {
+ private static final long serialVersionUID = 1L;
+
private PrimitiveLiteralNode(final long token, final int finish, final T value) {
super(token, finish, value);
}
@@ -304,6 +309,7 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
@Immutable
private static final class BooleanLiteralNode extends PrimitiveLiteralNode<Boolean> {
+ private static final long serialVersionUID = 1L;
private BooleanLiteralNode(final long token, final int finish, final boolean value) {
super(Token.recast(token, value ? TokenType.TRUE : TokenType.FALSE), finish, value);
@@ -356,6 +362,7 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
@Immutable
private static final class NumberLiteralNode extends PrimitiveLiteralNode<Number> {
+ private static final long serialVersionUID = 1L;
private final Type type = numberGetType(value);
@@ -418,6 +425,8 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
}
private static class UndefinedLiteralNode extends PrimitiveLiteralNode<Undefined> {
+ private static final long serialVersionUID = 1L;
+
private UndefinedLiteralNode(final long token, final int finish) {
super(Token.recast(token, TokenType.OBJECT), finish, ScriptRuntime.UNDEFINED);
}
@@ -454,6 +463,8 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
@Immutable
private static class StringLiteralNode extends PrimitiveLiteralNode<String> {
+ private static final long serialVersionUID = 1L;
+
private StringLiteralNode(final long token, final int finish, final String value) {
super(Token.recast(token, TokenType.STRING), finish, value);
}
@@ -497,6 +508,8 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
@Immutable
private static class LexerTokenLiteralNode extends LiteralNode<LexerToken> {
+ private static final long serialVersionUID = 1L;
+
private LexerTokenLiteralNode(final long token, final int finish, final LexerToken value) {
super(Token.recast(token, TokenType.STRING), finish, value); //TODO is string the correct token type here?
}
@@ -560,6 +573,7 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
}
private static final class NullLiteralNode extends PrimitiveLiteralNode<Object> {
+ private static final long serialVersionUID = 1L;
private NullLiteralNode(final long token, final int finish) {
super(Token.recast(token, TokenType.OBJECT), finish, null);
@@ -590,6 +604,7 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
*/
@Immutable
public static final class ArrayLiteralNode extends LiteralNode<Expression[]> implements LexicalContextNode {
+ private static final long serialVersionUID = 1L;
/** Array element type. */
private final Type elementType;
@@ -607,7 +622,9 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
* An ArrayUnit is a range in an ArrayLiteral. ArrayLiterals can
* be split if they are too large, for bytecode generation reasons
*/
- public static final class ArrayUnit implements CompileUnitHolder {
+ public static final class ArrayUnit implements CompileUnitHolder, Serializable {
+ private static final long serialVersionUID = 1L;
+
/** Compile unit associated with the postsets range. */
private final CompileUnit compileUnit;
@@ -655,13 +672,13 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
private static final class ArrayLiteralInitializer {
static ArrayLiteralNode initialize(final ArrayLiteralNode node) {
- final Type elementType = computeElementType(node.value, node.elementType);
+ final Type elementType = computeElementType(node.value);
final int[] postsets = computePostsets(node.value);
final Object presets = computePresets(node.value, elementType, postsets);
return new ArrayLiteralNode(node, node.value, elementType, postsets, presets, node.units);
}
- private static Type computeElementType(final Expression[] value, final Type elementType) {
+ private static Type computeElementType(final Expression[] value) {
Type widestElementType = Type.INT;
for (final Expression elem : value) {
diff --git a/src/jdk/nashorn/internal/ir/LoopNode.java b/src/jdk/nashorn/internal/ir/LoopNode.java
index 86ef3cda..e6436ad9 100644
--- a/src/jdk/nashorn/internal/ir/LoopNode.java
+++ b/src/jdk/nashorn/internal/ir/LoopNode.java
@@ -34,6 +34,8 @@ import jdk.nashorn.internal.codegen.Label;
* A loop node, for example a while node, do while node or for node
*/
public abstract class LoopNode extends BreakableStatement {
+ private static final long serialVersionUID = 1L;
+
/** loop continue label. */
protected final Label continueLabel;
diff --git a/src/jdk/nashorn/internal/ir/Node.java b/src/jdk/nashorn/internal/ir/Node.java
index 37ec4b96..d6c57655 100644
--- a/src/jdk/nashorn/internal/ir/Node.java
+++ b/src/jdk/nashorn/internal/ir/Node.java
@@ -25,6 +25,7 @@
package jdk.nashorn.internal.ir;
+import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
@@ -34,7 +35,18 @@ import jdk.nashorn.internal.parser.TokenType;
/**
* Nodes are used to compose Abstract Syntax Trees.
*/
-public abstract class Node implements Cloneable {
+public abstract class Node implements Cloneable, Serializable {
+ private static final long serialVersionUID = 1L;
+
+ /** Constant used for synthetic AST nodes that have no line number. */
+ public static final int NO_LINE_NUMBER = -1;
+
+ /** Constant used for synthetic AST nodes that have no token. */
+ public static final long NO_TOKEN = 0L;
+
+ /** Constant used for synthetic AST nodes that have no finish. */
+ public static final int NO_FINISH = 0;
+
/** Start of source range. */
protected final int start;
diff --git a/src/jdk/nashorn/internal/ir/ObjectNode.java b/src/jdk/nashorn/internal/ir/ObjectNode.java
index 38fd667d..d5bd78b5 100644
--- a/src/jdk/nashorn/internal/ir/ObjectNode.java
+++ b/src/jdk/nashorn/internal/ir/ObjectNode.java
@@ -37,6 +37,7 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
*/
@Immutable
public final class ObjectNode extends Expression {
+ private static final long serialVersionUID = 1L;
/** Literal elements. */
private final List<PropertyNode> elements;
diff --git a/src/jdk/nashorn/internal/ir/PropertyNode.java b/src/jdk/nashorn/internal/ir/PropertyNode.java
index b65d1291..b9b8abea 100644
--- a/src/jdk/nashorn/internal/ir/PropertyNode.java
+++ b/src/jdk/nashorn/internal/ir/PropertyNode.java
@@ -33,6 +33,7 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
*/
@Immutable
public final class PropertyNode extends Node {
+ private static final long serialVersionUID = 1L;
/** Property key. */
private final PropertyKey key;
diff --git a/src/jdk/nashorn/internal/ir/ReturnNode.java b/src/jdk/nashorn/internal/ir/ReturnNode.java
index 5ea12204..1491b834 100644
--- a/src/jdk/nashorn/internal/ir/ReturnNode.java
+++ b/src/jdk/nashorn/internal/ir/ReturnNode.java
@@ -36,6 +36,8 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
*/
@Immutable
public class ReturnNode extends Statement {
+ private static final long serialVersionUID = 1L;
+
/** Optional expression. */
private final Expression expression;
diff --git a/src/jdk/nashorn/internal/ir/RuntimeNode.java b/src/jdk/nashorn/internal/ir/RuntimeNode.java
index 755b3525..fd34c3ec 100644
--- a/src/jdk/nashorn/internal/ir/RuntimeNode.java
+++ b/src/jdk/nashorn/internal/ir/RuntimeNode.java
@@ -42,6 +42,7 @@ import jdk.nashorn.internal.parser.TokenType;
*/
@Immutable
public class RuntimeNode extends Expression implements Optimistic {
+ private static final long serialVersionUID = 1L;
/**
* Request enum used for meta-information about the runtime request
diff --git a/src/jdk/nashorn/internal/ir/SetSplitState.java b/src/jdk/nashorn/internal/ir/SetSplitState.java
new file mode 100644
index 00000000..33ca3d15
--- /dev/null
+++ b/src/jdk/nashorn/internal/ir/SetSplitState.java
@@ -0,0 +1,70 @@
+/*
+ * 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.ir;
+
+import jdk.nashorn.internal.codegen.CompilerConstants;
+import jdk.nashorn.internal.ir.visitor.NodeVisitor;
+import jdk.nashorn.internal.runtime.Scope;
+
+/**
+ * Synthetic AST node that represents loading of the scope object and invocation of the {@link Scope#setSplitState(int)}
+ * method on it. It has no JavaScript source representation and only occurs in synthetic functions created by
+ * the split-into-functions transformation.
+ */
+public final class SetSplitState extends Statement {
+ private static final long serialVersionUID = 1L;
+
+ private final int state;
+
+ /**
+ * Creates a new split state setter
+ * @param state the state to set
+ * @param lineNumber the line number where it is inserted
+ */
+ public SetSplitState(final int state, final int lineNumber) {
+ super(lineNumber, NO_TOKEN, NO_FINISH);
+ this.state = state;
+ }
+
+ /**
+ * Returns the state this setter sets.
+ * @return the state this setter sets.
+ */
+ public int getState() {
+ return state;
+ }
+
+ @Override
+ public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
+ return visitor.enterSetSplitState(this) ? visitor.leaveSetSplitState(this) : this;
+ }
+
+ @Override
+ public void toString(final StringBuilder sb, final boolean printType) {
+ sb.append(CompilerConstants.SCOPE.symbolName()).append('.').append(Scope.SET_SPLIT_STATE.name())
+ .append('(').append(state).append(");");
+ }
+}
diff --git a/src/jdk/nashorn/internal/ir/SplitNode.java b/src/jdk/nashorn/internal/ir/SplitNode.java
index e7dd9ed2..43b511d6 100644
--- a/src/jdk/nashorn/internal/ir/SplitNode.java
+++ b/src/jdk/nashorn/internal/ir/SplitNode.java
@@ -25,13 +25,10 @@
package jdk.nashorn.internal.ir;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.io.IOException;
+import java.io.NotSerializableException;
+import java.io.ObjectOutputStream;
import jdk.nashorn.internal.codegen.CompileUnit;
-import jdk.nashorn.internal.codegen.Label;
import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
@@ -39,7 +36,9 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
* Node indicating code is split across classes.
*/
@Immutable
-public class SplitNode extends LexicalContextStatement implements Labels, CompileUnitHolder {
+public class SplitNode extends LexicalContextStatement implements CompileUnitHolder {
+ private static final long serialVersionUID = 1L;
+
/** Split node method name. */
private final String name;
@@ -49,8 +48,6 @@ public class SplitNode extends LexicalContextStatement implements Labels, Compil
/** Body of split code. */
private final Block body;
- private Map<Label, JoinPredecessor> jumps;
-
/**
* Constructor
*
@@ -65,19 +62,18 @@ public class SplitNode extends LexicalContextStatement implements Labels, Compil
this.compileUnit = compileUnit;
}
- private SplitNode(final SplitNode splitNode, final Block body, final CompileUnit compileUnit, final Map<Label, JoinPredecessor> jumps) {
+ private SplitNode(final SplitNode splitNode, final Block body, final CompileUnit compileUnit) {
super(splitNode);
this.name = splitNode.name;
this.body = body;
this.compileUnit = compileUnit;
- this.jumps = jumps;
}
/**
* Get the body for this split node - i.e. the actual code it encloses
* @return body for split node
*/
- public Node getBody() {
+ public Block getBody() {
return body;
}
@@ -85,7 +81,7 @@ public class SplitNode extends LexicalContextStatement implements Labels, Compil
if (this.body == body) {
return this;
}
- return Node.replaceInLexicalContext(lc, this, new SplitNode(this, body, compileUnit, jumps));
+ return Node.replaceInLexicalContext(lc, this, new SplitNode(this, body, compileUnit));
}
@Override
@@ -131,33 +127,12 @@ public class SplitNode extends LexicalContextStatement implements Labels, Compil
if (this.compileUnit == compileUnit) {
return this;
}
- return Node.replaceInLexicalContext(lc, this, new SplitNode(this, body, compileUnit, jumps));
- }
-
- /**
- * Adds a jump that crosses this split node's boundary (it originates within the split node, and goes to a target
- * outside of it).
- * @param jumpOrigin the join predecessor that's the origin of the jump
- * @param targetLabel the label that's the target of the jump.
- */
- public void addJump(final JoinPredecessor jumpOrigin, final Label targetLabel) {
- if (jumps == null) {
- jumps = new HashMap<>();
- }
- jumps.put(targetLabel, jumpOrigin);
- }
-
- /**
- * Returns the jump origin within this split node for a target.
- * @param targetLabel the target for which a jump origin is sought.
- * @return the jump origin, or null.
- */
- public JoinPredecessor getJumpOrigin(final Label targetLabel) {
- return jumps == null ? null : jumps.get(targetLabel);
+ return Node.replaceInLexicalContext(lc, this, new SplitNode(this, body, compileUnit));
}
- @Override
- public List<Label> getLabels() {
- return Collections.unmodifiableList(new ArrayList<>(jumps.keySet()));
+ private void writeObject(final ObjectOutputStream out) throws IOException {
+ // We are only serializing the AST after we run SplitIntoFunctions; no SplitNodes can remain for the
+ // serialization.
+ throw new NotSerializableException(getClass().getName());
}
}
diff --git a/src/jdk/nashorn/internal/ir/SplitReturn.java b/src/jdk/nashorn/internal/ir/SplitReturn.java
new file mode 100644
index 00000000..fb1b58c7
--- /dev/null
+++ b/src/jdk/nashorn/internal/ir/SplitReturn.java
@@ -0,0 +1,64 @@
+/*
+ * 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.ir;
+
+import jdk.nashorn.internal.ir.visitor.NodeVisitor;
+
+/**
+ * Synthetic AST node that represents return from a split fragment of a split function for control flow reasons (break
+ * or continue into a target outside the current fragment). It has no JavaScript source representation and only occurs
+ * in synthetic functions created by the split-into-functions transformation. It is different from a return node in
+ * that the return value is irrelevant, and doesn't affect the function's return type calculation.
+ */
+public final class SplitReturn extends Statement {
+ private static final long serialVersionUID = 1L;
+
+ /** The sole instance of this AST node. */
+ public static final SplitReturn INSTANCE = new SplitReturn();
+
+ private SplitReturn() {
+ super(NO_LINE_NUMBER, NO_TOKEN, NO_FINISH);
+ }
+
+ @Override
+ public boolean isTerminal() {
+ return true;
+ }
+
+ @Override
+ public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
+ return visitor.enterSplitReturn(this) ? visitor.leaveSplitReturn(this) : this;
+ }
+
+ @Override
+ public void toString(StringBuilder sb, boolean printType) {
+ sb.append(":splitreturn;");
+ }
+
+ private Object readResolve() {
+ return INSTANCE;
+ }
+}
diff --git a/src/jdk/nashorn/internal/ir/Statement.java b/src/jdk/nashorn/internal/ir/Statement.java
index b436f71f..f9a335c7 100644
--- a/src/jdk/nashorn/internal/ir/Statement.java
+++ b/src/jdk/nashorn/internal/ir/Statement.java
@@ -31,6 +31,7 @@ package jdk.nashorn.internal.ir;
* location information is the Statement
*/
public abstract class Statement extends Node implements Terminal {
+ private static final long serialVersionUID = 1L;
private final int lineNumber;
diff --git a/src/jdk/nashorn/internal/ir/SwitchNode.java b/src/jdk/nashorn/internal/ir/SwitchNode.java
index a3eac1ce..8936764c 100644
--- a/src/jdk/nashorn/internal/ir/SwitchNode.java
+++ b/src/jdk/nashorn/internal/ir/SwitchNode.java
@@ -37,6 +37,8 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
*/
@Immutable
public final class SwitchNode extends BreakableStatement {
+ private static final long serialVersionUID = 1L;
+
/** Switch expression. */
private final Expression expression;
diff --git a/src/jdk/nashorn/internal/ir/Symbol.java b/src/jdk/nashorn/internal/ir/Symbol.java
index 8d7acfbd..d4ccd459 100644
--- a/src/jdk/nashorn/internal/ir/Symbol.java
+++ b/src/jdk/nashorn/internal/ir/Symbol.java
@@ -97,7 +97,7 @@ public final class Symbol implements Comparable<Symbol> {
private int firstSlot = -1;
/** Field number in scope or property; array index in varargs when not using arguments object. */
- private int fieldIndex;
+ private int fieldIndex = -1;
/** Number of times this symbol is used in code */
private int useCount;
@@ -135,28 +135,15 @@ public final class Symbol implements Comparable<Symbol> {
*
* @param name name of symbol
* @param flags symbol flags
- * @param slot bytecode slot for this symbol
*/
- protected Symbol(final String name, final int flags, final int slot) {
+ public Symbol(final String name, final int flags) {
this.name = name;
this.flags = flags;
- this.firstSlot = slot;
- this.fieldIndex = -1;
if(shouldTrace()) {
trace("CREATE SYMBOL " + name);
}
}
- /**
- * Constructor
- *
- * @param name name of symbol
- * @param flags symbol flags
- */
- public Symbol(final String name, final int flags) {
- this(name, flags, -1);
- }
-
private static String align(final String string, final int max) {
final StringBuilder sb = new StringBuilder();
sb.append(string.substring(0, Math.min(string.length(), max)));
diff --git a/src/jdk/nashorn/internal/ir/TernaryNode.java b/src/jdk/nashorn/internal/ir/TernaryNode.java
index ed8016a9..913262d9 100644
--- a/src/jdk/nashorn/internal/ir/TernaryNode.java
+++ b/src/jdk/nashorn/internal/ir/TernaryNode.java
@@ -37,6 +37,8 @@ import jdk.nashorn.internal.parser.TokenType;
*/
@Immutable
public final class TernaryNode extends Expression {
+ private static final long serialVersionUID = 1L;
+
private final Expression test;
private final JoinPredecessorExpression trueExpr;
private final JoinPredecessorExpression falseExpr;
diff --git a/src/jdk/nashorn/internal/ir/ThrowNode.java b/src/jdk/nashorn/internal/ir/ThrowNode.java
index 5f5a1356..f422aea4 100644
--- a/src/jdk/nashorn/internal/ir/ThrowNode.java
+++ b/src/jdk/nashorn/internal/ir/ThrowNode.java
@@ -33,6 +33,8 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
*/
@Immutable
public final class ThrowNode extends Statement implements JoinPredecessor {
+ private static final long serialVersionUID = 1L;
+
/** Exception expression. */
private final Expression expression;
diff --git a/src/jdk/nashorn/internal/ir/TryNode.java b/src/jdk/nashorn/internal/ir/TryNode.java
index 87a8c565..605e0a9e 100644
--- a/src/jdk/nashorn/internal/ir/TryNode.java
+++ b/src/jdk/nashorn/internal/ir/TryNode.java
@@ -36,6 +36,8 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
*/
@Immutable
public final class TryNode extends Statement implements JoinPredecessor {
+ private static final long serialVersionUID = 1L;
+
/** Try statements. */
private final Block body;
diff --git a/src/jdk/nashorn/internal/ir/UnaryNode.java b/src/jdk/nashorn/internal/ir/UnaryNode.java
index aeac234f..2cee33b6 100644
--- a/src/jdk/nashorn/internal/ir/UnaryNode.java
+++ b/src/jdk/nashorn/internal/ir/UnaryNode.java
@@ -46,6 +46,8 @@ import jdk.nashorn.internal.parser.TokenType;
*/
@Immutable
public final class UnaryNode extends Expression implements Assignment<Expression>, Optimistic {
+ private static final long serialVersionUID = 1L;
+
/** Right hand side argument. */
private final Expression expression;
diff --git a/src/jdk/nashorn/internal/ir/VarNode.java b/src/jdk/nashorn/internal/ir/VarNode.java
index f9aef826..44d7d4c7 100644
--- a/src/jdk/nashorn/internal/ir/VarNode.java
+++ b/src/jdk/nashorn/internal/ir/VarNode.java
@@ -34,6 +34,8 @@ import jdk.nashorn.internal.parser.Token;
*/
@Immutable
public final class VarNode extends Statement implements Assignment<IdentNode> {
+ private static final long serialVersionUID = 1L;
+
/** Var name. */
private final IdentNode name;
@@ -272,4 +274,12 @@ public final class VarNode extends Statement implements Assignment<IdentNode> {
public boolean isFunctionDeclaration() {
return init instanceof FunctionNode && ((FunctionNode)init).isDeclared();
}
+
+ /**
+ * Returns true if this is an anonymous function declaration.
+ * @return true if this is an anonymous function declaration.
+ */
+ public boolean isAnonymousFunctionDeclaration() {
+ return isFunctionDeclaration() && ((FunctionNode)init).isAnonymous();
+ }
}
diff --git a/src/jdk/nashorn/internal/ir/WhileNode.java b/src/jdk/nashorn/internal/ir/WhileNode.java
index 0cced567..9a0981fb 100644
--- a/src/jdk/nashorn/internal/ir/WhileNode.java
+++ b/src/jdk/nashorn/internal/ir/WhileNode.java
@@ -34,6 +34,8 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
*/
@Immutable
public final class WhileNode extends LoopNode {
+ private static final long serialVersionUID = 1L;
+
/** is this a do while node ? */
private final boolean isDoWhile;
diff --git a/src/jdk/nashorn/internal/ir/WithNode.java b/src/jdk/nashorn/internal/ir/WithNode.java
index 0ea52c98..a9ef936a 100644
--- a/src/jdk/nashorn/internal/ir/WithNode.java
+++ b/src/jdk/nashorn/internal/ir/WithNode.java
@@ -33,6 +33,8 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
*/
@Immutable
public final class WithNode extends LexicalContextStatement {
+ private static final long serialVersionUID = 1L;
+
/** This expression. */
private final Expression expression;
diff --git a/src/jdk/nashorn/internal/ir/visitor/NodeVisitor.java b/src/jdk/nashorn/internal/ir/visitor/NodeVisitor.java
index 5bf760bb..f9fd3862 100644
--- a/src/jdk/nashorn/internal/ir/visitor/NodeVisitor.java
+++ b/src/jdk/nashorn/internal/ir/visitor/NodeVisitor.java
@@ -38,6 +38,7 @@ import jdk.nashorn.internal.ir.EmptyNode;
import jdk.nashorn.internal.ir.ExpressionStatement;
import jdk.nashorn.internal.ir.ForNode;
import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.ir.GetSplitState;
import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.IfNode;
import jdk.nashorn.internal.ir.IndexNode;
@@ -50,7 +51,9 @@ import jdk.nashorn.internal.ir.ObjectNode;
import jdk.nashorn.internal.ir.PropertyNode;
import jdk.nashorn.internal.ir.ReturnNode;
import jdk.nashorn.internal.ir.RuntimeNode;
+import jdk.nashorn.internal.ir.SetSplitState;
import jdk.nashorn.internal.ir.SplitNode;
+import jdk.nashorn.internal.ir.SplitReturn;
import jdk.nashorn.internal.ir.SwitchNode;
import jdk.nashorn.internal.ir.TernaryNode;
import jdk.nashorn.internal.ir.ThrowNode;
@@ -390,6 +393,26 @@ public abstract class NodeVisitor<T extends LexicalContext> {
}
/**
+ * Callback for entering a {@link GetSplitState}.
+ *
+ * @param getSplitState the get split state expression
+ * @return true if traversal should continue and node children be traversed, false otherwise
+ */
+ public boolean enterGetSplitState(final GetSplitState getSplitState) {
+ return enterDefault(getSplitState);
+ }
+
+ /**
+ * Callback for leaving a {@link GetSplitState}.
+ *
+ * @param getSplitState the get split state expression
+ * @return processed node, which will replace the original one, or the original node
+ */
+ public Node leaveGetSplitState(final GetSplitState getSplitState) {
+ return leaveDefault(getSplitState);
+ }
+
+ /**
* Callback for entering an IdentNode
*
* @param identNode the node
@@ -570,6 +593,26 @@ public abstract class NodeVisitor<T extends LexicalContext> {
}
/**
+ * Callback for entering a {@link SetSplitState}.
+ *
+ * @param setSplitState the set split state statement
+ * @return true if traversal should continue and node children be traversed, false otherwise
+ */
+ public boolean enterSetSplitState(final SetSplitState setSplitState) {
+ return enterDefault(setSplitState);
+ }
+
+ /**
+ * Callback for leaving a {@link SetSplitState}.
+ *
+ * @param setSplitState the set split state expression
+ * @return processed node, which will replace the original one, or the original node
+ */
+ public Node leaveSetSplitState(final SetSplitState setSplitState) {
+ return leaveDefault(setSplitState);
+ }
+
+ /**
* Callback for entering a SplitNode
*
* @param splitNode the node
@@ -590,6 +633,26 @@ public abstract class NodeVisitor<T extends LexicalContext> {
}
/**
+ * Callback for entering a SplitReturn
+ *
+ * @param splitReturn the node
+ * @return true if traversal should continue and node children be traversed, false otherwise
+ */
+ public boolean enterSplitReturn(final SplitReturn splitReturn) {
+ return enterDefault(splitReturn);
+ }
+
+ /**
+ * Callback for leaving a SplitReturn
+ *
+ * @param splitReturn the node
+ * @return processed node, which will replace the original one, or the original node
+ */
+ public Node leaveSplitReturn(final SplitReturn splitReturn) {
+ return leaveDefault(splitReturn);
+ }
+
+ /**
* Callback for entering a SwitchNode
*
* @param switchNode the node
diff --git a/src/jdk/nashorn/internal/objects/ArrayBufferView.java b/src/jdk/nashorn/internal/objects/ArrayBufferView.java
index efcc3ddc..e33fac3b 100644
--- a/src/jdk/nashorn/internal/objects/ArrayBufferView.java
+++ b/src/jdk/nashorn/internal/objects/ArrayBufferView.java
@@ -31,7 +31,6 @@ import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
-
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
@@ -46,7 +45,7 @@ import jdk.nashorn.internal.runtime.arrays.ArrayData;
import jdk.nashorn.internal.runtime.arrays.TypedArrayData;
@ScriptClass("ArrayBufferView")
-abstract class ArrayBufferView extends ScriptObject {
+public abstract class ArrayBufferView extends ScriptObject {
private final NativeArrayBuffer buffer;
private final int byteOffset;
diff --git a/src/jdk/nashorn/internal/objects/NativeFloat32Array.java b/src/jdk/nashorn/internal/objects/NativeFloat32Array.java
index 049d8fa8..e5ef0b5a 100644
--- a/src/jdk/nashorn/internal/objects/NativeFloat32Array.java
+++ b/src/jdk/nashorn/internal/objects/NativeFloat32Array.java
@@ -26,6 +26,7 @@
package jdk.nashorn.internal.objects;
import static jdk.nashorn.internal.codegen.CompilerConstants.specialCall;
+
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.nio.ByteBuffer;
@@ -141,6 +142,11 @@ public final class NativeFloat32Array extends ArrayBufferView {
}
@Override
+ public double getDoubleOptimistic(final int index, final int programPoint) {
+ return getElem(index);
+ }
+
+ @Override
public Object getObject(final int index) {
return getDouble(index);
}
diff --git a/src/jdk/nashorn/internal/objects/NativeFloat64Array.java b/src/jdk/nashorn/internal/objects/NativeFloat64Array.java
index 79268cc5..1ad61b27 100644
--- a/src/jdk/nashorn/internal/objects/NativeFloat64Array.java
+++ b/src/jdk/nashorn/internal/objects/NativeFloat64Array.java
@@ -26,6 +26,7 @@
package jdk.nashorn.internal.objects;
import static jdk.nashorn.internal.codegen.CompilerConstants.specialCall;
+
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.nio.ByteBuffer;
@@ -141,6 +142,11 @@ public final class NativeFloat64Array extends ArrayBufferView {
}
@Override
+ public double getDoubleOptimistic(final int index, final int programPoint) {
+ return getElem(index);
+ }
+
+ @Override
public Object getObject(final int index) {
return getDouble(index);
}
diff --git a/src/jdk/nashorn/internal/objects/NativeInt16Array.java b/src/jdk/nashorn/internal/objects/NativeInt16Array.java
index 72e24a20..06148848 100644
--- a/src/jdk/nashorn/internal/objects/NativeInt16Array.java
+++ b/src/jdk/nashorn/internal/objects/NativeInt16Array.java
@@ -26,6 +26,7 @@
package jdk.nashorn.internal.objects;
import static jdk.nashorn.internal.codegen.CompilerConstants.specialCall;
+
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.nio.ByteBuffer;
@@ -124,16 +125,31 @@ public final class NativeInt16Array extends ArrayBufferView {
}
@Override
+ public int getIntOptimistic(final int index, final int programPoint) {
+ return getElem(index);
+ }
+
+ @Override
public long getLong(final int index) {
return getInt(index);
}
@Override
+ public long getLongOptimistic(final int index, final int programPoint) {
+ return getElem(index);
+ }
+
+ @Override
public double getDouble(final int index) {
return getInt(index);
}
@Override
+ public double getDoubleOptimistic(final int index, final int programPoint) {
+ return getElem(index);
+ }
+
+ @Override
public Object getObject(final int index) {
return getInt(index);
}
diff --git a/src/jdk/nashorn/internal/objects/NativeInt32Array.java b/src/jdk/nashorn/internal/objects/NativeInt32Array.java
index 56b3106f..5074dc68 100644
--- a/src/jdk/nashorn/internal/objects/NativeInt32Array.java
+++ b/src/jdk/nashorn/internal/objects/NativeInt32Array.java
@@ -26,6 +26,7 @@
package jdk.nashorn.internal.objects;
import static jdk.nashorn.internal.codegen.CompilerConstants.specialCall;
+
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.nio.ByteBuffer;
@@ -122,16 +123,31 @@ public final class NativeInt32Array extends ArrayBufferView {
}
@Override
+ public int getIntOptimistic(final int index, final int programPoint) {
+ return getElem(index);
+ }
+
+ @Override
public long getLong(final int index) {
return getInt(index);
}
@Override
+ public long getLongOptimistic(final int index, final int programPoint) {
+ return getElem(index);
+ }
+
+ @Override
public double getDouble(final int index) {
return getInt(index);
}
@Override
+ public double getDoubleOptimistic(final int index, final int programPoint) {
+ return getElem(index);
+ }
+
+ @Override
public Object getObject(final int index) {
return getInt(index);
}
diff --git a/src/jdk/nashorn/internal/objects/NativeInt8Array.java b/src/jdk/nashorn/internal/objects/NativeInt8Array.java
index 68e6b0bc..319168c0 100644
--- a/src/jdk/nashorn/internal/objects/NativeInt8Array.java
+++ b/src/jdk/nashorn/internal/objects/NativeInt8Array.java
@@ -26,6 +26,7 @@
package jdk.nashorn.internal.objects;
import static jdk.nashorn.internal.codegen.CompilerConstants.specialCall;
+
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.nio.ByteBuffer;
@@ -122,16 +123,31 @@ public final class NativeInt8Array extends ArrayBufferView {
}
@Override
+ public int getIntOptimistic(final int index, final int programPoint) {
+ return getElem(index);
+ }
+
+ @Override
public long getLong(final int index) {
return getInt(index);
}
@Override
+ public long getLongOptimistic(final int index, final int programPoint) {
+ return getElem(index);
+ }
+
+ @Override
public double getDouble(final int index) {
return getInt(index);
}
@Override
+ public double getDoubleOptimistic(final int index, final int programPoint) {
+ return getElem(index);
+ }
+
+ @Override
public Object getObject(final int index) {
return getInt(index);
}
diff --git a/src/jdk/nashorn/internal/objects/NativeJava.java b/src/jdk/nashorn/internal/objects/NativeJava.java
index 4395e081..7c6e60a9 100644
--- a/src/jdk/nashorn/internal/objects/NativeJava.java
+++ b/src/jdk/nashorn/internal/objects/NativeJava.java
@@ -90,7 +90,11 @@ public final class NativeJava {
*/
@Function(name="synchronized", attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static Object synchronizedFunc(final Object self, final Object func, final Object obj) {
- return ScriptUtils.makeSynchronizedFunction(func, obj);
+ if (func instanceof ScriptFunction) {
+ return ((ScriptFunction)func).makeSynchronizedFunction(obj);
+ }
+
+ throw typeError("not.a.function", ScriptRuntime.safeToString(func));
}
/**
diff --git a/src/jdk/nashorn/internal/objects/NativeJavaImporter.java b/src/jdk/nashorn/internal/objects/NativeJavaImporter.java
index d1aa8cb1..498c263c 100644
--- a/src/jdk/nashorn/internal/objects/NativeJavaImporter.java
+++ b/src/jdk/nashorn/internal/objects/NativeJavaImporter.java
@@ -25,6 +25,7 @@
package jdk.nashorn.internal.objects;
+import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
import jdk.internal.dynalink.CallSiteDescriptor;
@@ -36,9 +37,11 @@ import jdk.nashorn.internal.objects.annotations.Constructor;
import jdk.nashorn.internal.objects.annotations.Function;
import jdk.nashorn.internal.objects.annotations.ScriptClass;
import jdk.nashorn.internal.runtime.Context;
+import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.NativeJavaPackage;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptObject;
+import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.UnwarrantedOptimismException;
/**
@@ -94,33 +97,30 @@ public final class NativeJavaImporter extends ScriptObject {
}
/**
- * "No such property" call placeholder.
- *
- * This can never be called as we override {@link ScriptObject#noSuchProperty}. We do declare it here as it's a signal
- * to {@link jdk.nashorn.internal.runtime.WithObject} that it's worth trying doing a {@code noSuchProperty} on this object.
+ * "No such property" handler.
*
* @param self self reference
* @param name property name
- * @return never returns
+ * @return value of the missing property
*/
@Function(attributes = Attribute.NOT_ENUMERABLE)
public static Object __noSuchProperty__(final Object self, final Object name) {
- throw new AssertionError("__noSuchProperty__ placeholder called");
+ if (! (self instanceof NativeJavaImporter)) {
+ throw typeError("not.a.java.importer", ScriptRuntime.safeToString(self));
+ }
+ return ((NativeJavaImporter)self).createProperty(JSType.toString(name));
}
/**
- * "No such method call" placeholder
- *
- * This can never be called as we override {@link ScriptObject#noSuchMethod}. We do declare it here as it's a signal
- * to {@link jdk.nashorn.internal.runtime.WithObject} that it's worth trying doing a noSuchProperty on this object.
+ * "No such method call" handler
*
* @param self self reference
* @param args arguments to method
- * @return never returns
+ * @return never returns always throw TypeError
*/
@Function(attributes = Attribute.NOT_ENUMERABLE)
public static Object __noSuchMethod__(final Object self, final Object... args) {
- throw new AssertionError("__noSuchMethod__ placeholder called");
+ throw typeError("not.a.function", ScriptRuntime.safeToString(args[0]));
}
@Override
diff --git a/src/jdk/nashorn/internal/objects/NativeUint16Array.java b/src/jdk/nashorn/internal/objects/NativeUint16Array.java
index d049c933..7f8a1923 100644
--- a/src/jdk/nashorn/internal/objects/NativeUint16Array.java
+++ b/src/jdk/nashorn/internal/objects/NativeUint16Array.java
@@ -26,6 +26,7 @@
package jdk.nashorn.internal.objects;
import static jdk.nashorn.internal.codegen.CompilerConstants.specialCall;
+
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.nio.ByteBuffer;
@@ -128,16 +129,31 @@ public final class NativeUint16Array extends ArrayBufferView {
}
@Override
+ public int getIntOptimistic(final int index, final int programPoint) {
+ return getElem(index);
+ }
+
+ @Override
public long getLong(final int index) {
return getInt(index);
}
@Override
+ public long getLongOptimistic(final int index, final int programPoint) {
+ return getElem(index);
+ }
+
+ @Override
public double getDouble(final int index) {
return getInt(index);
}
@Override
+ public double getDoubleOptimistic(final int index, final int programPoint) {
+ return getElem(index);
+ }
+
+ @Override
public Object getObject(final int index) {
return getInt(index);
}
diff --git a/src/jdk/nashorn/internal/objects/NativeUint32Array.java b/src/jdk/nashorn/internal/objects/NativeUint32Array.java
index 2ae2be05..4772b977 100644
--- a/src/jdk/nashorn/internal/objects/NativeUint32Array.java
+++ b/src/jdk/nashorn/internal/objects/NativeUint32Array.java
@@ -26,6 +26,7 @@
package jdk.nashorn.internal.objects;
import static jdk.nashorn.internal.codegen.CompilerConstants.specialCall;
+
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.nio.ByteBuffer;
@@ -128,7 +129,7 @@ public final class NativeUint32Array extends ArrayBufferView {
@Override
public Class<?> getElementType() {
- return int.class;
+ return long.class;
}
@Override
@@ -142,11 +143,21 @@ public final class NativeUint32Array extends ArrayBufferView {
}
@Override
+ public long getLongOptimistic(final int index, final int programPoint) {
+ return getElem(index);
+ }
+
+ @Override
public double getDouble(final int index) {
return getLong(index);
}
@Override
+ public double getDoubleOptimistic(final int index, final int programPoint) {
+ return getLong(index);
+ }
+
+ @Override
public Object getObject(final int index) {
return getLong(index);
}
diff --git a/src/jdk/nashorn/internal/objects/NativeUint8Array.java b/src/jdk/nashorn/internal/objects/NativeUint8Array.java
index 1067baa5..be7eb368 100644
--- a/src/jdk/nashorn/internal/objects/NativeUint8Array.java
+++ b/src/jdk/nashorn/internal/objects/NativeUint8Array.java
@@ -26,6 +26,7 @@
package jdk.nashorn.internal.objects;
import static jdk.nashorn.internal.codegen.CompilerConstants.specialCall;
+
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.nio.ByteBuffer;
@@ -128,16 +129,31 @@ public final class NativeUint8Array extends ArrayBufferView {
}
@Override
+ public int getIntOptimistic(final int index, final int programPoint) {
+ return getElem(index);
+ }
+
+ @Override
public long getLong(final int index) {
return getInt(index);
}
@Override
+ public long getLongOptimistic(final int index, final int programPoint) {
+ return getElem(index);
+ }
+
+ @Override
public double getDouble(final int index) {
return getInt(index);
}
@Override
+ public double getDoubleOptimistic(final int index, final int programPoint) {
+ return getElem(index);
+ }
+
+ @Override
public Object getObject(final int index) {
return getInt(index);
}
diff --git a/src/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java b/src/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java
index ffd0f14c..125ac0a2 100644
--- a/src/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java
+++ b/src/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java
@@ -28,6 +28,7 @@ package jdk.nashorn.internal.objects;
import static jdk.nashorn.internal.codegen.CompilerConstants.specialCall;
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
import static jdk.nashorn.internal.lookup.Lookup.MH;
+
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.nio.ByteBuffer;
@@ -158,16 +159,31 @@ public final class NativeUint8ClampedArray extends ArrayBufferView {
}
@Override
+ public int getIntOptimistic(final int index, final int programPoint) {
+ return getElem(index);
+ }
+
+ @Override
public long getLong(final int index) {
return getInt(index);
}
@Override
+ public long getLongOptimistic(final int index, final int programPoint) {
+ return getElem(index);
+ }
+
+ @Override
public double getDouble(final int index) {
return getInt(index);
}
@Override
+ public double getDoubleOptimistic(final int index, final int programPoint) {
+ return getElem(index);
+ }
+
+ @Override
public Object getObject(final int index) {
return getInt(index);
}
diff --git a/src/jdk/nashorn/internal/parser/Lexer.java b/src/jdk/nashorn/internal/parser/Lexer.java
index 4bfeeaf2..04778cdb 100644
--- a/src/jdk/nashorn/internal/parser/Lexer.java
+++ b/src/jdk/nashorn/internal/parser/Lexer.java
@@ -46,6 +46,7 @@ import static jdk.nashorn.internal.parser.TokenType.RPAREN;
import static jdk.nashorn.internal.parser.TokenType.STRING;
import static jdk.nashorn.internal.parser.TokenType.XML;
+import java.io.Serializable;
import jdk.nashorn.internal.runtime.ECMAErrors;
import jdk.nashorn.internal.runtime.ErrorManager;
import jdk.nashorn.internal.runtime.JSErrorType;
@@ -1717,7 +1718,9 @@ public class Lexer extends Scanner {
* Helper class for Lexer tokens, e.g XML or RegExp tokens.
* This is the abstract superclass
*/
- public static abstract class LexerToken {
+ public static abstract class LexerToken implements Serializable {
+ private static final long serialVersionUID = 1L;
+
private final String expression;
/**
@@ -1741,6 +1744,8 @@ public class Lexer extends Scanner {
* Temporary container for regular expressions.
*/
public static class RegexToken extends LexerToken {
+ private static final long serialVersionUID = 1L;
+
/** Options. */
private final String options;
@@ -1773,6 +1778,7 @@ public class Lexer extends Scanner {
* Temporary container for XML expression.
*/
public static class XMLToken extends LexerToken {
+ private static final long serialVersionUID = 1L;
/**
* Constructor.
diff --git a/src/jdk/nashorn/internal/runtime/AstDeserializer.java b/src/jdk/nashorn/internal/runtime/AstDeserializer.java
new file mode 100644
index 00000000..8f276614
--- /dev/null
+++ b/src/jdk/nashorn/internal/runtime/AstDeserializer.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.nashorn.internal.runtime;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.util.zip.InflaterInputStream;
+import jdk.nashorn.internal.ir.FunctionNode;
+
+/**
+ * This static utility class performs deserialization of FunctionNode ASTs from a byte array.
+ * The format is a standard Java serialization stream, deflated.
+ */
+final class AstDeserializer {
+ static FunctionNode deserialize(final byte[] serializedAst) {
+ try {
+ return (FunctionNode)new ObjectInputStream(new InflaterInputStream(new ByteArrayInputStream(
+ serializedAst))).readObject();
+ } catch (final ClassNotFoundException | IOException e) {
+ // This is internal, can't happen
+ throw new AssertionError("Unexpected exception deserializing function", e);
+ }
+ }
+}
diff --git a/src/jdk/nashorn/internal/runtime/CompiledFunction.java b/src/jdk/nashorn/internal/runtime/CompiledFunction.java
index 2655e781..b37f94fa 100644
--- a/src/jdk/nashorn/internal/runtime/CompiledFunction.java
+++ b/src/jdk/nashorn/internal/runtime/CompiledFunction.java
@@ -27,6 +27,7 @@ package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
+
import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
@@ -786,6 +787,7 @@ final class CompiledFunction {
// isn't available, we'll use the old one bound into the call site.
final OptimismInfo effectiveOptInfo = currentOptInfo != null ? currentOptInfo : oldOptInfo;
FunctionNode fn = effectiveOptInfo.reparse();
+ final boolean serialized = effectiveOptInfo.isSerialized();
final Compiler compiler = effectiveOptInfo.getCompiler(fn, callSiteType, re); //set to non rest-of
if (!shouldRecompile) {
@@ -793,17 +795,17 @@ final class CompiledFunction {
// recompiled a deoptimized version for an inner invocation.
// We still need to do the rest of from the beginning
logRecompile("Rest-of compilation [STANDALONE] ", fn, callSiteType, effectiveOptInfo.invalidatedProgramPoints);
- return restOfHandle(effectiveOptInfo, compiler.compile(fn, CompilationPhases.COMPILE_ALL_RESTOF), currentOptInfo != null);
+ return restOfHandle(effectiveOptInfo, compiler.compile(fn, serialized ? CompilationPhases.COMPILE_SERIALIZED_RESTOF : CompilationPhases.COMPILE_ALL_RESTOF), currentOptInfo != null);
}
logRecompile("Deoptimizing recompilation (up to bytecode) ", fn, callSiteType, effectiveOptInfo.invalidatedProgramPoints);
- fn = compiler.compile(fn, CompilationPhases.COMPILE_UPTO_BYTECODE);
+ fn = compiler.compile(fn, serialized ? CompilationPhases.RECOMPILE_SERIALIZED_UPTO_BYTECODE : CompilationPhases.COMPILE_UPTO_BYTECODE);
log.info("Reusable IR generated");
// compile the rest of the function, and install it
log.info("Generating and installing bytecode from reusable IR...");
logRecompile("Rest-of compilation [CODE PIPELINE REUSE] ", fn, callSiteType, effectiveOptInfo.invalidatedProgramPoints);
- final FunctionNode normalFn = compiler.compile(fn, CompilationPhases.COMPILE_FROM_BYTECODE);
+ final FunctionNode normalFn = compiler.compile(fn, CompilationPhases.GENERATE_BYTECODE_AND_INSTALL);
if (effectiveOptInfo.data.usePersistentCodeCache()) {
final RecompilableScriptFunctionData data = effectiveOptInfo.data;
@@ -829,7 +831,7 @@ final class CompiledFunction {
constructor = null; // Will be regenerated when needed
log.info("Done: ", invoker);
- final MethodHandle restOf = restOfHandle(effectiveOptInfo, compiler.compile(fn, CompilationPhases.COMPILE_FROM_BYTECODE_RESTOF), canBeDeoptimized);
+ final MethodHandle restOf = restOfHandle(effectiveOptInfo, compiler.compile(fn, CompilationPhases.GENERATE_BYTECODE_AND_INSTALL_RESTOF), canBeDeoptimized);
// Note that we only adjust the switch point after we set the invoker/constructor. This is important.
if (canBeDeoptimized) {
@@ -921,6 +923,10 @@ final class CompiledFunction {
FunctionNode reparse() {
return data.reparse();
}
+
+ boolean isSerialized() {
+ return data.isSerialized();
+ }
}
@SuppressWarnings("unused")
diff --git a/src/jdk/nashorn/internal/runtime/Context.java b/src/jdk/nashorn/internal/runtime/Context.java
index e77c204a..23f0e3ab 100644
--- a/src/jdk/nashorn/internal/runtime/Context.java
+++ b/src/jdk/nashorn/internal/runtime/Context.java
@@ -150,6 +150,13 @@ public final class Context {
private final Context context;
private final ScriptLoader loader;
private final CodeSource codeSource;
+ private int usageCount = 0;
+ private int bytesDefined = 0;
+
+ // We reuse this installer for 10 compilations or 200000 defined bytes. Usually the first condition
+ // will occur much earlier, the second is a safety measure for very large scripts/functions.
+ private final static int MAX_USAGES = 10;
+ private final static int MAX_BYTES_DEFINED = 200_000;
private ContextCodeInstaller(final Context context, final ScriptLoader loader, final CodeSource codeSource) {
this.context = context;
@@ -168,6 +175,8 @@ public final class Context {
@Override
public Class<?> install(final String className, final byte[] bytecode) {
+ usageCount++;
+ bytesDefined += bytecode.length;
final String binaryName = Compiler.binaryName(className);
return loader.installClass(binaryName, bytecode, codeSource);
}
@@ -225,6 +234,10 @@ public final class Context {
@Override
public CodeInstaller<ScriptEnvironment> withNewLoader() {
+ // Reuse this installer if we're within our limits.
+ if (usageCount < MAX_USAGES && bytesDefined < MAX_BYTES_DEFINED) {
+ return this;
+ }
return new ContextCodeInstaller(context, context.createNewLoader(), codeSource);
}
diff --git a/src/jdk/nashorn/internal/runtime/GlobalConstants.java b/src/jdk/nashorn/internal/runtime/GlobalConstants.java
index 6099a70b..c6f9b964 100644
--- a/src/jdk/nashorn/internal/runtime/GlobalConstants.java
+++ b/src/jdk/nashorn/internal/runtime/GlobalConstants.java
@@ -31,7 +31,6 @@ import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.getProgramPoint;
import static jdk.nashorn.internal.runtime.logging.DebugLogger.quote;
-
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.SwitchPoint;
@@ -328,7 +327,9 @@ public final class GlobalConstants implements Loggable {
}
if (!acc.mayRetry()) {
- log.info("*** SET: Giving up on " + quote(name) + " - retry count has exceeded " + DynamicLinker.getLinkedCallSiteLocation());
+ if (log.isEnabled()) {
+ log.fine("*** SET: Giving up on " + quote(name) + " - retry count has exceeded " + DynamicLinker.getLinkedCallSiteLocation());
+ }
return null;
}
@@ -404,7 +405,9 @@ public final class GlobalConstants implements Loggable {
}
if (acc.hasBeenInvalidated() || acc.guardFailed()) {
- log.fine("*** GET: Giving up on " + quote(name) + " - retry count has exceeded");
+ if (log.isEnabled()) {
+ log.info("*** GET: Giving up on " + quote(name) + " - retry count has exceeded " + DynamicLinker.getLinkedCallSiteLocation());
+ }
return null;
}
diff --git a/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java b/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java
index f3ad2339..6b2c7620 100644
--- a/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java
+++ b/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java
@@ -43,6 +43,7 @@ import jdk.nashorn.internal.codegen.Compiler;
import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
import jdk.nashorn.internal.codegen.CompilerConstants;
import jdk.nashorn.internal.codegen.FunctionSignature;
+import jdk.nashorn.internal.codegen.Namespace;
import jdk.nashorn.internal.codegen.ObjectClassGenerator.AllocatorDescriptor;
import jdk.nashorn.internal.codegen.OptimisticTypesPersistence;
import jdk.nashorn.internal.codegen.TypeMap;
@@ -79,6 +80,9 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
/** Source from which FunctionNode was parsed. */
private transient Source source;
+ /** Serialized, compressed form of the AST. Used by split functions as they can't be reparsed from source. */
+ private final byte[] serializedAst;
+
/** Token of this function within the source. */
private final long token;
@@ -127,6 +131,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
* @param nestedFunctions nested function map
* @param externalScopeDepths external scope depths
* @param internalSymbols internal symbols to method, defined in its scope
+ * @param serializedAst a serialized AST representation. Normally only used for split functions.
*/
public RecompilableScriptFunctionData(
final FunctionNode functionNode,
@@ -134,7 +139,8 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
final AllocatorDescriptor allocationDescriptor,
final Map<Integer, RecompilableScriptFunctionData> nestedFunctions,
final Map<String, Integer> externalScopeDepths,
- final Set<String> internalSymbols) {
+ final Set<String> internalSymbols,
+ final byte[] serializedAst) {
super(functionName(functionNode),
Math.min(functionNode.getParameters().size(), MAX_ARITY),
@@ -158,6 +164,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
nfn.setParent(this);
}
+ this.serializedAst = serializedAst;
createLogger();
}
@@ -212,10 +219,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
*/
public int getExternalSymbolDepth(final String symbolName) {
final Integer depth = externalScopeDepths.get(symbolName);
- if (depth == null) {
- return -1;
- }
- return depth;
+ return depth == null ? -1 : depth;
}
/**
@@ -354,8 +358,15 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
return allocationStrategy.allocate(map);
}
+ boolean isSerialized() {
+ return serializedAst != null;
+ }
+
FunctionNode reparse() {
- // NOTE: If we aren't recompiling the top-level program, we decrease functionNodeId 'cause we'll have a synthetic program node
+ if (isSerialized()) {
+ return deserialize();
+ }
+
final int descPosition = Token.descPosition(token);
final Context context = Context.getContextTrusted();
final Parser parser = new Parser(
@@ -363,8 +374,10 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
source,
new Context.ThrowErrorManager(),
isStrict(),
+ // source starts at line 0, so even though lineNumber is the correct declaration line, back off
+ // one to make it exclusive
lineNumber - 1,
- context.getLogger(Parser.class)); // source starts at line 0, so even though lineNumber is the correct declaration line, back off one to make it exclusive
+ context.getLogger(Parser.class));
if (getFunctionFlag(FunctionNode.IS_ANONYMOUS)) {
parser.setFunctionName(functionName);
@@ -378,6 +391,17 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
return (isProgram() ? program : extractFunctionFromScript(program)).setName(null, functionName);
}
+ private FunctionNode deserialize() {
+ final ScriptEnvironment env = installer.getOwner();
+ final Timing timing = env._timing;
+ final long t1 = System.nanoTime();
+ try {
+ return AstDeserializer.deserialize(serializedAst).initializeDeserialized(source, new Namespace(env.getNamespace()));
+ } finally {
+ timing.accumulateTime("'Deserialize'", System.nanoTime() - t1);
+ }
+ }
+
private boolean getFunctionFlag(final int flag) {
return (functionFlags & flag) != 0;
}
@@ -486,7 +510,8 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
final FunctionNode fn = reparse();
final Compiler compiler = getCompiler(fn, actualCallSiteType, runtimeScope);
- final FunctionNode compiledFn = compiler.compile(fn, CompilationPhases.COMPILE_ALL);
+ final FunctionNode compiledFn = compiler.compile(fn,
+ isSerialized() ? CompilationPhases.COMPILE_ALL_SERIALIZED : CompilationPhases.COMPILE_ALL);
if (persist && !compiledFn.getFlag(FunctionNode.HAS_APPLY_TO_CALL_SPECIALIZATION)) {
compiler.persistClassInfo(cacheKey, compiledFn);
@@ -606,7 +631,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
MethodHandle lookupCodeMethod(final Class<?> codeClass, final MethodType targetType) {
if (log.isEnabled()) {
- log.info("Looking up ", DebugLogger.quote(name), " type=", targetType);
+ log.info("Looking up ", DebugLogger.quote(functionName), " type=", targetType);
}
return MH.findStatic(LOOKUP, codeClass, functionName, targetType);
}
@@ -676,6 +701,22 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
return addCode(lookup(fnInit).asType(toType), fnInit.getInvalidatedProgramPoints(), callSiteType, fnInit.getFlags());
}
+ /**
+ * Returns the return type of a function specialization for particular parameter types.<br>
+ * <b>Be aware that the way this is implemented, it forces full materialization (compilation and installation) of
+ * code for that specialization.</b>
+ * @param callSiteType the parameter types at the call site. It must include the mandatory {@code callee} and
+ * {@code this} parameters, so it needs to start with at least {@code ScriptFunction.class} and
+ * {@code Object.class} class. Since the return type of the function is calculated from the code itself, it is
+ * irrelevant and should be set to {@code Object.class}.
+ * @param runtimeScope a current runtime scope. Can be null but when it's present it will be used as a source of
+ * current runtime values that can improve the compiler's type speculations (and thus reduce the need for later
+ * recompilations) if the specialization is not already present and thus needs to be freshly compiled.
+ * @return the return type of the function specialization.
+ */
+ public Class<?> getReturnType(final MethodType callSiteType, final ScriptObject runtimeScope) {
+ return getBest(callSiteType, runtimeScope, CompiledFunction.NO_FUNCTIONS).type().returnType();
+ }
@Override
synchronized CompiledFunction getBest(final MethodType callSiteType, final ScriptObject runtimeScope, final Collection<CompiledFunction> forbidden) {
@@ -801,6 +842,26 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
return true;
}
+ /**
+ * Restores the {@link #getFunctionFlags()} flags to a function node. During on-demand compilation, we might need
+ * to restore flags to a function node that was otherwise not subjected to a full compile pipeline (e.g. its parse
+ * was skipped, or it's a nested function of a deserialized function.
+ * @param lc current lexical context
+ * @param fn the function node to restore flags onto
+ * @return the transformed function node
+ */
+ public FunctionNode restoreFlags(final LexicalContext lc, final FunctionNode fn) {
+ assert fn.getId() == functionNodeId;
+ FunctionNode newFn = fn.setFlags(lc, functionFlags);
+ // 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 (newFn.hasNestedEval()) {
+ assert newFn.hasScopeBlock();
+ newFn = newFn.setBody(lc, newFn.getBody().setNeedsScope(null));
+ }
+ return newFn;
+ }
+
private void readObject(final java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
createLogger();
diff --git a/src/jdk/nashorn/internal/runtime/WithObject.java b/src/jdk/nashorn/internal/runtime/WithObject.java
index 54ec06f5..20510dfd 100644
--- a/src/jdk/nashorn/internal/runtime/WithObject.java
+++ b/src/jdk/nashorn/internal/runtime/WithObject.java
@@ -210,6 +210,19 @@ public final class WithObject extends ScriptObject implements Scope {
}
@Override
+ protected Object invokeNoSuchProperty(final String name, final int programPoint) {
+ FindProperty find = expression.findProperty(NO_SUCH_PROPERTY_NAME, true);
+ if (find != null) {
+ final Object func = find.getObjectValue();
+ if (func instanceof ScriptFunction) {
+ return ScriptRuntime.apply((ScriptFunction)func, expression, name);
+ }
+ }
+
+ return getProto().invokeNoSuchProperty(name, programPoint);
+ }
+
+ @Override
public void setSplitState(final int state) {
getNonWithParent().setSplitState(state);
}
diff --git a/src/jdk/nashorn/internal/runtime/arrays/ContinuousArrayData.java b/src/jdk/nashorn/internal/runtime/arrays/ContinuousArrayData.java
index dfa6133d..8724d64a 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/ContinuousArrayData.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/ContinuousArrayData.java
@@ -30,6 +30,7 @@ import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.JSType.getAccessorTypeIndex;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
+
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
@@ -37,6 +38,7 @@ import java.lang.invoke.SwitchPoint;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
+import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.lookup.Lookup;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
@@ -120,6 +122,11 @@ public abstract class ContinuousArrayData extends ArrayData {
*/
public abstract Class<?> getElementType();
+ @Override
+ public Type getOptimisticType() {
+ return Type.typeFor(getElementType());
+ }
+
/**
* Look up a continuous array element getter
* @param get getter, sometimes combined with a has check that throws CCE on failure for relink
diff --git a/src/jdk/nashorn/internal/runtime/arrays/DeletedRangeArrayFilter.java b/src/jdk/nashorn/internal/runtime/arrays/DeletedRangeArrayFilter.java
index 8732add9..cd5cadb9 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/DeletedRangeArrayFilter.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/DeletedRangeArrayFilter.java
@@ -66,9 +66,9 @@ final class DeletedRangeArrayFilter extends ArrayFilter {
public Object[] asObjectArray() {
final Object[] value = super.asObjectArray();
- if (lo <= Integer.MAX_VALUE) {
- final int intHi = (int)Math.min(hi, Integer.MAX_VALUE);
- for (int i = (int)lo; i <= intHi; i++) {
+ if (lo < Integer.MAX_VALUE) {
+ final int end = (int)Math.min(hi + 1, Integer.MAX_VALUE);
+ for (int i = (int)lo; i < end; i++) {
value[i] = ScriptRuntime.UNDEFINED;
}
}
@@ -81,9 +81,9 @@ final class DeletedRangeArrayFilter extends ArrayFilter {
final Object value = super.asArrayOfType(componentType);
final Object undefValue = convertUndefinedValue(componentType);
- if (lo <= Integer.MAX_VALUE) {
- final int intHi = (int)Math.min(hi, Integer.MAX_VALUE);
- for (int i = (int)lo; i <= intHi; i++) {
+ if (lo < Integer.MAX_VALUE) {
+ final int end = (int)Math.min(hi + 1, Integer.MAX_VALUE);
+ for (int i = (int)lo; i < end; i++) {
Array.set(value, i, undefValue);
}
}
diff --git a/src/jdk/nashorn/internal/runtime/arrays/IntArrayData.java b/src/jdk/nashorn/internal/runtime/arrays/IntArrayData.java
index 48dd088e..0792b6b6 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/IntArrayData.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/IntArrayData.java
@@ -26,10 +26,10 @@
package jdk.nashorn.internal.runtime.arrays;
import static jdk.nashorn.internal.codegen.CompilerConstants.specialCall;
+
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.util.Arrays;
-import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.ScriptRuntime;
@@ -73,7 +73,7 @@ final class IntArrayData extends ContinuousArrayData implements IntElements {
@Override
public Object[] asObjectArray() {
- return toObjectArray();
+ return toObjectArray(true);
}
@SuppressWarnings("unused")
@@ -116,9 +116,9 @@ final class IntArrayData extends ContinuousArrayData implements IntElements {
return super.asArrayOfType(componentType);
}
- private Object[] toObjectArray() {
+ private Object[] toObjectArray(final boolean trim) {
assert length <= array.length : "length exceeds internal array size";
- final Object[] oarray = new Object[array.length];
+ final Object[] oarray = new Object[trim ? (int)length : array.length];
for (int index = 0; index < length; index++) {
oarray[index] = Integer.valueOf(array[index]);
@@ -158,7 +158,7 @@ final class IntArrayData extends ContinuousArrayData implements IntElements {
}
private ObjectArrayData convertToObject() {
- return new ObjectArrayData(toObjectArray(), (int)length);
+ return new ObjectArrayData(toObjectArray(false), (int)length);
}
@Override
@@ -257,11 +257,6 @@ final class IntArrayData extends ContinuousArrayData implements IntElements {
}
@Override
- public Type getOptimisticType() {
- return Type.INT;
- }
-
- @Override
public int getInt(final int index) {
return array[index];
}
diff --git a/src/jdk/nashorn/internal/runtime/arrays/LongArrayData.java b/src/jdk/nashorn/internal/runtime/arrays/LongArrayData.java
index ad050d69..f41ee15a 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/LongArrayData.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/LongArrayData.java
@@ -27,10 +27,10 @@ package jdk.nashorn.internal.runtime.arrays;
import static jdk.nashorn.internal.codegen.CompilerConstants.specialCall;
import static jdk.nashorn.internal.lookup.Lookup.MH;
+
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.util.Arrays;
-import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.ScriptRuntime;
@@ -67,12 +67,12 @@ final class LongArrayData extends ContinuousArrayData implements IntOrLongElemen
@Override
public Object[] asObjectArray() {
- return toObjectArray(array, (int)length);
+ return toObjectArray(true);
}
- private static Object[] toObjectArray(final long[] array, final int length) {
+ private Object[] toObjectArray(final boolean trim) {
assert length <= array.length : "length exceeds internal array size";
- final Object[] oarray = new Object[array.length];
+ final Object[] oarray = new Object[trim ? (int)length : array.length];
for (int index = 0; index < length; index++) {
oarray[index] = Long.valueOf(array[index]);
@@ -89,7 +89,7 @@ final class LongArrayData extends ContinuousArrayData implements IntOrLongElemen
return super.asArrayOfType(componentType);
}
- private static double[] toDoubleArray(final long[] array, final int length) {
+ private double[] toDoubleArray() {
assert length <= array.length : "length exceeds internal array size";
final double[] darray = new double[array.length];
@@ -107,9 +107,9 @@ final class LongArrayData extends ContinuousArrayData implements IntOrLongElemen
}
final int len = (int)length;
if (type == Double.class) {
- return new NumberArrayData(LongArrayData.toDoubleArray(array, len), len);
+ return new NumberArrayData(toDoubleArray(), len);
}
- return new ObjectArrayData(LongArrayData.toObjectArray(array, len), len);
+ return new ObjectArrayData(toObjectArray(false), len);
}
@Override
@@ -186,11 +186,6 @@ final class LongArrayData extends ContinuousArrayData implements IntOrLongElemen
return convert(Double.class).set(index, value, strict);
}
- @Override
- public Type getOptimisticType() {
- return Type.LONG;
- }
-
private static final MethodHandle HAS_GET_ELEM = specialCall(MethodHandles.lookup(), LongArrayData.class, "getElem", long.class, int.class).methodHandle();
private static final MethodHandle SET_ELEM = specialCall(MethodHandles.lookup(), LongArrayData.class, "setElem", void.class, int.class, long.class).methodHandle();
diff --git a/src/jdk/nashorn/internal/runtime/arrays/NumberArrayData.java b/src/jdk/nashorn/internal/runtime/arrays/NumberArrayData.java
index b2d843e6..2c57208f 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/NumberArrayData.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/NumberArrayData.java
@@ -28,10 +28,10 @@ package jdk.nashorn.internal.runtime.arrays;
import static jdk.nashorn.internal.codegen.CompilerConstants.specialCall;
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 java.util.Arrays;
-import jdk.nashorn.internal.codegen.types.Type;
/**
* Implementation of {@link ArrayData} as soon as a double has been
@@ -66,12 +66,12 @@ final class NumberArrayData extends ContinuousArrayData implements NumericElemen
@Override
public Object[] asObjectArray() {
- return toObjectArray(array, (int)length);
+ return toObjectArray(true);
}
- private static Object[] toObjectArray(final double[] array, final int length) {
+ private Object[] toObjectArray(final boolean trim) {
assert length <= array.length : "length exceeds internal array size";
- final Object[] oarray = new Object[array.length];
+ final Object[] oarray = new Object[trim ? (int)length : array.length];
for (int index = 0; index < length; index++) {
oarray[index] = Double.valueOf(array[index]);
@@ -91,7 +91,7 @@ final class NumberArrayData extends ContinuousArrayData implements NumericElemen
public ArrayData convert(final Class<?> type) {
if (type != Double.class && type != Integer.class && type != Long.class) {
final int len = (int)length;
- return new ObjectArrayData(NumberArrayData.toObjectArray(array, len), len);
+ return new ObjectArrayData(toObjectArray(false), len);
}
return this;
}
@@ -166,11 +166,6 @@ final class NumberArrayData extends ContinuousArrayData implements NumericElemen
return this;
}
- @Override
- public Type getOptimisticType() {
- return Type.NUMBER;
- }
-
private static final MethodHandle HAS_GET_ELEM = specialCall(MethodHandles.lookup(), NumberArrayData.class, "getElem", double.class, int.class).methodHandle();
private static final MethodHandle SET_ELEM = specialCall(MethodHandles.lookup(), NumberArrayData.class, "setElem", void.class, int.class, double.class).methodHandle();
diff --git a/src/jdk/nashorn/internal/runtime/arrays/ObjectArrayData.java b/src/jdk/nashorn/internal/runtime/arrays/ObjectArrayData.java
index 8fd1a453..379ba6e4 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/ObjectArrayData.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/ObjectArrayData.java
@@ -26,10 +26,10 @@
package jdk.nashorn.internal.runtime.arrays;
import static jdk.nashorn.internal.codegen.CompilerConstants.specialCall;
+
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.util.Arrays;
-import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.ScriptRuntime;
@@ -155,15 +155,11 @@ final class ObjectArrayData extends ContinuousArrayData {
@Override
public ArrayData setEmpty(final long lo, final long hi) {
- Arrays.fill(array, (int)Math.max(lo, 0L), (int)Math.min(hi, Integer.MAX_VALUE), ScriptRuntime.EMPTY);
+ // hi parameter is inclusive, but Arrays.fill toIndex parameter is exclusive
+ Arrays.fill(array, (int)Math.max(lo, 0L), (int)Math.min(hi + 1, Integer.MAX_VALUE), ScriptRuntime.EMPTY);
return this;
}
- @Override
- public Type getOptimisticType() {
- return Type.OBJECT;
- }
-
private static final MethodHandle HAS_GET_ELEM = specialCall(MethodHandles.lookup(), ObjectArrayData.class, "getElem", Object.class, int.class).methodHandle();
private static final MethodHandle SET_ELEM = specialCall(MethodHandles.lookup(), ObjectArrayData.class, "setElem", void.class, int.class, Object.class).methodHandle();
diff --git a/src/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java b/src/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java
index d28b731c..ef473451 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java
@@ -78,7 +78,7 @@ class SparseArrayData extends ArrayData {
for (final Map.Entry<Long, Object> entry : sparseMap.entrySet()) {
final long key = entry.getKey();
- if (key <= Integer.MAX_VALUE) {
+ if (key < Integer.MAX_VALUE) {
objArray[(int)key] = entry.getValue();
} else {
break; // ascending key order
diff --git a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java
index 44ea1846..7986b8b4 100644
--- a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java
+++ b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java
@@ -152,6 +152,7 @@ final class JavaAdapterBytecodeGenerator {
static final String SET_GLOBAL_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.VOID_TYPE, OBJECT_TYPE);
static final String VOID_NOARG_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.VOID_TYPE);
+ private static final Type SCRIPT_OBJECT_TYPE = Type.getType(ScriptObject.class);
private static final Type SCRIPT_FUNCTION_TYPE = Type.getType(ScriptFunction.class);
private static final Type STRING_TYPE = Type.getType(String.class);
private static final Type METHOD_TYPE_TYPE = Type.getType(MethodType.class);
@@ -536,8 +537,8 @@ final class JavaAdapterBytecodeGenerator {
final int argLen = originalArgTypes.length;
final Type[] newArgTypes = new Type[argLen + 1];
- // Insert ScriptFunction|Object as the last argument to the constructor
- final Type extraArgumentType = fromFunction ? SCRIPT_FUNCTION_TYPE : OBJECT_TYPE;
+ // Insert ScriptFunction|ScriptObject as the last argument to the constructor
+ final Type extraArgumentType = fromFunction ? SCRIPT_FUNCTION_TYPE : SCRIPT_OBJECT_TYPE;
newArgTypes[argLen] = extraArgumentType;
System.arraycopy(originalArgTypes, 0, newArgTypes, 0, argLen);
@@ -588,6 +589,34 @@ final class JavaAdapterBytecodeGenerator {
// Initialize converters
generateConverterInit(mv, fromFunction);
endInitMethod(mv);
+
+ if (! fromFunction) {
+ newArgTypes[argLen] = OBJECT_TYPE;
+ final InstructionAdapter mv2 = new InstructionAdapter(cw.visitMethod(ACC_PUBLIC, INIT,
+ Type.getMethodDescriptor(originalCtorType.getReturnType(), newArgTypes), null, null));
+ generateOverridingConstructorWithObjectParam(mv2, ctor, originalCtorType.getDescriptor());
+ }
+ }
+
+ // Object additional param accepting constructor - generated to handle null and undefined value
+ // for script adapters. This is effectively to throw TypeError on such script adapters. See
+ // JavaAdapterServices.getHandle as well.
+ private void generateOverridingConstructorWithObjectParam(final InstructionAdapter mv, final Constructor<?> ctor, final String ctorDescriptor) {
+ mv.visitCode();
+ mv.visitVarInsn(ALOAD, 0);
+ final Class<?>[] argTypes = ctor.getParameterTypes();
+ int offset = 1; // First arg is at position 1, after this.
+ for (int i = 0; i < argTypes.length; ++i) {
+ final Type argType = Type.getType(argTypes[i]);
+ mv.load(offset, argType);
+ offset += argType.getSize();
+ }
+ mv.invokespecial(superClassName, INIT, ctorDescriptor, false);
+ mv.visitVarInsn(ALOAD, offset);
+ mv.visitInsn(ACONST_NULL);
+ mv.visitInsn(ACONST_NULL);
+ mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getHandle", GET_HANDLE_OBJECT_DESCRIPTOR, false);
+ endInitMethod(mv);
}
private static void endInitMethod(final InstructionAdapter mv) {
diff --git a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterClassLoader.java b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterClassLoader.java
index f5ba8b12..77004978 100644
--- a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterClassLoader.java
+++ b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterClassLoader.java
@@ -39,6 +39,7 @@ import jdk.nashorn.internal.codegen.DumpBytecode;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.ScriptFunction;
+import jdk.nashorn.internal.runtime.ScriptObject;
/**
* This class encapsulates the bytecode of the adapter class and can be used to load it into the JVM as an actual Class.
@@ -51,7 +52,7 @@ final class JavaAdapterClassLoader {
private static final AccessControlContext CREATE_LOADER_ACC_CTXT = ClassAndLoader.createPermAccCtxt("createClassLoader");
private static final AccessControlContext GET_CONTEXT_ACC_CTXT = ClassAndLoader.createPermAccCtxt(Context.NASHORN_GET_CONTEXT);
private static final Collection<String> VISIBLE_INTERNAL_CLASS_NAMES = Collections.unmodifiableCollection(new HashSet<>(
- Arrays.asList(JavaAdapterServices.class.getName(), ScriptFunction.class.getName(), JSType.class.getName())));
+ Arrays.asList(JavaAdapterServices.class.getName(), ScriptObject.class.getName(), ScriptFunction.class.getName(), JSType.class.getName())));
private final String className;
private final byte[] classBytes;
diff --git a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterServices.java b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterServices.java
index ef91f35c..08e46821 100644
--- a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterServices.java
+++ b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterServices.java
@@ -47,7 +47,6 @@ import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.Opcodes;
import jdk.internal.org.objectweb.asm.Type;
import jdk.internal.org.objectweb.asm.commons.InstructionAdapter;
-import jdk.nashorn.api.scripting.ScriptUtils;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
@@ -220,7 +219,7 @@ public final class JavaAdapterServices {
* @return the filtered return value.
*/
public static Object exportReturnValue(final Object obj) {
- return ScriptUtils.wrap(NashornBeansLinker.exportArgument(obj));
+ return NashornBeansLinker.exportArgument(obj, true);
}
/**
diff --git a/src/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java b/src/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java
index f802e039..25ba619b 100644
--- a/src/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java
+++ b/src/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java
@@ -35,17 +35,28 @@ import jdk.internal.dynalink.linker.GuardingDynamicLinker;
import jdk.internal.dynalink.linker.LinkRequest;
import jdk.internal.dynalink.linker.LinkerServices;
import jdk.internal.dynalink.support.Lookup;
+import jdk.nashorn.api.scripting.ScriptUtils;
+import jdk.nashorn.internal.objects.NativeArray;
import jdk.nashorn.internal.runtime.ConsString;
+import jdk.nashorn.internal.runtime.ScriptObject;
+import jdk.nashorn.internal.runtime.options.Options;
/**
* This linker delegates to a {@code BeansLinker} but passes it a special linker services object that has a modified
* {@code asType} method that will ensure that we never pass internal engine objects that should not be externally
- * observable (currently only ConsString) to Java APIs, but rather that we flatten it into a String. We can't just add
+ * observable (currently ConsString and ScriptObject) to Java APIs, but rather that we flatten it into a String. We can't just add
* this functionality as custom converters via {@code GuaardingTypeConverterFactory}, since they are not consulted when
* the target method handle parameter signature is {@code Object}.
*/
public class NashornBeansLinker implements GuardingDynamicLinker {
+ // System property to control whether to wrap ScriptObject->ScriptObjectMirror for
+ // Object type arguments of Java method calls, field set and array set.
+ private static final boolean MIRROR_ALWAYS = Options.getBooleanProperty("nashorn.mirror.always", true);
+
private static final MethodHandle EXPORT_ARGUMENT = new Lookup(MethodHandles.lookup()).findOwnStatic("exportArgument", Object.class, Object.class);
+ private static final MethodHandle EXPORT_NATIVE_ARRAY = new Lookup(MethodHandles.lookup()).findOwnStatic("exportNativeArray", Object.class, NativeArray.class);
+ private static final MethodHandle EXPORT_SCRIPT_OBJECT = new Lookup(MethodHandles.lookup()).findOwnStatic("exportScriptObject", Object.class, ScriptObject.class);
+ private static final MethodHandle IMPORT_RESULT = new Lookup(MethodHandles.lookup()).findOwnStatic("importResult", Object.class, Object.class);
private final BeansLinker beansLinker = new BeansLinker();
@@ -67,8 +78,39 @@ public class NashornBeansLinker implements GuardingDynamicLinker {
return delegateLinker.getGuardedInvocation(linkRequest, new NashornBeansLinkerServices(linkerServices));
}
- static Object exportArgument(final Object arg) {
- return arg instanceof ConsString ? arg.toString() : arg;
+ @SuppressWarnings("unused")
+ private static Object exportArgument(final Object arg) {
+ return exportArgument(arg, MIRROR_ALWAYS);
+ }
+
+ @SuppressWarnings("unused")
+ private static Object exportNativeArray(final NativeArray arg) {
+ return exportArgument(arg, MIRROR_ALWAYS);
+ }
+
+ @SuppressWarnings("unused")
+ private static Object exportScriptObject(final ScriptObject arg) {
+ return exportArgument(arg, MIRROR_ALWAYS);
+ }
+
+ @SuppressWarnings("unused")
+ private static Object exportScriptArray(final NativeArray arg) {
+ return exportArgument(arg, MIRROR_ALWAYS);
+ }
+
+ static Object exportArgument(final Object arg, final boolean mirrorAlways) {
+ if (arg instanceof ConsString) {
+ return arg.toString();
+ } else if (mirrorAlways && arg instanceof ScriptObject) {
+ return ScriptUtils.wrap((ScriptObject)arg);
+ } else {
+ return arg;
+ }
+ }
+
+ @SuppressWarnings("unused")
+ private static Object importResult(final Object arg) {
+ return ScriptUtils.unwrap(arg);
}
private static class NashornBeansLinkerServices implements LinkerServices {
@@ -80,35 +122,58 @@ public class NashornBeansLinker implements GuardingDynamicLinker {
@Override
public MethodHandle asType(final MethodHandle handle, final MethodType fromType) {
- final MethodHandle typed = linkerServices.asType(handle, fromType);
-
final MethodType handleType = handle.type();
final int paramCount = handleType.parameterCount();
assert fromType.parameterCount() == handleType.parameterCount();
+ MethodType newFromType = fromType;
MethodHandle[] filters = null;
for(int i = 0; i < paramCount; ++i) {
- if(shouldConvert(handleType.parameterType(i), fromType.parameterType(i))) {
- if(filters == null) {
+ final MethodHandle filter = argConversionFilter(handleType.parameterType(i), fromType.parameterType(i));
+ if (filter != null) {
+ if (filters == null) {
filters = new MethodHandle[paramCount];
}
- filters[i] = EXPORT_ARGUMENT;
+ // "erase" specific type with Object type or else we'll get filter mismatch
+ newFromType = newFromType.changeParameterType(i, Object.class);
+ filters[i] = filter;
}
}
- return filters != null ? MethodHandles.filterArguments(typed, 0, filters) : typed;
+ final MethodHandle typed = linkerServices.asType(handle, newFromType);
+ MethodHandle result = filters != null ? MethodHandles.filterArguments(typed, 0, filters) : typed;
+ // Filter Object typed return value for possible ScriptObjectMirror. We convert
+ // ScriptObjectMirror as ScriptObject (if it is mirror from current global).
+ if (MIRROR_ALWAYS && areBothObjects(handleType.returnType(), fromType.returnType())) {
+ result = MethodHandles.filterReturnValue(result, IMPORT_RESULT);
+ }
+
+ return result;
}
- @Override
- public MethodHandle asTypeLosslessReturn(final MethodHandle handle, final MethodType fromType) {
- return Implementation.asTypeLosslessReturn(this, handle, fromType);
+ private static MethodHandle argConversionFilter(final Class<?> handleType, final Class<?> fromType) {
+ if (handleType == Object.class) {
+ if (fromType == Object.class) {
+ return EXPORT_ARGUMENT;
+ } else if (fromType == NativeArray.class) {
+ return EXPORT_NATIVE_ARRAY;
+ } else if (fromType == ScriptObject.class) {
+ return EXPORT_SCRIPT_OBJECT;
+ }
+ }
+ return null;
}
- private static boolean shouldConvert(final Class<?> handleType, final Class<?> fromType) {
+ private static boolean areBothObjects(final Class<?> handleType, final Class<?> fromType) {
return handleType == Object.class && fromType == Object.class;
}
@Override
+ public MethodHandle asTypeLosslessReturn(final MethodHandle handle, final MethodType fromType) {
+ return Implementation.asTypeLosslessReturn(this, handle, fromType);
+ }
+
+ @Override
public MethodHandle getTypeConverter(final Class<?> sourceType, final Class<?> targetType) {
return linkerServices.getTypeConverter(sourceType, targetType);
}
diff --git a/src/jdk/nashorn/internal/runtime/linker/NashornLinker.java b/src/jdk/nashorn/internal/runtime/linker/NashornLinker.java
index bde01567..75af367f 100644
--- a/src/jdk/nashorn/internal/runtime/linker/NashornLinker.java
+++ b/src/jdk/nashorn/internal/runtime/linker/NashornLinker.java
@@ -292,7 +292,7 @@ final class NashornLinker implements TypeBasedGuardingDynamicLinker, GuardingTyp
@SuppressWarnings("unused")
private static Object createMirror(final Object obj) {
- return ScriptUtils.wrap(obj);
+ return obj instanceof ScriptObject? ScriptUtils.wrap((ScriptObject)obj) : obj;
}
private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
diff --git a/src/jdk/nashorn/internal/runtime/resources/Messages.properties b/src/jdk/nashorn/internal/runtime/resources/Messages.properties
index 056dc87d..3a161c8d 100644
--- a/src/jdk/nashorn/internal/runtime/resources/Messages.properties
+++ b/src/jdk/nashorn/internal/runtime/resources/Messages.properties
@@ -73,6 +73,7 @@ type.error.strict.getter.setter.poison=In strict mode, "caller", "callee", and "
type.error.not.an.object={0} is not an Object
type.error.not.a.boolean={0} is not a Boolean
type.error.not.a.date={0} is not a Date
+type.error.not.a.java.importer={0} is not a JavaImporter object
type.error.not.a.number={0} is not a Number
type.error.not.a.regexp={0} is not a RegExp
type.error.not.a.string={0} is not a String
diff --git a/src/jdk/nashorn/internal/runtime/resources/mozilla_compat.js b/src/jdk/nashorn/internal/runtime/resources/mozilla_compat.js
index 85e2161c..6c27e2a6 100644
--- a/src/jdk/nashorn/internal/runtime/resources/mozilla_compat.js
+++ b/src/jdk/nashorn/internal/runtime/resources/mozilla_compat.js
@@ -105,7 +105,7 @@ Object.defineProperty(this, "sync", {
if (arguments.length < 1 || arguments.length > 2 ) {
throw "sync(function [,object]) parameter count mismatch";
}
- return Packages.jdk.nashorn.api.scripting.ScriptUtils.makeSynchronizedFunction(func, syncobj);
+ return Java.synchronized(func, syncobj);
}
});
@@ -160,7 +160,7 @@ Object.defineProperty(Object.prototype, "toSource", {
configurable: true, enumerable: false, writable: true,
value: function(state) {
if (! state) {
- state = java.util.Collections.newSetFromMap(new java.util.IdentityHashMap());
+ state = java.util.Collections.newSetFromMap(new java.util.HashMap());
}
if (state.contains(this)) {
return "{}";
diff --git a/test/script/basic/JDK-8058610.js b/test/script/basic/JDK-8058610.js
new file mode 100644
index 00000000..47078c0c
--- /dev/null
+++ b/test/script/basic/JDK-8058610.js
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/**
+ * JDK-8058610: must not let long operations overflow
+ *
+ * @test
+ * @run
+ */
+
+function mul(x) {
+ return x.foo * x.bar;
+}
+print("=== mul ===")
+print(mul({foo: 2147483647, bar: 2147483647})); // 2^31
+print(mul({foo: 17179869184, bar: 2147483647})); // 2^34
+
+function self_mul(x) {
+ return x.foo *= x.bar;
+}
+print("=== self_mul ===")
+print(self_mul({foo: 2147483647, bar: 2147483647})); // 2^31
+print(self_mul({foo: 17179869184, bar: 2147483647})); // 2^34
+
+// We'll need to use this function to obtain long values larger in
+// magnitude than those precisely representable in a double (2^53),
+// as Nashorn's parser will reify such literals as a double. For
+// overflow on add and sub we need (2^63)-1.
+var parseLong = Java.type("java.lang.Long").parseLong;
+
+function sub(x) {
+ return x.foo - x.bar;
+}
+print("=== sub ===")
+print(sub({foo: 2147483647, bar: -2147483647})); // 2^31
+print(sub({foo: parseLong("9223372036854775807"), bar: parseLong("-9223372036854775807")})); // 2^63-1
+
+function self_sub(x) {
+ return x.foo -= x.bar;
+}
+print("=== self_sub ===")
+print(self_sub({foo: 2147483647, bar: -2147483647})); // 2^31
+print(self_sub({foo: parseLong("9223372036854775807"), bar: parseLong("-9223372036854775807")})); // 2^63-1
+
+function add(x) {
+ return x.foo + x.bar;
+}
+print("=== add ===")
+print(add({foo: 2147483647, bar: 2147483647})); // 2^31
+print(add({foo: parseLong("9223372036854775807"), bar: parseLong("9223372036854775807")})); // 2^63-1
+
+function self_add(x) {
+ return x.foo += x.bar;
+}
+print("=== self_add ===")
+print(self_add({foo: 2147483647, bar: 2147483647})); // 2^31
+print(self_add({foo: parseLong("9223372036854775807"), bar: parseLong("9223372036854775807")})); // 2^63-1
diff --git a/test/script/basic/JDK-8058610.js.EXPECTED b/test/script/basic/JDK-8058610.js.EXPECTED
new file mode 100644
index 00000000..d9d39915
--- /dev/null
+++ b/test/script/basic/JDK-8058610.js.EXPECTED
@@ -0,0 +1,18 @@
+=== mul ===
+4611686014132420600
+36893488130239234000
+=== self_mul ===
+4611686014132420600
+36893488130239234000
+=== sub ===
+4294967294
+18446744073709552000
+=== self_sub ===
+4294967294
+18446744073709552000
+=== add ===
+4294967294
+18446744073709552000
+=== self_add ===
+4294967294
+18446744073709552000
diff --git a/test/script/basic/JDK-8060011.js b/test/script/basic/JDK-8060011.js
new file mode 100644
index 00000000..7514ff29
--- /dev/null
+++ b/test/script/basic/JDK-8060011.js
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/**
+ * JDK-8060011: Concatenating an array and converting it to Java gives wrong result
+ *
+ * @test
+ * @run
+ */
+
+
+function compareAsJavaArrays(a1, a2) {
+ var ja1 = Java.to(a1);
+ var ja2 = Java.to(a2);
+ if (ja1.length !== ja2.length) {
+ throw "different length";
+ }
+ for (var i = 0; i < ja1.length; i++) {
+ if (ja1[i] !== ja2[i]) {
+ throw "different element at " + i;
+ }
+ }
+ if (java.util.Arrays.toString(ja1) !== java.util.Arrays.toString(ja2)) {
+ throw "different string representation";
+ }
+}
+
+compareAsJavaArrays([0, 1, 2, 3],
+ [0].concat([1, 2, 3]));
+compareAsJavaArrays([1000000000, 2000000000, 3000000000, 4000000000],
+ [1000000000].concat([2000000000, 3000000000, 4000000000]));
+compareAsJavaArrays([0.5, 1.5, 2.5, 3.5],
+ [0.5].concat([1.5, 2.5, 3.5]));
+compareAsJavaArrays(["0", "1", "2", "3"],
+ ["0"].concat(["1", "2", "3"]));
+
+
+
diff --git a/test/script/basic/JDK-8060101.js b/test/script/basic/JDK-8060101.js
new file mode 100644
index 00000000..617bbccd
--- /dev/null
+++ b/test/script/basic/JDK-8060101.js
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/**
+ * JDK-8060101: AssertionError: __noSuchProperty__ placeholder called from NativeJavaImporter
+ *
+ * @test
+ * @run
+ */
+
+var constant = 0.50;
+var ind = 0.0;
+
+// make sure callsites are exercised quite a few times
+// to induce megamorphic callsite for with/JavaImporter
+// combo - which triggered that AssertionError.
+for (var i = 0; i < 50; i++) {
+ var math = new JavaImporter(java.lang.StrictMath);
+ ind += 10.0;
+ with (math) {
+ StrictMath.exp(-constant*ind);
+ }
+}
+
+for (var i = 0; i < 50; i++) {
+ var math = new JavaImporter(java.lang.StrictMath);
+ try {
+ math.Foo();
+ } catch (e) {
+ if (! (e instanceof TypeError)) {
+ throw e;
+ }
+ }
+}
diff --git a/test/script/basic/JDK-8061113.js b/test/script/basic/JDK-8061113.js
new file mode 100644
index 00000000..1cd8b35b
--- /dev/null
+++ b/test/script/basic/JDK-8061113.js
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/**
+ * JDK-8061113: Boolean used as optimistic call return type
+ *
+ * @test
+ * @run
+ */
+
+function testcase() {
+ var a = {x:0};
+ return (function () {return a.x === 0})();
+}
+print(testcase());
diff --git a/test/script/basic/JDK-8061113.js.EXPECTED b/test/script/basic/JDK-8061113.js.EXPECTED
new file mode 100644
index 00000000..27ba77dd
--- /dev/null
+++ b/test/script/basic/JDK-8061113.js.EXPECTED
@@ -0,0 +1 @@
+true
diff --git a/test/script/basic/convert.js b/test/script/basic/convert.js
index 3a1bca08..2c87661c 100644
--- a/test/script/basic/convert.js
+++ b/test/script/basic/convert.js
@@ -42,7 +42,7 @@ print(list);
// object to Map
obj = { foo: 333, bar: 'hello'};
-var map = ScriptUtils.convert(obj, java.util.Map.class);
+var map = ScriptUtils.wrap(obj);
print(map instanceof java.util.Map);
for (m in map) {
print(m + " " + map[m]);
diff --git a/test/script/nosecurity/JDK-8044798.js b/test/script/nosecurity/JDK-8044798.js
index c3b6d4cb..c24edf21 100644
--- a/test/script/nosecurity/JDK-8044798.js
+++ b/test/script/nosecurity/JDK-8044798.js
@@ -25,6 +25,8 @@
* JDK-8044798: API for debugging Nashorn
*
* @test
+ * @option -Dnashorn.mirror.always=false
+ * @fork
* @run
*/
diff --git a/test/script/nosecurity/JDK-8060688.js b/test/script/nosecurity/JDK-8060688.js
new file mode 100644
index 00000000..8fe57814
--- /dev/null
+++ b/test/script/nosecurity/JDK-8060688.js
@@ -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.
+ *
+ * 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.
+ */
+
+/**
+ * JDK-8060688: Nashorn: Generated script class name fails --verify-code for names with special chars
+ *
+ * @test
+ * @run
+ */
+
+var NashornEngineFactory = Java.type("jdk.nashorn.api.scripting.NashornScriptEngineFactory");
+var ScriptEngine = Java.type("javax.script.ScriptEngine");
+var ScriptContext = Java.type("javax.script.ScriptContext");
+
+var factory = new NashornEngineFactory();
+
+var e = factory.getScriptEngine("--verify-code");
+
+function evalAndCheck(code) {
+ try {
+ e.eval(code);
+ } catch (exp) {
+ exp.printStackTrace();
+ }
+}
+
+// check default name
+evalAndCheck("var a = 3");
+// check few names with special chars
+var scontext = e.context;
+scontext.setAttribute(ScriptEngine.FILENAME, "<myscript>", ScriptContext.ENGINE_SCOPE);
+evalAndCheck("var h = 'hello'");
+scontext.setAttribute(ScriptEngine.FILENAME, "[myscript]", ScriptContext.ENGINE_SCOPE);
+evalAndCheck("var foo = 'world'");
+scontext.setAttribute(ScriptEngine.FILENAME, ";/\\$.", ScriptContext.ENGINE_SCOPE);
+evalAndCheck("var foo = 'helloworld'");
diff --git a/test/src/jdk/nashorn/api/scripting/ScriptEngineSecurityTest.java b/test/src/jdk/nashorn/api/scripting/ScriptEngineSecurityTest.java
index 09199a52..226832e7 100644
--- a/test/src/jdk/nashorn/api/scripting/ScriptEngineSecurityTest.java
+++ b/test/src/jdk/nashorn/api/scripting/ScriptEngineSecurityTest.java
@@ -168,42 +168,6 @@ public class ScriptEngineSecurityTest {
}
}
- @Test
- /**
- * Check that script can't implement sensitive package interfaces.
- */
- public void checkSensitiveInterfaceImplTest() throws ScriptException {
- if (System.getSecurityManager() == null) {
- // pass vacuously
- return;
- }
-
- final ScriptEngineManager m = new ScriptEngineManager();
- final ScriptEngine e = m.getEngineByName("nashorn");
- final Object[] holder = new Object[1];
- e.put("holder", holder);
- // put an empty script object into array
- e.eval("holder[0] = {}");
- // holder[0] is an object of some subclass of ScriptObject
- final Class<?> ScriptObjectClass = holder[0].getClass().getSuperclass();
- final Class<?> PropertyAccessClass = ScriptObjectClass.getInterfaces()[0];
- // implementation methods for PropertyAccess class
- e.eval("function set() {}; function get() {}; function getInt(){} " +
- "function getDouble(){}; function getLong() {}; " +
- "this.delete = function () {}; function has() {}; " +
- "function hasOwnProperty() {}");
-
- // get implementation of a restricted package interface
- try {
- log(Objects.toString(((Invocable)e).getInterface((Class<?>)PropertyAccessClass)));
- fail("should have thrown SecurityException");
- } catch (final Exception exp) {
- if (! (exp instanceof SecurityException)) {
- fail("SecurityException expected, got " + exp);
- }
- }
- }
-
// @bug 8032948: Nashorn linkages awry
public static class FakeProxy extends Proxy {
public FakeProxy(final InvocationHandler ih) {
diff --git a/test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java b/test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java
index 291e8d42..124b5a92 100644
--- a/test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java
+++ b/test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java
@@ -38,6 +38,7 @@ import java.lang.reflect.Proxy;
import java.util.concurrent.Callable;
import javax.script.Compilable;
import javax.script.CompiledScript;
+import javax.script.Invocable;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
@@ -629,6 +630,40 @@ public class ScriptEngineTest {
assertEquals(enumerable, Boolean.FALSE);
}
+ public static class Context {
+ private Object myobj;
+
+ public void set(Object o) {
+ myobj = o;
+ }
+
+ public Object get() {
+ return myobj;
+ }
+ }
+
+ // @bug 8050977: Java8 Javascript Nashorn exception:
+ // no current Global instance for nashorn
+ @Test
+ public void currentGlobalMissingTest() throws Exception {
+ final ScriptEngineManager manager = new ScriptEngineManager();
+ final ScriptEngine e = manager.getEngineByName("nashorn");
+
+ final Context ctx = new Context();
+ e.put("ctx", ctx);
+ e.eval("var obj = { foo: function(str) { return str.toUpperCase() } }");
+ e.eval("ctx.set(obj)");
+ final Invocable inv = (Invocable)e;
+ assertEquals("HELLO", inv.invokeMethod(ctx.get(), "foo", "hello"));
+ // try object literal
+ e.eval("ctx.set({ bar: function(str) { return str.toLowerCase() } })");
+ assertEquals("hello", inv.invokeMethod(ctx.get(), "bar", "HELLO"));
+ // try array literal
+ e.eval("var arr = [ 'hello', 'world' ]");
+ e.eval("ctx.set(arr)");
+ assertEquals("helloworld", inv.invokeMethod(ctx.get(), "join", ""));
+ }
+
private static void checkProperty(final ScriptEngine e, final String name)
throws ScriptException {
final String value = System.getProperty(name);
diff --git a/test/src/jdk/nashorn/internal/codegen/CompilerTest.java b/test/src/jdk/nashorn/internal/codegen/CompilerTest.java
index 9eb801fd..dadeb15f 100644
--- a/test/src/jdk/nashorn/internal/codegen/CompilerTest.java
+++ b/test/src/jdk/nashorn/internal/codegen/CompilerTest.java
@@ -72,6 +72,7 @@ public class CompilerTest {
options.set("print.parse", true);
options.set("scripting", true);
options.set("const.as.var", true);
+ options.set("verify.code", true);
final ErrorManager errors = new ErrorManager() {
@Override
diff --git a/test/src/jdk/nashorn/internal/runtime/TrustedScriptEngineTest.java b/test/src/jdk/nashorn/internal/runtime/TrustedScriptEngineTest.java
index 8225cc08..f7a18dcd 100644
--- a/test/src/jdk/nashorn/internal/runtime/TrustedScriptEngineTest.java
+++ b/test/src/jdk/nashorn/internal/runtime/TrustedScriptEngineTest.java
@@ -325,4 +325,29 @@ public class TrustedScriptEngineTest {
);
assertEquals(ret, 10, "Parsed and executed OK");
}
+
+ @Test
+ public void evalDefaultFileNameTest() throws ScriptException {
+ final NashornScriptEngineFactory fac = new NashornScriptEngineFactory();
+ final ScriptEngine engine = fac.getScriptEngine(new String[] { "--verify-code=true" });
+ // default FILENAME being "<eval>" make sure generated code bytecode verifies.
+ engine.eval("var a = 3;");
+ }
+
+ @Test
+ public void evalFileNameWithSpecialCharsTest() throws ScriptException {
+ final NashornScriptEngineFactory fac = new NashornScriptEngineFactory();
+ final ScriptEngine engine = fac.getScriptEngine(new String[] { "--verify-code=true" });
+ final ScriptContext ctxt = new SimpleScriptContext();
+ // use file name with "dangerous" chars.
+ ctxt.setAttribute(ScriptEngine.FILENAME, "<myscript>", ScriptContext.ENGINE_SCOPE);
+ engine.eval("var a = 3;");
+ ctxt.setAttribute(ScriptEngine.FILENAME, "[myscript]", ScriptContext.ENGINE_SCOPE);
+ engine.eval("var h = 'hello';");
+ ctxt.setAttribute(ScriptEngine.FILENAME, ";/\\$.", ScriptContext.ENGINE_SCOPE);
+ engine.eval("var foo = 'world';");
+ // name used by jjs shell tool for the interactive mode
+ ctxt.setAttribute(ScriptEngine.FILENAME, "<shell>", ScriptContext.ENGINE_SCOPE);
+ engine.eval("var foo = 'world';");
+ }
}
diff --git a/test/src/jdk/nashorn/internal/test/framework/ParallelTestRunner.java b/test/src/jdk/nashorn/internal/test/framework/ParallelTestRunner.java
index 5faebfda..0de5524a 100644
--- a/test/src/jdk/nashorn/internal/test/framework/ParallelTestRunner.java
+++ b/test/src/jdk/nashorn/internal/test/framework/ParallelTestRunner.java
@@ -78,7 +78,8 @@ public class ParallelTestRunner {
// ParallelTestRunner-specific
private static final String TEST_JS_THREADS = "test.js.threads";
private static final String TEST_JS_REPORT_FILE = "test.js.report.file";
- private static final int THREADS = Integer.getInteger(TEST_JS_THREADS, Runtime.getRuntime().availableProcessors());
+ // test262 does a lot of eval's and the JVM hates multithreaded class definition, so lower thread count is usually faster.
+ private static final int THREADS = Integer.getInteger(TEST_JS_THREADS, Runtime.getRuntime().availableProcessors() > 4 ? 4 : 2);
private final List<ScriptRunnable> tests = new ArrayList<>();
private final Set<String> orphans = new TreeSet<>();