aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoraph <none@none>2014-11-04 17:21:00 +0000
committeraph <none@none>2014-11-04 17:21:00 +0000
commit26bb98da48624e826379c5651a4c59ef6061d737 (patch)
tree3e68a90fa1ac149228abc77393ed694caced39f9
parentd5e3667913984e51299fed1fd433a83e1a128189 (diff)
parent8bc3074f2f762a214520796905ebb397a600180e (diff)
Merge to jdk8u40-b12jdk8u40-b12-aarch64
-rw-r--r--.hgtags22
-rw-r--r--bin/fixwhitespace.sh37
-rw-r--r--buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ClassGenerator.java1
-rw-r--r--buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MemberInfo.java77
-rw-r--r--buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MethodGenerator.java31
-rw-r--r--buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInfo.java12
-rw-r--r--buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInfoCollector.java40
-rw-r--r--buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInstrumentor.java1
-rw-r--r--buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/StringConstants.java29
-rw-r--r--docs/DEVELOPER_README737
-rw-r--r--make/BuildNashorn.gmk2
-rw-r--r--make/build-nasgen.xml2
-rw-r--r--make/build.xml75
-rw-r--r--make/project.properties21
-rw-r--r--samples/BufferArray.java28
-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.java23
-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.java15
-rw-r--r--src/jdk/nashorn/internal/codegen/CodeGenerator.java324
-rw-r--r--src/jdk/nashorn/internal/codegen/CompilationPhase.java212
-rw-r--r--src/jdk/nashorn/internal/codegen/CompileUnit.java41
-rw-r--r--src/jdk/nashorn/internal/codegen/Compiler.java250
-rw-r--r--src/jdk/nashorn/internal/codegen/FindScopeDepths.java7
-rw-r--r--src/jdk/nashorn/internal/codegen/Label.java18
-rw-r--r--src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java60
-rw-r--r--src/jdk/nashorn/internal/codegen/Lower.java14
-rw-r--r--src/jdk/nashorn/internal/codegen/MethodEmitter.java94
-rw-r--r--src/jdk/nashorn/internal/codegen/OptimisticTypesCalculator.java29
-rw-r--r--src/jdk/nashorn/internal/codegen/OptimisticTypesPersistence.java8
-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.java111
-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.java72
-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.java12
-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.java15
-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.java163
-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.java21
-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.java32
-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.java28
-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/lookup/MethodHandleFactory.java68
-rw-r--r--src/jdk/nashorn/internal/lookup/MethodHandleFunctionality.java9
-rw-r--r--src/jdk/nashorn/internal/objects/ArrayBufferView.java2
-rw-r--r--src/jdk/nashorn/internal/objects/Global.java248
-rw-r--r--src/jdk/nashorn/internal/objects/NativeArray.java421
-rw-r--r--src/jdk/nashorn/internal/objects/NativeDataView.java6
-rw-r--r--src/jdk/nashorn/internal/objects/NativeDate.java5
-rw-r--r--src/jdk/nashorn/internal/objects/NativeDebug.java5
-rw-r--r--src/jdk/nashorn/internal/objects/NativeFloat32Array.java10
-rw-r--r--src/jdk/nashorn/internal/objects/NativeFloat64Array.java10
-rw-r--r--src/jdk/nashorn/internal/objects/NativeInt16Array.java20
-rw-r--r--src/jdk/nashorn/internal/objects/NativeInt32Array.java20
-rw-r--r--src/jdk/nashorn/internal/objects/NativeInt8Array.java20
-rw-r--r--src/jdk/nashorn/internal/objects/NativeJSAdapter.java1
-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/NativeRegExp.java9
-rw-r--r--src/jdk/nashorn/internal/objects/NativeString.java137
-rw-r--r--src/jdk/nashorn/internal/objects/NativeUint16Array.java20
-rw-r--r--src/jdk/nashorn/internal/objects/NativeUint32Array.java15
-rw-r--r--src/jdk/nashorn/internal/objects/NativeUint8Array.java20
-rw-r--r--src/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java20
-rw-r--r--src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java18
-rw-r--r--src/jdk/nashorn/internal/objects/annotations/SpecializedFunction.java315
-rw-r--r--src/jdk/nashorn/internal/parser/AbstractParser.java1
-rw-r--r--src/jdk/nashorn/internal/parser/Lexer.java8
-rw-r--r--src/jdk/nashorn/internal/runtime/AccessorProperty.java50
-rw-r--r--src/jdk/nashorn/internal/runtime/AstDeserializer.java47
-rw-r--r--src/jdk/nashorn/internal/runtime/CodeInstaller.java25
-rw-r--r--src/jdk/nashorn/internal/runtime/CodeStore.java9
-rw-r--r--src/jdk/nashorn/internal/runtime/CompiledFunction.java134
-rw-r--r--src/jdk/nashorn/internal/runtime/Context.java80
-rw-r--r--src/jdk/nashorn/internal/runtime/Debug.java9
-rw-r--r--src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java21
-rw-r--r--src/jdk/nashorn/internal/runtime/FindProperty.java3
-rw-r--r--src/jdk/nashorn/internal/runtime/GlobalConstants.java15
-rw-r--r--src/jdk/nashorn/internal/runtime/GlobalFunctions.java18
-rw-r--r--src/jdk/nashorn/internal/runtime/OptimisticBuiltins.java65
-rw-r--r--src/jdk/nashorn/internal/runtime/Property.java36
-rw-r--r--src/jdk/nashorn/internal/runtime/PropertyMap.java2
-rw-r--r--src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java145
-rw-r--r--src/jdk/nashorn/internal/runtime/ScriptEnvironment.java4
-rw-r--r--src/jdk/nashorn/internal/runtime/ScriptFunction.java119
-rw-r--r--src/jdk/nashorn/internal/runtime/ScriptFunctionData.java26
-rw-r--r--src/jdk/nashorn/internal/runtime/ScriptObject.java81
-rw-r--r--src/jdk/nashorn/internal/runtime/ScriptRuntime.java27
-rw-r--r--src/jdk/nashorn/internal/runtime/SetMethodCreator.java25
-rw-r--r--src/jdk/nashorn/internal/runtime/Specialization.java114
-rw-r--r--src/jdk/nashorn/internal/runtime/StoredScript.java6
-rw-r--r--src/jdk/nashorn/internal/runtime/UserAccessorProperty.java1
-rw-r--r--src/jdk/nashorn/internal/runtime/WithObject.java13
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/ArrayData.java21
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/ArrayFilter.java28
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/ContinuousArrayData.java89
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/DeletedArrayFilter.java21
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/DeletedRangeArrayFilter.java23
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/IntArrayData.java118
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/IntElements.java (renamed from src/jdk/nashorn/internal/objects/annotations/SpecializedConstructor.java)25
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/IntOrLongElements.java34
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/LongArrayData.java93
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/NoTypeArrayData.java18
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/NumberArrayData.java90
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/NumericElements.java35
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/ObjectArrayData.java90
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java80
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/TypedArrayData.java9
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/UndefinedArrayFilter.java17
-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/linker/PrimitiveLookup.java28
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/RegExpFactory.java16
-rw-r--r--src/jdk/nashorn/internal/runtime/resources/Messages.properties1
-rw-r--r--src/jdk/nashorn/internal/runtime/resources/Options.properties6
-rw-r--r--src/jdk/nashorn/internal/runtime/resources/mozilla_compat.js4
-rw-r--r--test/examples/charcodeat-benchmark.js63
-rw-r--r--test/examples/push-pop-benchmark.js59
-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/apply_to_call/apply_to_call5.js111
-rw-r--r--test/script/basic/apply_to_call/apply_to_call5.js.EXPECTED19
-rw-r--r--test/script/basic/convert.js2
-rw-r--r--test/script/basic/fastpushpop.js61
-rw-r--r--test/script/basic/fastpushpop.js.EXPECTED6
-rw-r--r--test/script/basic/octane-payload.js2
-rw-r--r--test/script/basic/run-octane.js221
-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/ClassFilterTest.java12
-rw-r--r--test/src/jdk/nashorn/internal/runtime/TrustedScriptEngineTest.java25
-rw-r--r--test/src/jdk/nashorn/internal/test/framework/ParallelTestRunner.java3
-rw-r--r--test/src/jdk/nashorn/internal/test/framework/ScriptRunnable.java2
-rw-r--r--test/src/jdk/nashorn/internal/test/framework/TestFinder.java60
196 files changed, 5935 insertions, 2428 deletions
diff --git a/.hgtags b/.hgtags
index b331b300..0cc78182 100644
--- a/.hgtags
+++ b/.hgtags
@@ -308,6 +308,25 @@ ad36f9454ce38d78be39fc819902e1223765ee5e jdk8u20-b23
d3da140e179343011017669a6dbfcc52b0e56f52 jdk8u20-b24
d3da140e179343011017669a6dbfcc52b0e56f52 jdk8u20-b25
a23ac9db4227d78b3389e01fa94a8cb695a8fb0a jdk8u20-b26
+7001e9f95b443a75e432205a29974c05b88e0fdc jdk8u25-b00
+a9f77bd14874d5f8fdf935704dd54a0451f2bc69 jdk8u25-b01
+895e47783e2ee6823496a5ae84039a4f50311c7d jdk8u25-b02
+b84d92194c367411fcd8b5f510d4589709a8e71e jdk8u25-b03
+894ab2f06c93987f8596f5906985ff0a452f2fb2 jdk8u25-b04
+25b89ca363c41e1a1d90d7e95d5227d23e4292f3 jdk8u25-b05
+0a50d568a901700213fe40c38089748ca1d1af88 jdk8u25-b06
+25b719b33ac8f8ffb7e4353fddcda93ca6027b0d jdk8u25-b07
+0f74f65763a300cfe5f897b6b21f36d64f9d2115 jdk8u25-b08
+158837f537e45fc4664a56ad4759f8a1b30cab73 jdk8u25-b09
+7e00c05fc54b0404bf2eedda35dd38ae1ad23e50 jdk8u25-b10
+8cd6af10dd4de9e28ffe30c9107954fffd15dc99 jdk8u25-b11
+f76715cd4e902602bdbb4ba9a3774c10afeee012 jdk8u25-b12
+34c95bcacff79a5794416a8e715a8e63bfe7fc58 jdk8u25-b13
+6a93467eaa36f732b84ecd463e046c4066fef40c jdk8u25-b14
+71e8403a2f8279315419adf5f4e9d6b232b6835c jdk8u25-b15
+1500138ce513600457be6bfa10979ecce6515aa6 jdk8u25-b16
+4b9cc65dd24d398c4f921c0beccfb8caeaaaf584 jdk8u25-b17
+cdbf34dbef404b47805c8c85b11c65c2afaa6674 jdk8u25-b18
f2925491b61b22ac42f8c30ee9c6723ffa401a4c jdk8u40-b00
62468d841b842769d875bd97d10370585c296eb7 jdk8u40-b01
bdd9c38d1e61edbf770b8733b70a37d0cf0e7055 jdk8u40-b02
@@ -317,3 +336,6 @@ a2e0a985764b5afd5f316429bfab4f44bf150f7f jdk8u40-b03
1196f17cf7bc709766319f5bf7a5394a7251b47a jdk8u40-b06
0032961e1866c22afe3d0bbbb217f8840be61846 jdk8u40-b07
89551828b279233825204b72233edafc72d8feb3 jdk8u40-b08
+6a8ecdeae4a9a438eed637b5a5d0d18fddb9f711 jdk8u40-b09
+076b1f38a5ccd4692a6f93939a7fc03bc1a1bbb4 jdk8u40-b10
+57c7b273277e00f7a98fafb18ff07aa3245808f0 jdk8u40-b11
diff --git a/bin/fixwhitespace.sh b/bin/fixwhitespace.sh
new file mode 100644
index 00000000..d3274700
--- /dev/null
+++ b/bin/fixwhitespace.sh
@@ -0,0 +1,37 @@
+#!/bin/bash
+#
+# 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.
+#
+# 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.
+#
+
+fix() {
+ #convert tabs to spaces
+ find . -name $1 -exec sed -i "" 's/ / /g' {} \;
+ #remove trailing whitespace
+ find . -name $1 -exec sed -i "" 's/[ ]*$//' \{} \;
+}
+
+if [ ! -z $1 ]; then
+ fix $1;
+else
+ fix "*.java"
+ fix "*.js"
+fi
diff --git a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ClassGenerator.java b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ClassGenerator.java
index 2059567b..75874316 100644
--- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ClassGenerator.java
+++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ClassGenerator.java
@@ -292,7 +292,6 @@ public class ClassGenerator {
mi.push(memInfo.getArity());
mi.invokeVirtual(SCRIPTFUNCTION_TYPE, SCRIPTFUNCTION_SETARITY, SCRIPTFUNCTION_SETARITY_DESC);
}
-
}
static void linkerAddGetterSetter(final MethodGenerator mi, final String className, final MemberInfo memInfo) {
diff --git a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MemberInfo.java b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MemberInfo.java
index c8a929dc..cc3524da 100644
--- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MemberInfo.java
+++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MemberInfo.java
@@ -28,7 +28,6 @@ import static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_ARRAY_DES
import static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTOBJECT_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.STRING_DESC;
-
import jdk.internal.org.objectweb.asm.Opcodes;
import jdk.internal.org.objectweb.asm.Type;
import jdk.nashorn.internal.objects.annotations.Where;
@@ -75,10 +74,6 @@ public final class MemberInfo implements Cloneable {
* This is a specialized version of a function
*/
SPECIALIZED_FUNCTION,
- /**
- * This is a specialized version of a constructor
- */
- SPECIALIZED_CONSTRUCTOR
}
// keep in sync with jdk.nashorn.internal.objects.annotations.Attribute
@@ -107,6 +102,12 @@ public final class MemberInfo implements Cloneable {
private Where where;
+ private Type linkLogicClass;
+
+ private boolean isSpecializedConstructor;
+
+ private boolean isOptimistic;
+
/**
* @return the kind
*/
@@ -136,6 +137,57 @@ public final class MemberInfo implements Cloneable {
}
/**
+ * Tag something as specialized constructor or not
+ * @param isSpecializedConstructor boolean, true if specialized constructor
+ */
+ public void setIsSpecializedConstructor(final boolean isSpecializedConstructor) {
+ this.isSpecializedConstructor = isSpecializedConstructor;
+ }
+
+ /**
+ * Check if something is a specialized constructor
+ * @return true if specialized constructor
+ */
+ public boolean isSpecializedConstructor() {
+ return isSpecializedConstructor;
+ }
+
+ /**
+ * Check if this is an optimistic builtin function
+ * @return true if optimistic builtin
+ */
+ public boolean isOptimistic() {
+ return isOptimistic;
+ }
+
+ /**
+ * Tag something as optimitic builtin or not
+ * @param isOptimistic boolean, true if builtin constructor
+ */
+ public void setIsOptimistic(final boolean isOptimistic) {
+ this.isOptimistic = isOptimistic;
+ }
+
+ /**
+ * Get the SpecializedFunction guard for specializations, i.e. optimistic
+ * builtins
+ * @return specialization, null if none
+ */
+ public Type getLinkLogicClass() {
+ return linkLogicClass;
+ }
+
+ /**
+ * Set thre SpecializedFunction link logic class for specializations, i.e. optimistic
+ * builtins
+ * @param linkLogicClass link logic class
+ */
+
+ public void setLinkLogicClass(final Type linkLogicClass) {
+ this.linkLogicClass = linkLogicClass;
+ }
+
+ /**
* @return the attributes
*/
public int getAttributes() {
@@ -304,19 +356,6 @@ public final class MemberInfo implements Cloneable {
}
}
break;
- case SPECIALIZED_CONSTRUCTOR: {
- final Type returnType = Type.getReturnType(javaDesc);
- if (!isJSObjectType(returnType)) {
- error("return value of a @SpecializedConstructor method should be a valid JS type, found " + returnType);
- }
- final Type[] argTypes = Type.getArgumentTypes(javaDesc);
- for (int i = 0; i < argTypes.length; i++) {
- if (!isValidJSType(argTypes[i])) {
- error(i + "'th argument of a @SpecializedConstructor method is not valid JS type, found " + argTypes[i]);
- }
- }
- }
- break;
case FUNCTION: {
final Type returnType = Type.getReturnType(javaDesc);
if (!(isValidJSType(returnType) || Type.VOID_TYPE == returnType)) {
@@ -351,7 +390,7 @@ public final class MemberInfo implements Cloneable {
break;
case SPECIALIZED_FUNCTION: {
final Type returnType = Type.getReturnType(javaDesc);
- if (!(isValidJSType(returnType) || Type.VOID_TYPE == returnType)) {
+ if (!(isValidJSType(returnType) || (isSpecializedConstructor() && Type.VOID_TYPE == returnType))) {
error("return value of a @SpecializedFunction method should be a valid JS type, found " + returnType);
}
final Type[] argTypes = Type.getArgumentTypes(javaDesc);
diff --git a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MethodGenerator.java b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MethodGenerator.java
index a8d6ae2b..a8bd954f 100644
--- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MethodGenerator.java
+++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MethodGenerator.java
@@ -56,6 +56,7 @@ import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKESTATIC;
import static jdk.internal.org.objectweb.asm.Opcodes.IALOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.IASTORE;
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_0;
+import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_1;
import static jdk.internal.org.objectweb.asm.Opcodes.ILOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.INVOKEINTERFACE;
import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESPECIAL;
@@ -76,13 +77,16 @@ import static jdk.internal.org.objectweb.asm.Opcodes.SALOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.SASTORE;
import static jdk.internal.org.objectweb.asm.Opcodes.SIPUSH;
import static jdk.internal.org.objectweb.asm.Opcodes.SWAP;
-import static jdk.nashorn.internal.tools.nasgen.StringConstants.METHODHANDLE_TYPE;
-import static jdk.nashorn.internal.tools.nasgen.StringConstants.TYPE_METHODHANDLE;
-
+import static jdk.nashorn.internal.tools.nasgen.StringConstants.INIT;
+import static jdk.nashorn.internal.tools.nasgen.StringConstants.SPECIALIZATION_INIT2;
+import static jdk.nashorn.internal.tools.nasgen.StringConstants.SPECIALIZATION_INIT3;
+import static jdk.nashorn.internal.tools.nasgen.StringConstants.SPECIALIZATION_TYPE;
+import static jdk.nashorn.internal.tools.nasgen.StringConstants.TYPE_SPECIALIZATION;
import java.util.List;
import jdk.internal.org.objectweb.asm.Handle;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.Type;
+import jdk.nashorn.internal.objects.annotations.SpecializedFunction.LinkLogic;
/**
* Base class for all method generating classes.
@@ -95,6 +99,8 @@ public class MethodGenerator extends MethodVisitor {
private final Type returnType;
private final Type[] argumentTypes;
+ static final Type EMPTY_LINK_LOGIC_TYPE = Type.getType(LinkLogic.getEmptyLinkLogicClass());
+
MethodGenerator(final MethodVisitor mv, final int access, final String name, final String descriptor) {
super(ASM4, mv);
this.access = access;
@@ -380,6 +386,11 @@ public class MethodGenerator extends MethodVisitor {
super.visitFieldInsn(GETFIELD, owner, field, desc);
}
+ private static boolean linkLogicIsEmpty(final Type type) {
+ assert EMPTY_LINK_LOGIC_TYPE != null; //type is ok for null if we are a @SpecializedFunction without any attribs
+ return EMPTY_LINK_LOGIC_TYPE.equals(type);
+ }
+
void memberInfoArray(final String className, final List<MemberInfo> mis) {
if (mis.isEmpty()) {
pushNull();
@@ -388,12 +399,22 @@ public class MethodGenerator extends MethodVisitor {
int pos = 0;
push(mis.size());
- newObjectArray(METHODHANDLE_TYPE);
+ newObjectArray(SPECIALIZATION_TYPE);
for (final MemberInfo mi : mis) {
dup();
push(pos++);
+ visitTypeInsn(NEW, SPECIALIZATION_TYPE);
+ dup();
visitLdcInsn(new Handle(H_INVOKESTATIC, className, mi.getJavaName(), mi.getJavaDesc()));
- arrayStore(TYPE_METHODHANDLE);
+ final Type linkLogicClass = mi.getLinkLogicClass();
+ final boolean linkLogic = !linkLogicIsEmpty(linkLogicClass);
+ final String ctor = linkLogic ? SPECIALIZATION_INIT3 : SPECIALIZATION_INIT2;
+ if (linkLogic) {
+ visitLdcInsn(linkLogicClass);
+ }
+ visitInsn(mi.isOptimistic() ? ICONST_1 : ICONST_0);
+ visitMethodInsn(INVOKESPECIAL, SPECIALIZATION_TYPE, INIT, ctor, false);
+ arrayStore(TYPE_SPECIALIZATION);
}
}
diff --git a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInfo.java b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInfo.java
index 0a1579ff..9e3dfc17 100644
--- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInfo.java
+++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInfo.java
@@ -37,8 +37,8 @@ import jdk.nashorn.internal.objects.annotations.Getter;
import jdk.nashorn.internal.objects.annotations.Property;
import jdk.nashorn.internal.objects.annotations.ScriptClass;
import jdk.nashorn.internal.objects.annotations.Setter;
-import jdk.nashorn.internal.objects.annotations.SpecializedConstructor;
import jdk.nashorn.internal.objects.annotations.SpecializedFunction;
+import jdk.nashorn.internal.objects.annotations.SpecializedFunction.LinkLogic;
import jdk.nashorn.internal.objects.annotations.Where;
import jdk.nashorn.internal.tools.nasgen.MemberInfo.Kind;
@@ -56,8 +56,8 @@ public final class ScriptClassInfo {
static final String SETTER_ANNO_DESC = Type.getDescriptor(Setter.class);
static final String PROPERTY_ANNO_DESC = Type.getDescriptor(Property.class);
static final String WHERE_ENUM_DESC = Type.getDescriptor(Where.class);
+ static final String LINK_LOGIC_DESC = Type.getDescriptor(LinkLogic.class);
static final String SPECIALIZED_FUNCTION = Type.getDescriptor(SpecializedFunction.class);
- static final String SPECIALIZED_CONSTRUCTOR = Type.getDescriptor(SpecializedConstructor.class);
static final Map<String, Kind> annotations = new HashMap<>();
@@ -69,7 +69,6 @@ public final class ScriptClassInfo {
annotations.put(SETTER_ANNO_DESC, Kind.SETTER);
annotations.put(PROPERTY_ANNO_DESC, Kind.PROPERTY);
annotations.put(SPECIALIZED_FUNCTION, Kind.SPECIALIZED_FUNCTION);
- annotations.put(SPECIALIZED_CONSTRUCTOR, Kind.SPECIALIZED_CONSTRUCTOR);
}
// name of the script class
@@ -119,11 +118,12 @@ public final class ScriptClassInfo {
List<MemberInfo> getSpecializedConstructors() {
final List<MemberInfo> res = new LinkedList<>();
for (final MemberInfo memInfo : members) {
- if (memInfo.getKind() == Kind.SPECIALIZED_CONSTRUCTOR) {
+ if (memInfo.isSpecializedConstructor()) {
+ assert memInfo.getKind() == Kind.SPECIALIZED_FUNCTION;
res.add(memInfo);
}
}
- return res;
+ return Collections.unmodifiableList(res);
}
int getPrototypeMemberCount() {
@@ -175,7 +175,7 @@ public final class ScriptClassInfo {
res.add(memInfo);
}
}
- return res;
+ return Collections.unmodifiableList(res);
}
MemberInfo findSetter(final MemberInfo getter) {
diff --git a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInfoCollector.java b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInfoCollector.java
index 5561cfbf..aa477621 100644
--- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInfoCollector.java
+++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInfoCollector.java
@@ -27,7 +27,6 @@ package jdk.nashorn.internal.tools.nasgen;
import static jdk.nashorn.internal.tools.nasgen.ScriptClassInfo.SCRIPT_CLASS_ANNO_DESC;
import static jdk.nashorn.internal.tools.nasgen.ScriptClassInfo.WHERE_ENUM_DESC;
-
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
@@ -41,6 +40,7 @@ import jdk.internal.org.objectweb.asm.ClassVisitor;
import jdk.internal.org.objectweb.asm.FieldVisitor;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.Type;
import jdk.nashorn.internal.objects.annotations.Where;
import jdk.nashorn.internal.tools.nasgen.MemberInfo.Kind;
@@ -194,6 +194,7 @@ public class ScriptClassInfoCollector extends ClassVisitor {
final MemberInfo memInfo = new MemberInfo();
+ //annokind == e.g. GETTER or SPECIALIZED_FUNCTION
memInfo.setKind(annoKind);
memInfo.setJavaName(methodName);
memInfo.setJavaDesc(methodDesc);
@@ -208,12 +209,18 @@ public class ScriptClassInfoCollector extends ClassVisitor {
private Integer attributes;
private Integer arity;
private Where where;
+ private boolean isSpecializedConstructor;
+ private boolean isOptimistic;
+ private Type linkLogicClass = MethodGenerator.EMPTY_LINK_LOGIC_TYPE;
@Override
public void visit(final String annotationName, final Object annotationValue) {
switch (annotationName) {
case "name":
this.name = (String)annotationValue;
+ if (name.isEmpty()) {
+ name = null;
+ }
break;
case "attributes":
this.attributes = (Integer)annotationValue;
@@ -221,6 +228,17 @@ public class ScriptClassInfoCollector extends ClassVisitor {
case "arity":
this.arity = (Integer)annotationValue;
break;
+ case "isConstructor":
+ assert annoKind == Kind.SPECIALIZED_FUNCTION;
+ this.isSpecializedConstructor = (Boolean)annotationValue;
+ break;
+ case "isOptimistic":
+ assert annoKind == Kind.SPECIALIZED_FUNCTION;
+ this.isOptimistic = (Boolean)annotationValue;
+ break;
+ case "linkLogic":
+ this.linkLogicClass = (Type)annotationValue;
+ break;
default:
break;
}
@@ -230,12 +248,19 @@ public class ScriptClassInfoCollector extends ClassVisitor {
@Override
public void visitEnum(final String enumName, final String desc, final String enumValue) {
- if ("where".equals(enumName) && WHERE_ENUM_DESC.equals(desc)) {
- this.where = Where.valueOf(enumValue);
+ switch (enumName) {
+ case "where":
+ if (WHERE_ENUM_DESC.equals(desc)) {
+ this.where = Where.valueOf(enumValue);
+ }
+ break;
+ default:
+ break;
}
super.visitEnum(enumName, desc, enumValue);
}
+ @SuppressWarnings("fallthrough")
@Override
public void visitEnd() {
super.visitEnd();
@@ -256,7 +281,6 @@ public class ScriptClassInfoCollector extends ClassVisitor {
case SETTER:
where = Where.INSTANCE;
break;
- case SPECIALIZED_CONSTRUCTOR:
case CONSTRUCTOR:
where = Where.CONSTRUCTOR;
break;
@@ -264,12 +288,18 @@ public class ScriptClassInfoCollector extends ClassVisitor {
where = Where.PROTOTYPE;
break;
case SPECIALIZED_FUNCTION:
- //TODO is this correct
+ if (isSpecializedConstructor) {
+ where = Where.CONSTRUCTOR;
+ }
+ //fallthru
default:
break;
}
}
memInfo.setWhere(where);
+ memInfo.setLinkLogicClass(linkLogicClass);
+ memInfo.setIsSpecializedConstructor(isSpecializedConstructor);
+ memInfo.setIsOptimistic(isOptimistic);
}
};
}
diff --git a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInstrumentor.java b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInstrumentor.java
index 0ec233ae..0d6a9e37 100644
--- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInstrumentor.java
+++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInstrumentor.java
@@ -38,7 +38,6 @@ import static jdk.nashorn.internal.tools.nasgen.StringConstants.DEFAULT_INIT_DES
import static jdk.nashorn.internal.tools.nasgen.StringConstants.INIT;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTOBJECT_TYPE;
-
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
diff --git a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/StringConstants.java b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/StringConstants.java
index fb72bbe6..8656a42b 100644
--- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/StringConstants.java
+++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/StringConstants.java
@@ -37,6 +37,7 @@ import jdk.nashorn.internal.runtime.AccessorProperty;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
+import jdk.nashorn.internal.runtime.Specialization;
/**
* String constants used for code generation/instrumentation.
@@ -44,20 +45,26 @@ import jdk.nashorn.internal.runtime.ScriptObject;
@SuppressWarnings("javadoc")
public interface StringConstants {
// standard jdk types, methods
- static final Type TYPE_METHODHANDLE = Type.getType(MethodHandle.class);
- static final Type TYPE_METHODHANDLE_ARRAY = Type.getType(MethodHandle[].class);
- static final Type TYPE_OBJECT = Type.getType(Object.class);
- static final Type TYPE_STRING = Type.getType(String.class);
- static final Type TYPE_COLLECTION = Type.getType(Collection.class);
- static final Type TYPE_COLLECTIONS = Type.getType(Collections.class);
- static final Type TYPE_ARRAYLIST = Type.getType(ArrayList.class);
- static final Type TYPE_LIST = Type.getType(List.class);
+ static final Type TYPE_METHODHANDLE = Type.getType(MethodHandle.class);
+ static final Type TYPE_METHODHANDLE_ARRAY = Type.getType(MethodHandle[].class);
+ static final Type TYPE_SPECIALIZATION = Type.getType(Specialization.class);
+ static final Type TYPE_SPECIALIZATION_ARRAY = Type.getType(Specialization[].class);
+ static final Type TYPE_OBJECT = Type.getType(Object.class);
+ static final Type TYPE_STRING = Type.getType(String.class);
+ static final Type TYPE_CLASS = Type.getType(Class.class);
+ static final Type TYPE_COLLECTION = Type.getType(Collection.class);
+ static final Type TYPE_COLLECTIONS = Type.getType(Collections.class);
+ static final Type TYPE_ARRAYLIST = Type.getType(ArrayList.class);
+ static final Type TYPE_LIST = Type.getType(List.class);
static final String CLINIT = "<clinit>";
static final String INIT = "<init>";
static final String DEFAULT_INIT_DESC = Type.getMethodDescriptor(Type.VOID_TYPE);
static final String METHODHANDLE_TYPE = TYPE_METHODHANDLE.getInternalName();
+ static final String SPECIALIZATION_TYPE = TYPE_SPECIALIZATION.getInternalName();
+ static final String SPECIALIZATION_INIT2 = Type.getMethodDescriptor(Type.VOID_TYPE, TYPE_METHODHANDLE, Type.getType(boolean.class));
+ static final String SPECIALIZATION_INIT3 = Type.getMethodDescriptor(Type.VOID_TYPE, TYPE_METHODHANDLE, TYPE_CLASS, Type.getType(boolean.class));
static final String OBJECT_TYPE = TYPE_OBJECT.getInternalName();
static final String OBJECT_DESC = TYPE_OBJECT.getDescriptor();
static final String STRING_TYPE = TYPE_STRING.getInternalName();
@@ -122,11 +129,11 @@ public interface StringConstants {
static final String SCRIPTFUNCTIONIMPL_MAKEFUNCTION_DESC =
Type.getMethodDescriptor(TYPE_SCRIPTFUNCTION, TYPE_STRING, TYPE_METHODHANDLE);
static final String SCRIPTFUNCTIONIMPL_MAKEFUNCTION_SPECS_DESC =
- Type.getMethodDescriptor(TYPE_SCRIPTFUNCTION, TYPE_STRING, TYPE_METHODHANDLE, TYPE_METHODHANDLE_ARRAY);
+ Type.getMethodDescriptor(TYPE_SCRIPTFUNCTION, TYPE_STRING, TYPE_METHODHANDLE, TYPE_SPECIALIZATION_ARRAY);
static final String SCRIPTFUNCTIONIMPL_INIT_DESC3 =
- Type.getMethodDescriptor(Type.VOID_TYPE, TYPE_STRING, TYPE_METHODHANDLE, TYPE_METHODHANDLE_ARRAY);
+ Type.getMethodDescriptor(Type.VOID_TYPE, TYPE_STRING, TYPE_METHODHANDLE, TYPE_SPECIALIZATION_ARRAY);
static final String SCRIPTFUNCTIONIMPL_INIT_DESC4 =
- Type.getMethodDescriptor(Type.VOID_TYPE, TYPE_STRING, TYPE_METHODHANDLE, TYPE_PROPERTYMAP, TYPE_METHODHANDLE_ARRAY);
+ Type.getMethodDescriptor(Type.VOID_TYPE, TYPE_STRING, TYPE_METHODHANDLE, TYPE_PROPERTYMAP, TYPE_SPECIALIZATION_ARRAY);
// ScriptObject
static final String SCRIPTOBJECT_TYPE = TYPE_SCRIPTOBJECT.getInternalName();
diff --git a/docs/DEVELOPER_README b/docs/DEVELOPER_README
index fe140954..5550b903 100644
--- a/docs/DEVELOPER_README
+++ b/docs/DEVELOPER_README
@@ -25,6 +25,14 @@ Example:
> java -Dnashorn.args="--lazy-complation --log=compiler" large-java-app-with-nashorn.jar
> ant -Dnashorn.args="--log=codegen" antjob
+SYSTEM PROPERTY: -Dnashorn.args.prepend=<string>
+
+This property behaves like nashorn.args, but adds the given arguments
+before the existing ones instead of after them. Later arguments will
+overwrite earlier ones, so this is useful for setting default arguments
+that can be overwritten.
+
+
SYSTEM PROPERTY: -Dnashorn.unstable.relink.threshold=x
This property controls how many call site misses are allowed before a
@@ -42,533 +50,38 @@ be split into several classes in order not to run out of bytecode space.
The default value is 0x8000 (32768).
-SYSTEM PROPERTY: -Dnashorn.compiler.intarithmetic
-
-(and integer arithmetic in general)
-
-<currently disabled - this is being refactored for update releases>
-
-Arithmetic operations in Nashorn (except bitwise ones) typically
-coerce the operands to doubles (as per the JavaScript spec). To switch
-this off and remain in integer mode, for example for "var x = a&b; var
-y = c&d; var z = x*y;", use this flag. This will force the
-multiplication of variables that are ints to be done with the IMUL
-bytecode and the result "z" to become an int.
-
-WARNING: Note that is is experimental only to ensure that type support
-exists for all primitive types. The generated code is unsound. This
-will be the case until we do optimizations based on it. There is a CR
-in Nashorn to do better range analysis, and ensure that this is only
-done where the operation can't overflow into a wider type. Currently
-no overflow checking is done, so at the moment, until range analysis
-has been completed, this option is turned off.
-
-We've experimented by using int arithmetic for everything and putting
-overflow checks afterwards, which would recompute the operation with
-the correct precision, but have yet to find a configuration where this
-is faster than just using doubles directly, even if the int operation
-does not overflow. Getting access to a JVM intrinsic that does branch
-on overflow would probably alleviate this.
-
-The future:
-
-We are transitioning to an optimistic type system that uses int
-arithmetic everywhere until proven wrong. The problem here is mostly
-catch an overflow exception and rolling back the state to a new method
-with less optimistic assumptions for an operation at a particular
-program point. This will most likely not be in the Java 8.0 release
-but likely end up in an update release
-
-For Java 8, several java.lang.Math methods like addExact, subExact and
-mulExact are available to help us. Experiments intrinsifying these
-show a lot of promise, and we have devised a system that basically
-does on stack replacement with exceptions in bytecode to revert
-erroneous assumptions. An explanation of how this works and what we
-are doing can be found here:
-http://www.slideshare.net/lagergren/lagergren-jvmls2013final
-
-Experiments with this show significant ~x2-3 performance increases on
-pretty much everything, provided that optimistic assumptions don't
-fail much. It will affect warmup time negatively, depending on how
-many erroneous too optimistic assumptions are placed in the code at
-compile time. We don't think this will be much of an issue.
-
-For example for a small benchmark that repeatedly executes this
-method taken from the Crypto Octane benchmark
-
-function am3(i,x,w,j,c,n) {
- var this_array = this.array;
- var w_array = w.array;
- var xl = x&0x3fff, xh = x>>14;
- while(--n >= 0) {
- var l = this_array[i]&0x3fff;
- var h = this_array[i++]>>14;
- var m = xh*l+h*xl;
- l = xl*l+((m&0x3fff)<<14)+w_array[j]+c;
- c = (l>>28)+(m>>14)+xh*h;
- w_array[j++] = l&0xfffffff;
- }
-
- return c;
-}
-
-The performance increase more than doubles. We are also working hard
-with the code generation team in the Java Virtual Machine to fix
-things that are lacking in invokedynamic performance, which is another
-area where a lot of ongoing performance work takes place
-
-"Pessimistic" bytecode for am3, guaranteed to be semantically correct:
-
-// access flags 0x9
- public static am3(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
- L0
- LINENUMBER 12 L0
- ALOAD 0
- INVOKEDYNAMIC dyn:getProp|getElem|getMethod:array(Ljava/lang/Object;)Ljava/lang/Object; [
- // handle kind 0x6 : INVOKESTATIC
- jdk/nashorn/internal/runtime/linker/Bootstrap.bootstrap((Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;I)Ljava/lang/invoke/CallSite;)
- // arguments:
- 0
- ]
- ASTORE 8
- L1
- LINENUMBER 13 L1
- ALOAD 3
- INVOKEDYNAMIC dyn:getProp|getElem|getMethod:array(Ljava/lang/Object;)Ljava/lang/Object; [
- // handle kind 0x6 : INVOKESTATIC
- jdk/nashorn/internal/runtime/linker/Bootstrap.bootstrap((Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;I)Ljava/lang/invoke/CallSite;)
- // arguments:
- 0
- ]
- ASTORE 9
- L2
- LINENUMBER 14 L2
- ALOAD 2
- INVOKESTATIC jdk/nashorn/internal/runtime/JSType.toInt32 (Ljava/lang/Object;)I
- SIPUSH 16383
- IAND
- ISTORE 10
- ALOAD 2
- INVOKESTATIC jdk/nashorn/internal/runtime/JSType.toInt32 (Ljava/lang/Object;)I
- BIPUSH 14
- ISHR
- ISTORE 11
- L3
- LINENUMBER 15 L3
- GOTO L4
- L5
- LINENUMBER 16 L5
- FRAME FULL [java/lang/Object java/lang/Object java/lang/Object java/lang/Object java/lang/Object java/lang/Object java/lang/Double T java/lang/Object java/lang/Object I I] []
- ALOAD 8
- ALOAD 1
- INVOKEDYNAMIC dyn:getElem|getProp|getMethod(Ljava/lang/Object;Ljava/lang/Object;)I [
- // handle kind 0x6 : INVOKESTATIC
- jdk/nashorn/internal/runtime/linker/Bootstrap.bootstrap((Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;I)Ljava/lang/invoke/CallSite;)
- // arguments:
- 0
- ]
- SIPUSH 16383
- IAND
- INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
- ASTORE 12
- L6
- LINENUMBER 17 L6
- ALOAD 8
- ALOAD 1
- INVOKESTATIC jdk/nashorn/internal/runtime/JSType.toNumber (Ljava/lang/Object;)D
- DUP2
- DCONST_1
- DADD
- INVOKESTATIC java/lang/Double.valueOf (D)Ljava/lang/Double;
- ASTORE 1
- INVOKEDYNAMIC dyn:getElem|getProp|getMethod(Ljava/lang/Object;D)I [
- // handle kind 0x6 : INVOKESTATIC
- jdk/nashorn/internal/runtime/linker/Bootstrap.bootstrap((Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;I)Ljava/lang/invoke/CallSite;)
- // arguments:
- 0
- ]
- BIPUSH 14
- ISHR
- ISTORE 13
- L7
- LINENUMBER 18 L7
- ILOAD 11
- I2D
- ALOAD 12
- INVOKESTATIC jdk/nashorn/internal/runtime/JSType.toNumber (Ljava/lang/Object;)D
- DMUL
- ILOAD 13
- I2D
- ILOAD 10
- I2D
- DMUL
- DADD
- DSTORE 14
- L8
- LINENUMBER 19 L8
- ILOAD 10
- I2D
- ALOAD 12
- INVOKESTATIC jdk/nashorn/internal/runtime/JSType.toNumber (Ljava/lang/Object;)D
- DMUL
- DLOAD 14
- INVOKESTATIC jdk/nashorn/internal/runtime/JSType.toInt32 (D)I
- SIPUSH 16383
- IAND
- BIPUSH 14
- ISHL
- I2D
- DADD
- ALOAD 9
- ALOAD 4
- INVOKEDYNAMIC dyn:getElem|getProp|getMethod(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; [
- // handle kind 0x6 : INVOKESTATIC
- jdk/nashorn/internal/runtime/linker/Bootstrap.bootstrap((Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;I)Ljava/lang/invoke/CallSite;)
- // arguments:
- 0
- ]
- INVOKEDYNAMIC ADD:ODO_D(DLjava/lang/Object;)Ljava/lang/Object; [
- // handle kind 0x6 : INVOKESTATIC
- jdk/nashorn/internal/runtime/linker/Bootstrap.runtimeBootstrap((Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;)
- // arguments: none
- ]
- ALOAD 5
- INVOKEDYNAMIC ADD:OOO_I(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; [
- // handle kind 0x6 : INVOKESTATIC
- jdk/nashorn/internal/runtime/linker/Bootstrap.runtimeBootstrap((Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;)
- // arguments: none
- ]
- ASTORE 12
- L9
- LINENUMBER 20 L9
- ALOAD 12
- INVOKESTATIC jdk/nashorn/internal/runtime/JSType.toInt32 (Ljava/lang/Object;)I
- BIPUSH 28
- ISHR
- I2D
- DLOAD 14
- INVOKESTATIC jdk/nashorn/internal/runtime/JSType.toInt32 (D)I
- BIPUSH 14
- ISHR
- I2D
- DADD
- ILOAD 11
- I2D
- ILOAD 13
- I2D
- DMUL
- DADD
- INVOKESTATIC java/lang/Double.valueOf (D)Ljava/lang/Double;
- ASTORE 5
- L10
- LINENUMBER 21 L10
- ALOAD 9
- ALOAD 4
- INVOKESTATIC jdk/nashorn/internal/runtime/JSType.toNumber (Ljava/lang/Object;)D
- DUP2
- DCONST_1
- DADD
- INVOKESTATIC java/lang/Double.valueOf (D)Ljava/lang/Double;
- ASTORE 4
- ALOAD 12
- INVOKESTATIC jdk/nashorn/internal/runtime/JSType.toInt32 (Ljava/lang/Object;)I
- LDC 268435455
- IAND
- INVOKEDYNAMIC dyn:setElem|setProp(Ljava/lang/Object;DI)V [
- // handle kind 0x6 : INVOKESTATIC
- jdk/nashorn/internal/runtime/linker/Bootstrap.bootstrap((Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;I)Ljava/lang/invoke/CallSite;)
- // arguments:
- 0
- ]
- L4
- FRAME FULL [java/lang/Object java/lang/Object java/lang/Object java/lang/Object java/lang/Object java/lang/Object java/lang/Object T java/lang/Object java/lang/Object I I] []
- ALOAD 6
- INVOKESTATIC jdk/nashorn/internal/runtime/JSType.toNumber (Ljava/lang/Object;)D
- LDC -1.0
- DADD
- DUP2
- INVOKESTATIC java/lang/Double.valueOf (D)Ljava/lang/Double;
- ASTORE 6
- DCONST_0
- DCMPL
- IFGE L5
- L11
- LINENUMBER 24 L11
- ALOAD 5
- ARETURN
-
-"Optimistic" bytecode that requires invalidation on e.g overflow. Factor
-x2-3 speedup:
-
-public static am3(Ljava/lang/Object;IILjava/lang/Object;III)I
- L0
- LINENUMBER 12 L0
- ALOAD 0
- INVOKEDYNAMIC dyn:getProp|getElem|getMethod:array(Ljava/lang/Object;)Ljava/lang/Object; [
- // handle kind 0x6 : INVOKESTATIC
- jdk/nashorn/internal/runtime/linker/Bootstrap.bootstrap((Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;I)Ljava/lang/invoke/CallSite;)
- // arguments:
- 0
- ]
- ASTORE 8
- L1
- LINENUMBER 13 L1
- ALOAD 3
- INVOKEDYNAMIC dyn:getProp|getElem|getMethod:array(Ljava/lang/Object;)Ljava/lang/Object; [
- // handle kind 0x6 : INVOKESTATIC
- jdk/nashorn/internal/runtime/linker/Bootstrap.bootstrap((Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;I)Ljava/lang/invoke/CallSite;)
- // arguments:
- 0
- ]
- ASTORE 9
- L2
- LINENUMBER 14 L2
- ILOAD 2
- SIPUSH 16383
- IAND
- ISTORE 10
- ILOAD 2
- BIPUSH 14
- ISHR
- ISTORE 11
- L3
- LINENUMBER 15 L3
- GOTO L4
- L5
- LINENUMBER 16 L5
- FRAME FULL [java/lang/Object I I java/lang/Object I I I T java/lang/Object java/lang/Object I I] []
- ALOAD 8
- ILOAD 1
- INVOKEDYNAMIC dyn:getElem|getProp|getMethod(Ljava/lang/Object;I)I [
- // handle kind 0x6 : INVOKESTATIC
- jdk/nashorn/internal/runtime/linker/Bootstrap.bootstrap((Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;I)Ljava/lang/invoke/CallSite;)
- // arguments:
- 0
- ]
- SIPUSH 16383
- IAND
- ISTORE 12
- L6
- LINENUMBER 17 L6
- ALOAD 8
- ILOAD 1
- DUP
- ICONST_1
- IADD
- ISTORE 1
- INVOKEDYNAMIC dyn:getElem|getProp|getMethod(Ljava/lang/Object;I)I [
- // handle kind 0x6 : INVOKESTATIC
- jdk/nashorn/internal/runtime/linker/Bootstrap.bootstrap((Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;I)Ljava/lang/invoke/CallSite;)
- // arguments:
- 0
- ]
- BIPUSH 14
- ISHR
- ISTORE 13
- L7
- LINENUMBER 18 L7
- ILOAD 11
- ILOAD 12
- BIPUSH 8
- INVOKESTATIC jdk/nashorn/internal/runtime/JSType.mulExact (III)I
- ILOAD 13
- ILOAD 10
- BIPUSH 9
- INVOKESTATIC jdk/nashorn/internal/runtime/JSType.mulExact (III)I
- IADD
- ISTORE 14
- L8
- LINENUMBER 19 L8
- ILOAD 10
- ILOAD 12
- BIPUSH 11
- INVOKESTATIC jdk/nashorn/internal/runtime/JSType.mulExact (III)I
- ILOAD 14
- SIPUSH 16383
- IAND
- BIPUSH 14
- ISHL
- IADD
- ALOAD 9
- ILOAD 4
- INVOKEDYNAMIC dyn:getElem|getProp|getMethod(Ljava/lang/Object;I)I [
- // handle kind 0x6 : INVOKESTATIC
- jdk/nashorn/internal/runtime/linker/Bootstrap.bootstrap((Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;I)Ljava/lang/invoke/CallSite;)
- // arguments:
- 0
- ]
- IADD
- ILOAD 5
- IADD
- ISTORE 12
- L9
- LINENUMBER 20 L9
- ILOAD 12
- BIPUSH 28
- ISHR
- ILOAD 14
- BIPUSH 14
- ISHR
- IADD
- ILOAD 11
- ILOAD 13
- BIPUSH 21
- INVOKESTATIC jdk/nashorn/internal/runtime/JSType.mulExact (III)I
- IADD
- ISTORE 5
- L10
- LINENUMBER 21 L10
- ALOAD 9
- ILOAD 4
- DUP
- ICONST_1
- IADD
- ISTORE 4
- ILOAD 12
- LDC 268435455
- IAND
- INVOKEDYNAMIC dyn:setElem|setProp(Ljava/lang/Object;II)V [
- // handle kind 0x6 : INVOKESTATIC
- jdk/nashorn/internal/runtime/linker/Bootstrap.bootstrap((Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;I)Ljava/lang/invoke/CallSite;)
- // arguments:
- 0
- ]
- L4
- FRAME SAME
- ILOAD 6
- ICONST_M1
- IADD
- DUP
- ISTORE 6
- ICONST_0
- IF_ICMPGE L5
- L11
- LINENUMBER 24 L11
- ILOAD 5
- IRETURN
-
-
-SYSTEM PROPERTY: -Dnashorn.codegen.debug, -Dnashorn.codegen.debug.trace=<x>
+SYSTEM PROPERTY: -Dnashorn.serialize.compression=<x>
-See the description of the codegen logger below.
+This property sets the compression level used when deflating serialized
+AST structures of anonymous split functions. Valid values range from 0 to 9,
+the default value is 4. Higher values will reduce memory size of serialized
+AST but increase CPU usage required for compression.
-SYSTEM_PROPERTY: -Dnashorn.fields.debug
+SYSTEM PROPERTY: -Dnashorn.codegen.debug.trace=<x>
-See the description on the fields logger below.
+See the description of the codegen logger below.
-SYSTEM PROPERTY: -Dnashorn.fields.dual
+SYSTEM PROPERTY: -Dnashorn.fields.objects
-When this property is true, Nashorn will attempt to use primitive
-fields for AccessorProperties (currently just AccessorProperties, not
-spill properties). Memory footprint for script objects will increase,
-as we need to maintain both a primitive field (a long) as well as an
-Object field for the property value. Ints are represented as the 32
-low bits of the long fields. Doubles are represented as the
-doubleToLongBits of their value. This way a single field can be used
-for all primitive types. Packing and unpacking doubles to their bit
-representation is intrinsified by the JVM and extremely fast.
+When this property is true, Nashorn will only use object fields for
+AccessorProperties. This means that primitive values must be boxed
+when stored in a field, which is significantly slower than using
+primitive fields.
-While dual fields in theory runs significantly faster than Object
-fields due to reduction of boxing and memory allocation overhead,
-there is still work to be done to make this a general purpose
-solution. Research is ongoing.
+By default, Nashorn uses dual object and long fields. Ints are
+represented as the 32 low bits of the long fields. Doubles are
+represented as the doubleToLongBits of their value. This way a
+single field can be used for all primitive types. Packing and
+unpacking doubles to their bit representation is intrinsified by
+the JVM and extremely fast.
In the future, this might complement or be replaced by experimental
feature sun.misc.TaggedArray, which has been discussed on the mlvm
mailing list. TaggedArrays are basically a way to share data space
between primitives and references, and have the GC understand this.
-As long as only primitive values are written to the fields and enough
-type information exists to make sure that any reads don't have to be
-uselessly boxed and unboxed, this is significantly faster than the
-standard "Objects only" approach that currently is the default. See
-test/examples/dual-fields-micro.js for an example that runs twice as
-fast with dual fields as without them. Here, the compiler, can
-determine that we are dealing with numbers only throughout the entire
-property life span of the properties involved.
-
-If a "real" object (not a boxed primitive) is written to a field that
-has a primitive representation, its callsite is relinked and an Object
-field is used forevermore for that particular field in that
-PropertyMap and its children, even if primitives are later assigned to
-it.
-
-As the amount of compile time type information is very small in a
-dynamic language like JavaScript, it is frequently the case that
-something has to be treated as an object, because we don't know any
-better. In reality though, it is often a boxed primitive is stored to
-an AccessorProperty. The fastest way to handle this soundly is to use
-a callsite typecheck and avoid blowing the field up to an Object. We
-never revert object fields to primitives. Ping-pong:ing back and forth
-between primitive representation and Object representation would cause
-fatal performance overhead, so this is not an option.
-
-For a general application the dual fields approach is still slower
-than objects only fields in some places, about the same in most cases,
-and significantly faster in very few. This is due the program using
-primitives, but we still can't prove it. For example "local_var a =
-call(); field = a;" may very well write a double to the field, but the
-compiler dare not guess a double type if field is a local variable,
-due to bytecode variables being strongly typed and later non
-interchangeable. To get around this, the entire method would have to
-be replaced and a continuation retained to restart from. We believe
-that the next steps we should go through are instead:
-
-1) Implement method specialization based on callsite, as it's quite
-frequently the case that numbers are passed around, but currently our
-function nodes just have object types visible to the compiler. For
-example "var b = 17; func(a,b,17)" is an example where two parameters
-can be specialized, but the main version of func might also be called
-from another callsite with func(x,y,"string").
-
-2) This requires lazy jitting as the functions have to be specialized
-per callsite.
-
-Even though "function square(x) { return x*x }" might look like a
-trivial function that can always only take doubles, this is not
-true. Someone might have overridden the valueOf for x so that the
-toNumber coercion has side effects. To fulfil JavaScript semantics,
-the coercion has to run twice for both terms of the multiplication
-even if they are the same object. This means that call site
-specialization is necessary, not parameter specialization on the form
-"function square(x) { var xd = (double)x; return xd*xd; }", as one
-might first think.
-
-Generating a method specialization for any variant of a function that
-we can determine by types at compile time is a combinatorial explosion
-of byte code (try it e.g. on all the variants of am3 in the Octane
-benchmark crypto.js). Thus, this needs to be lazy
-
-3) Optimistic callsite writes, something on the form
-
-x = y; //x is a field known to be a primitive. y is only an object as
-far as we can tell
-
-turns into
-
-try {
- x = (int)y;
-} catch (X is not an integer field right now | ClassCastException e) {
- x = y;
-}
-
-Mini POC shows that this is the key to a lot of dual field performance
-in seemingly trivial micros where one unknown object, in reality
-actually a primitive, foils it for us. Very common pattern. Once we
-are "all primitives", dual fields runs a lot faster than Object fields
-only.
-
-We still have to deal with objects vs primitives for local bytecode
-slots, possibly through code copying and versioning.
-
-The Future:
-
-We expect the usefulness of dual fields to increase significantly
-after the optimistic type system described in the section on
-integer arithmetic above is implemented.
-
SYSTEM PROPERTY: -Dnashorn.compiler.symbol.trace=[<x>[,*]],
-Dnashorn.compiler.symbol.stacktrace=[<x>[,*]]
@@ -628,6 +141,9 @@ for invoking a particular script function.
"identical" - this method compares two script objects for reference
equality. It is a == Java comparison
+"equals" - Returns true if two objects are either referentially
+identical or equal as defined by java.lang.Object.equals.
+
"dumpCounters" - will dump the debug counters' current values to
stdout.
@@ -648,66 +164,66 @@ Finally we count callsite misses on a per callsite bases, which occur
when a callsite has to be relinked, due to a previous assumption of
object layout being invalidated.
+"getContext" - return the current Nashorn context.
-SYSTEM PROPERTY: -Dnashorn.methodhandles.debug,
--Dnashorn.methodhandles.debug=create
+"equalWithoutType" - Returns true if if the two objects are both
+property maps, and they have identical properties in the same order,
+but allows the properties to differ in their types.
-If this property is enabled, each MethodHandle related call that uses
-the java.lang.invoke package gets its MethodHandle intercepted and an
-instrumentation printout of arguments and return value appended to
-it. This shows exactly which method handles are executed and from
-where. (Also MethodTypes and SwitchPoints). This can be augmented with
-more information, for example, instance count, by subclassing or
-further extending the TraceMethodHandleFactory implementation in
-MethodHandleFactory.java.
+"diffPropertyMaps" Returns a diagnostic string representing the difference
+of two property maps.
+
+"getClass" - Returns the Java class of an object, or undefined if null.
+
+"toJavaString" - Returns the Java toString representation of an object.
+
+"toIdentString" - Returns a string representation of an object consisting
+of its java class name and hash code.
+
+"getListenerCount" - Return the number of property listeners for a
+script object.
-If the property is specialized with "=create" as its option,
-instrumentation will be shown for method handles upon creation time
-rather than at runtime usage.
+"getEventQueueCapacity" - Get the capacity of the event queue.
+
+"setEventQueueCapacity" - Set the event queue capacity.
+
+"addRuntimeEvent" - Add a runtime event to the runtime event queue.
+The queue has a fixed size (see -Dnashorn.runtime.event.queue.size)
+and the oldest entry will be thrown out of the queue is about to overflow.
+
+"expandEventQueueCapacity" - Expands the event queue capacity,
+or truncates if capacity is lower than current capacity. Then only
+the newest entries are kept.
+
+"clearRuntimeEvents" - Clear the runtime event queue.
+
+"removeRuntimeEvent" - Remove a specific runtime event from the event queue.
+
+"getRuntimeEvents" - Return all runtime events in the queue as an array.
+
+"getLastRuntimeEvent" - Return the last runtime event in the queue.
SYSTEM PROPERTY: -Dnashorn.methodhandles.debug.stacktrace
-This does the same as nashorn.methodhandles.debug, but when enabled
-also dumps the stack trace for every instrumented method handle
-operation. Warning: This is enormously verbose, but provides a pretty
+This enhances methodhandles logging (see below) to also dump the
+stack trace for every instrumented method handle operation.
+Warning: This is enormously verbose, but provides a pretty
decent "grep:able" picture of where the calls are coming from.
-See the description of the codegen logger below for a more verbose
-description of this option
-
-
-SYSTEM PROPERTY: -Dnashorn.scriptfunction.specialization.disable
-
-There are several "fast path" implementations of constructors and
-functions in the NativeObject classes that, in their original form,
-take a variable amount of arguments. Said functions are also declared
-to take Object parameters in their original form, as this is what the
-JavaScript specification mandates.
-However, we often know quite a lot more at a callsite of one of these
-functions. For example, Math.min is called with a fixed number (2) of
-integer arguments. The overhead of boxing these ints to Objects and
-folding them into an Object array for the generic varargs Math.min
-function is an order of magnitude slower than calling a specialized
-implementation of Math.min that takes two integers. Specialized
-functions and constructors are identified by the tag
-@SpecializedFunction and @SpecializedConstructor in the Nashorn
-code. The linker will link in the most appropriate (narrowest types,
-right number of types and least number of arguments) specialization if
-specializations are available.
-
-Every ScriptFunction may carry specializations that the linker can
-choose from. This framework will likely be extended for user defined
-functions. The compiler can often infer enough parameter type info
-from callsites for in order to generate simpler versions with less
-generic Object types. This feature depends on future lazy jitting, as
-there tend to be many calls to user defined functions, some where the
-callsite can be specialized, some where we mostly see object
-parameters even at the callsite.
-
-If this system property is set to true, the linker will not attempt to
-use any specialized function or constructor for native objects, but
-just call the generic one.
+
+SYSTEM PROPERTY: -Dnashorn.cce
+
+Setting this system property causes the Nashorn linker to rely on
+ClassCastExceptions for triggering a callsite relink. If not set, the linker
+will add an explicit instanceof guard.
+
+
+SYSTEM PROPERTY: -Dnashorn.spill.threshold=<x>
+
+This property sets the number of fields in an object from which to use
+generic array based spill storage instead of Java fields. The default value
+is 256.
SYSTEM PROPERTY: -Dnashorn.tcs.miss.samplePercent=<x>
@@ -719,8 +235,47 @@ system property can be used to constrain the percentage of misses that
should be logged. Typically this is set to 1 or 5 (percent). 1% is the
default value.
+SYSTEM PROPERTY: -Dnashorn.persistent.code.cache
+
+This property can be used to set the directory where Nashorn stores
+serialized script classes generated with the -pcc/--persistent-code-cache
+option. The default directory name is "nashorn_code_cache".
-SYSTEM_PROPERTY: -Dnashorn.profilefile=<filename>
+
+SYSTEM PROPERTY: -Dnashorn.typeInfo.maxFiles
+
+Maximum number of files to store in the type info cache. The type info cache
+is used to cache type data of JavaScript functions when running with
+optimistic types (-ot/--optimistic-types). There is one file per JavaScript
+function in the cache.
+
+The default value is 0 which means the feature is disabled. Setting this
+to something like 20000 is probably good enough for most applications and
+will usually cap the cache directory to about 80MB presuming a 4kB
+filesystem allocation unit. Set this to "unlimited" to run without limit.
+
+If the value is not 0 or "unlimited", Nashorn will spawn a cleanup thread
+that makes sure the number of files in the cache does not exceed the given
+value by deleting the least recently modified files.
+
+
+SYSTEM PROPERTY: -Dnashorn.typeInfo.cacheDir
+
+This property can be used to set the directory where Nashorn stores the
+type info cache when -Dnashorn.typeInfo.maxFiles is set to a nonzero
+value. The default location is platform specific. On Windows, it is
+"${java.io.tmpdir}\com.oracle.java.NashornTypeInfo". On Linux and
+Solaris it is "~/.cache/com.oracle.java.NashornTypeInfo". On Mac OS X,
+it is "~/Library/Caches/com.oracle.java.NashornTypeInfo".
+
+
+SYSTEM PROPERTY: -Dnashorn.typeInfo.cleanupDelaySeconds=<value>
+
+This sets the delay between cleanups of the typeInfo cache, in seconds.
+The default delay is 20 seconds.
+
+
+SYSTEM PROPERTY: -Dnashorn.profilefile=<filename>
When running with the profile callsite options (-pcs), Nashorn will
dump profiling data for all callsites to stderr as a shutdown hook. To
@@ -736,6 +291,11 @@ JDK's java.util.regex package. Set this property to "joni" to install
an implementation based on Joni, the regular expression engine used by
the JRuby project. The default value for this flag is "joni"
+SYSTEM PROPERTY: -Dnashorn.runtime.event.queue.size=<value>
+
+Nashorn provides a fixed sized runtime event queue for debugging purposes.
+See -Dnashorn.debug for methods to access the event queue.
+The default value is 1024.
===============
2. The loggers.
@@ -767,7 +327,9 @@ times on the same command line, with the same effect.
For example: --log=codegen,fields:finest is equivalent to
--log=codegen:info --log=fields:finest
-The subsystems that currently support logging are:
+The following is an incomplete list of subsystems that currently
+support logging. Look for classes implementing
+jdk.nashorn.internal.runtime.logging.Loggable for more loggers.
* compiler
@@ -780,6 +342,14 @@ settings that all the tiers of codegen (e.g. Lower and CodeGenerator)
use.s
+* recompile
+
+This logger shows information about recompilation of scripts and
+functions at runtime. Recompilation may happen because a function
+was called with different parameter types, or because an optimistic
+assumption failed while executing a function with -ot/--optimistic-types.
+
+
* codegen
The code generator is the emitter stage of the code pipeline, and
@@ -836,25 +406,13 @@ and inlining finally blocks.
Lower is also responsible for determining control flow information
like end points.
+* symbols
-* attr
-
-The lowering annotates a FunctionNode with symbols for each identifier
-and transforms high level constructs into lower level ones, that the
-CodeGenerator consumes.
-
-Lower logging typically outputs things like post pass actions,
-insertions of casts because symbol types have been changed and type
-specialization information. Currently very little info is generated by
-this logger. This will probably change.
+The symbols logger tracks the assignment os symbols to identifiers.
+* scopedepths
-* finalize
-
-This --log=finalize log option outputs information for type finalization,
-the third tier of the compiler. This means things like placement of
-specialized scope nodes or explicit conversions.
-
+This logs the calculation of scope depths for non-local symbols.
* fields
@@ -896,6 +454,21 @@ Here is an example:
[time]
[time] Total runtime: 11994 ms (Non-runtime: 11027 ms [91%])
+* methodhandles
+
+If this logger is enabled, each MethodHandle related call that uses
+the java.lang.invoke package gets its MethodHandle intercepted and an
+instrumentation printout of arguments and return value appended to
+it. This shows exactly which method handles are executed and from
+where. (Also MethodTypes and SwitchPoints).
+
+* classcache
+
+This logger shows information about reusing code classes using the
+in-memory class cache. Nashorn will try to avoid compilation of
+scripts by using existing classes. This can significantly improve
+performance when repeatedly evaluating the same script.
+
=======================
3. Undocumented options
=======================
diff --git a/make/BuildNashorn.gmk b/make/BuildNashorn.gmk
index a987874c..683d4624 100644
--- a/make/BuildNashorn.gmk
+++ b/make/BuildNashorn.gmk
@@ -65,7 +65,7 @@ $(eval $(call SetupJavaCompilation,BUILD_NASGEN, \
SETUP := GENERATE_NEWBYTECODE_DEBUG, \
SRC := $(NASGEN_SRC) $(ASM_SRC), \
BIN := $(NASHORN_OUTPUTDIR)/nasgen_classes, \
- ADD_JAVAC_FLAGS := -cp $(NASHORN_OUTPUTDIR)/nashorn_classes))
+ ADD_JAVAC_FLAGS := -bootclasspath "$(BOOT_RTJAR)$(PATH_SEP)$(NASHORN_OUTPUTDIR)/nashorn_classes"))
# Nasgen needs nashorn classes
$(BUILD_NASGEN): $(BUILD_NASHORN)
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 9d21de84..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 -->
@@ -79,7 +77,30 @@
</condition>
</target>
- <target name="init" depends="init-conditions, init-cc">
+ <!-- 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="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}"/>
@@ -419,6 +428,7 @@ grant codeBase "file:/${basedir}/test/script/markdown.js" {
<propertyref prefix="test-sys-prop-no-security."/>
<mapper from="test-sys-prop-no-security.*" to="*" type="glob"/>
</propertyset>
+ <sysproperty key="optimistic.override" value="${optimistic}"/>
<classpath>
<pathelement path="${run.test.classpath}"/>
</classpath>
@@ -429,8 +439,8 @@ 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"
- verbose="${testng.verbose}" haltonfailure="true" useDefaultListeners="false" listeners="${testng.listeners}" workingDir="${basedir}">
+ <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}"/>
<jvmarg line="${debug.test.jvmargs}"/>
@@ -441,6 +451,7 @@ grant codeBase "file:/${basedir}/test/script/markdown.js" {
<propertyref prefix="test-sys-prop."/>
<mapper from="test-sys-prop.*" to="*" type="glob"/>
</propertyset>
+ <sysproperty key="optimistic.override" value="${optimistic}"/>
<sysproperty key="test.js.excludes.file" value="${exclude.list}"/>
<classpath>
<pathelement path="${run.test.classpath}"/>
@@ -448,7 +459,31 @@ grant codeBase "file:/${basedir}/test/script/markdown.js" {
</testng>
</target>
- <target name="test" depends="jar, -test-classes-all,-test-classes-single, check-testng, check-external-tests, compile-test, generate-policy-file, -test-security, -test-nosecurity" if="testng.available"/>
+ <target name="test" depends="test-pessimistic, test-optimistic"/>
+
+ <target name="test-optimistic" depends="jar, -test-classes-all,-test-classes-single, check-testng, check-external-tests, compile-test, generate-policy-file" if="testng.available">
+ <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>
+
+ <target name="test-pessimistic" depends="jar, -test-classes-all,-test-classes-single, check-testng, check-external-tests, compile-test, generate-policy-file" if="testng.available">
+ <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>
<target name="check-jemmy.jfx.testng" unless="jemmy.jfx.testng.available">
<echo message="WARNING: Jemmy or JavaFX or TestNG not available, will not run tests. Please copy testng.jar, JemmyCore.jar, JemmyFX.jar, JemmyAWTInput.jar under test${file.separator}lib directory. And make sure you have jfxrt.jar in ${java.home}${file.separator}lib${file.separator}ext dir."/>
diff --git a/make/project.properties b/make/project.properties
index 800a6295..9d36fbdc 100644
--- a/make/project.properties
+++ b/make/project.properties
@@ -286,7 +286,8 @@ run.test.jvmargs.common=\
# turn on assertions for tests
run.test.jvmargs.main=${run.test.jvmargs.common} -ea
-# extra jvmargs that might be useful for debugging
+# Extra jvmargs that might be useful for debugging
+# and performance improvements/monitoring
#
# -XX:+UnlockDiagnosticVMOptions
#
@@ -304,9 +305,25 @@ run.test.jvmargs.main=${run.test.jvmargs.common} -ea
#
# print all compiled nmethods with oopmaps and lots of other info
# -XX:+PrintNMethods
+#
+# activate the generic "UseNewCode" flag to test whatever functionality
+# lies behind it. This is the preferred way to test a, yet flagless,
+# feature in HotSpot - for example, the uncommon trap placement fix
+# was hidden behind this flag before it became the default
+#
+# -XX:+UnlockDiagnosticVMOptions -XX:+UseNewCode
+#
+# Crank up the type profile level to 222, which has some warmup
+# penalties, but produces much better code for JavaScript, where better
+# and more intrusive type profiling is required to get rid of
+# a large amount of unnecessary guard code, that could not otherwise
+# be eliminated
+#
+# -XX:TypeProfileLevel=222
+#
# Use best known performance options for octane
-run.test.jvmargs.octane.main=${run.test.jvmargs.common} -XX:+UnlockDiagnosticVMOptions -XX:+UseNewCode -XX:TypeProfileLevel=222
+run.test.jvmargs.octane.main=${run.test.jvmargs.common} -XX:TypeProfileLevel=222
# Security manager args - make sure that we run with the nashorn.policy that the build creates
run.test.jvmsecurityargs=-Xverify:all -Djava.security.manager -Djava.security.policy=${build.dir}/nashorn.policy
diff --git a/samples/BufferArray.java b/samples/BufferArray.java
index 00d66023..b26b5c08 100644
--- a/samples/BufferArray.java
+++ b/samples/BufferArray.java
@@ -29,8 +29,8 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-import jdk.nashorn.api.scripting.AbstractJSObject;
import java.nio.DoubleBuffer;
+import jdk.nashorn.api.scripting.AbstractJSObject;
/**
* Simple class demonstrating pluggable script object
@@ -52,41 +52,49 @@ public class BufferArray extends AbstractJSObject {
// underlying nio buffer
private final DoubleBuffer buf;
- public BufferArray(int size) {
+ /**
+ * Constructor
+ * @param size initial size
+ */
+ public BufferArray(final int size) {
buf = DoubleBuffer.allocate(size);
}
- public BufferArray(DoubleBuffer buf) {
+ /**
+ * Constructur
+ * @param buf {@link DoubleBuffer} to link to
+ */
+ public BufferArray(final DoubleBuffer buf) {
this.buf = buf;
}
// called to check if indexed property exists
@Override
- public boolean hasSlot(int index) {
+ public boolean hasSlot(final int index) {
return index > 0 && index < buf.capacity();
}
// get the value from that index
@Override
- public Object getSlot(int index) {
+ public Object getSlot(final int index) {
return buf.get(index);
}
// set the value at that index
@Override
- public void setSlot(int index, Object value) {
+ public void setSlot(final int index, final Object value) {
buf.put(index, ((Number)value).doubleValue());
}
// do you have a property of that given name?
@Override
- public boolean hasMember(String name) {
+ public boolean hasMember(final String name) {
return "length".equals(name) || "buf".equals(name);
}
// get the value of that named property
@Override
- public Object getMember(String name) {
+ public Object getMember(final String name) {
switch (name) {
case "length":
return buf.capacity();
@@ -94,7 +102,7 @@ public class BufferArray extends AbstractJSObject {
// return a 'function' value for this property
return new AbstractJSObject() {
@Override
- public Object call(Object thiz, Object... args) {
+ public Object call(final Object thiz, final Object... args) {
return BufferArray.this.buf;
}
@@ -104,6 +112,8 @@ public class BufferArray extends AbstractJSObject {
return true;
}
};
+ default:
+ break;
}
return null;
}
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 d1668894..cdfe34f2 100644
--- a/src/jdk/nashorn/internal/codegen/ApplySpecialization.java
+++ b/src/jdk/nashorn/internal/codegen/ApplySpecialization.java
@@ -35,11 +35,11 @@ import java.util.Deque;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
-
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;
@@ -131,11 +131,12 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
@SuppressWarnings("serial")
final UnsupportedOperationException uoe = new UnsupportedOperationException() {
@Override
- public Throwable fillInStackTrace() {
+ public synchronized Throwable fillInStackTrace() {
return null;
}
};
+ final Set<Expression> argumentsFound = new HashSet<>();
final Deque<Set<Expression>> stack = new ArrayDeque<>();
//ensure that arguments is only passed as arg to apply
try {
@@ -145,7 +146,11 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
}
private boolean isArguments(final Expression expr) {
- return expr instanceof IdentNode && ARGUMENTS.equals(((IdentNode)expr).getName());
+ if (expr instanceof IdentNode && ARGUMENTS.equals(((IdentNode)expr).getName())) {
+ argumentsFound.add(expr);
+ return true;
+ }
+ return false;
}
private boolean isParam(final String name) {
@@ -159,7 +164,7 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
@Override
public Node leaveIdentNode(final IdentNode identNode) {
- if (isParam(identNode.getName()) || ARGUMENTS.equals(identNode.getName()) && !isCurrentArg(identNode)) {
+ if (isParam(identNode.getName()) || isArguments(identNode) && !isCurrentArg(identNode)) {
throw uoe; //avoid filling in stack trace
}
return identNode;
@@ -186,7 +191,9 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
}
});
} catch (final UnsupportedOperationException e) {
- log.fine("'arguments' escapes, is not used in standard call dispatch, or is reassigned in '" + functionNode.getName() + "'. Aborting");
+ if (!argumentsFound.isEmpty()) {
+ log.fine("'arguments' is used but escapes, or is reassigned in '" + functionNode.getName() + "'. Aborting");
+ }
return true; //bad
}
@@ -267,9 +274,9 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
return false;
}
- if (!Global.instance().isSpecialNameValid("apply")) {
+ if (!Global.isBuiltinFunctionPrototypeApply()) {
log.fine("Apply transform disabled: apply/call overridden");
- assert !Global.instance().isSpecialNameValid("call") : "call and apply should have the same SwitchPoint";
+ assert !Global.isBuiltinFunctionPrototypeCall() : "call and apply should have the same SwitchPoint";
return false;
}
@@ -316,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 9f7fe79d..80bb61dd 100644
--- a/src/jdk/nashorn/internal/codegen/ClassEmitter.java
+++ b/src/jdk/nashorn/internal/codegen/ClassEmitter.java
@@ -56,16 +56,15 @@ import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.security.AccessController;
import java.security.PrivilegedAction;
+import java.util.Collections;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;
-
import jdk.internal.org.objectweb.asm.ClassWriter;
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;
@@ -160,8 +159,12 @@ public class ClassEmitter implements Emitter {
this.methodNames = new HashSet<>();
}
+ /**
+ * Return the method names encountered
+ * @return method names
+ */
public Set<String> getMethodNames() {
- return methodNames;
+ return Collections.unmodifiableSet(methodNames);
}
/**
@@ -473,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 97b26212..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,11 +97,12 @@ 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;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LexicalContextNode;
@@ -120,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;
@@ -492,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
@@ -553,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
@@ -573,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;
@@ -632,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) {
@@ -663,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);
@@ -1047,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());
@@ -1204,17 +1229,21 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
@Override
public boolean enterBreakNode(final BreakNode breakNode) {
+ return enterJumpStatement(breakNode);
+ }
+
+ private boolean enterJumpStatement(final JumpStatement jump) {
if(!method.isReachable()) {
return false;
}
- enterStatement(breakNode);
+ enterStatement(jump);
- method.beforeJoinPoint(breakNode);
- final BreakableNode breakFrom = lc.getBreakable(breakNode.getLabelName());
- popScopesUntil(breakFrom);
- final Label breakLabel = breakFrom.getBreakLabel();
- breakLabel.markAsBreakTarget();
- method.splitAwareGoto(lc, breakLabel, breakFrom);
+ method.beforeJoinPoint(jump);
+ final BreakableNode target = jump.getTarget(lc);
+ popScopesUntil(target);
+ final Label targetLabel = jump.getTargetLabel(target);
+ targetLabel.markAsBreakTarget();
+ method._goto(targetLabel);
return false;
}
@@ -1517,19 +1546,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
@Override
public boolean enterContinueNode(final ContinueNode continueNode) {
- if(!method.isReachable()) {
- return false;
- }
- enterStatement(continueNode);
- method.beforeJoinPoint(continueNode);
-
- final LoopNode continueTo = lc.getContinueTo(continueNode.getLabelName());
- popScopesUntil(continueTo);
- final Label continueLabel = continueTo.getContinueLabel();
- continueLabel.markAsBreakTarget();
- method.splitAwareGoto(lc, continueLabel, continueTo);
-
- return false;
+ return enterJumpStatement(continueNode);
}
@Override
@@ -2036,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() {
@@ -2086,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]);
@@ -2707,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);
@@ -2789,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 List<BreakableNode> targetNodes = splitMethod.getExternalTargetNodes();
- final Type returnType = lc.getCurrentFunction().getReturnType();
-
- try {
- // Wrap up this method.
-
- if(method.isReachable()) {
- 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;
- }
-
- // Handle return from split method if there was one.
- final MethodEmitter caller = method;
- final int targetCount = targets.size();
-
- //no external jump targets or return in switch node
- if (!hasReturn && targets.isEmpty()) {
- return splitNode;
- }
-
- caller.loadCompilerConstant(SCOPE);
- caller.checkcast(Scope.class);
- 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 {
- // Clear split state.
- caller.loadCompilerConstant(SCOPE);
- caller.checkcast(Scope.class);
- caller.load(-1);
- caller.invoke(Scope.SET_SPLIT_STATE);
- final BreakableNode targetNode = targetNodes.get(i - 1);
- final Label label = targets.get(i - 1);
- final JoinPredecessor jumpOrigin = splitNode.getJumpOrigin(label);
- if(jumpOrigin != null) {
- method.beforeJoinPoint(jumpOrigin);
- }
- popScopesUntil(targetNode);
- caller.splitAwareGoto(lc, targets.get(i - 1), 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
@@ -3685,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
@@ -3802,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() {
@@ -3830,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();
}
}
@@ -3953,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.
@@ -3970,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
@@ -4386,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 603157d6..2a1d2661 100644
--- a/src/jdk/nashorn/internal/codegen/CompilationPhase.java
+++ b/src/jdk/nashorn/internal/codegen/CompilationPhase.java
@@ -40,25 +40,19 @@ import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SYMBOLS_ASSI
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;
-
import jdk.nashorn.internal.AssertsEnabled;
import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
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;
@@ -83,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
@@ -106,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
@@ -120,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,
@@ -146,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
@@ -179,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);
@@ -187,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());
@@ -200,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,
@@ -210,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
@@ -230,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
@@ -252,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);
}
@@ -276,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();
@@ -332,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;
@@ -352,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
@@ -445,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) {
@@ -617,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);
@@ -627,7 +652,7 @@ enum CompilationPhase {
/**
* Start a compilation phase
- * @param compiler
+ * @param compiler the compiler to use
* @param functionNode function to compile
* @return function node
*/
@@ -703,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 d45a58f1..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;
@@ -60,6 +67,10 @@ public final class CompileUnit implements Comparable<CompileUnit> {
emittedUnitCount++;
}
+ /**
+ * Get the amount of emitted compile units so far in the system
+ * @return emitted compile unit count
+ */
public static int getEmittedUnitCount() {
return emittedUnitCount;
}
@@ -72,6 +83,10 @@ public final class CompileUnit implements Comparable<CompileUnit> {
return isUsed;
}
+ /**
+ * Check if a compile unit has code, not counting inits and clinits
+ * @return true of if there is "real code" in the compile unit
+ */
public boolean hasCode() {
return (classEmitter.getMethodCount() - classEmitter.getInitCount() - classEmitter.getClinitCount()) > 0;
}
@@ -114,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
@@ -147,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 0a1de709..196862f0 100644
--- a/src/jdk/nashorn/internal/codegen/Compiler.java
+++ b/src/jdk/nashorn/internal/codegen/Compiler.java
@@ -35,13 +35,13 @@ 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;
@@ -154,118 +154,153 @@ public final class Compiler implements Loggable {
private RecompilableScriptFunctionData compiledFunction;
/**
- * Compilation phases that a compilation goes through
+ * Most compile unit names are longer than the default StringBuilder buffer,
+ * worth startup performance when massive class generation is going on to increase
+ * this
*/
- public static class CompilationPhases implements Iterable<CompilationPhase> {
+ private static final int COMPILE_UNIT_NAME_BUFFER_SIZE = 32;
- /** 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
- });
+ private final Map<Integer, byte[]> serializedAsts = new HashMap<>();
- /** 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);
+ /**
+ * Compilation phases that a compilation goes through
+ */
+ public static class CompilationPhases implements Iterable<CompilationPhase> {
- /** 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) {
@@ -278,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() {
@@ -404,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('/').
@@ -452,12 +506,16 @@ public final class Compiler implements Loggable {
@Override
public DebugLogger initLogger(final Context ctxt) {
+ final boolean optimisticTypes = env._optimistic_types;
+ final boolean lazyCompilation = env._lazy_compilation;
+
return ctxt.getLogger(this.getClass(), new Consumer<DebugLogger>() {
@Override
public void accept(final DebugLogger newLogger) {
- if (!Compiler.this.getScriptEnvironment()._lazy_compilation) {
+ if (!lazyCompilation) {
newLogger.warning("WARNING: Running with lazy compilation switched off. This is not a default setting.");
}
+ newLogger.warning("Optimistic types are ", optimisticTypes ? "ENABLED." : "DISABLED.");
}
});
}
@@ -535,9 +593,10 @@ public final class Compiler implements Loggable {
* @throws CompilationException if error occurs during compilation
*/
public FunctionNode compile(final FunctionNode functionNode, final CompilationPhases phases) throws CompilationException {
-
- log.finest("Starting compile job for ", DebugLogger.quote(functionNode.getName()), " phases=", quote(phases.getDesc()));
- log.indent();
+ if (log.isEnabled()) {
+ log.info(">> Starting compile job for ", DebugLogger.quote(functionNode.getName()), " phases=", quote(phases.getDesc()));
+ log.indent();
+ }
final String name = DebugLogger.quote(functionNode.getName());
@@ -554,7 +613,7 @@ public final class Compiler implements Loggable {
long time = 0L;
for (final CompilationPhase phase : phases) {
- log.fine(phase, " starting for ", quote(name));
+ log.fine(phase, " starting for ", name);
try {
newFunctionNode = phase.apply(this, phases, newFunctionNode);
@@ -582,8 +641,11 @@ public final class Compiler implements Loggable {
log.unindent();
if (info) {
- final StringBuilder sb = new StringBuilder();
- sb.append("Compile job for ").append(newFunctionNode.getSource()).append(':').append(quote(newFunctionNode.getName())).append(" finished");
+ final StringBuilder sb = new StringBuilder("<< Finished compile job for ");
+ sb.append(newFunctionNode.getSource()).
+ append(':').
+ append(quote(newFunctionNode.getName()));
+
if (time > 0L && timeLogger != null) {
assert env.isTimingEnabled();
sb.append(" in ").append(time).append(" ms");
@@ -631,7 +693,8 @@ public final class Compiler implements Loggable {
}
String nextCompileUnitName() {
- final StringBuilder sb = new StringBuilder(firstCompileUnitName);
+ final StringBuilder sb = new StringBuilder(COMPILE_UNIT_NAME_BUFFER_SIZE);
+ sb.append(firstCompileUnitName);
final int cuid = nextCompileUnitId.getAndIncrement();
if (cuid > 0) {
sb.append("$cu").append(cuid);
@@ -715,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)) {
@@ -737,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 86304690..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
@@ -590,8 +593,13 @@ public final class Label {
return label.getOffset() > other.label.getOffset();
}
+ private String str;
+
@Override
public String toString() {
- return name + '_' + id;
+ if (str == null) {
+ str = name + '_' + id;
+ }
+ return str;
}
}
diff --git a/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java b/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java
index c9580d41..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.
@@ -464,36 +460,20 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
@Override
public boolean enterBreakNode(final BreakNode breakNode) {
- if(!reachable) {
- return false;
- }
-
- final BreakableNode target = lc.getBreakable(breakNode.getLabelName());
- return splitAwareJumpToLabel(breakNode, target, target.getBreakLabel());
+ return enterJumpStatement(breakNode);
}
@Override
public boolean enterContinueNode(final ContinueNode continueNode) {
- if(!reachable) {
- return false;
- }
- final LoopNode target = lc.getContinueTo(continueNode.getLabelName());
- return splitAwareJumpToLabel(continueNode, target, target.getContinueLabel());
+ return enterJumpStatement(continueNode);
}
- 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;
+ private boolean enterJumpStatement(final JumpStatement jump) {
+ if(!reachable) {
+ return false;
}
-
- jumpToLabel(jumpOrigin, targetLabel, getBreakTargetTypes(target));
+ final BreakableNode target = jump.getTarget(lc);
+ jumpToLabel(jump, jump.getTargetLabel(target), getBreakTargetTypes(target));
doesNotContinueSequentially();
return false;
}
@@ -704,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
@@ -1117,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 2c87f3b9..724c6f1e 100644
--- a/src/jdk/nashorn/internal/codegen/Lower.java
+++ b/src/jdk/nashorn/internal/codegen/Lower.java
@@ -52,6 +52,7 @@ import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.IfNode;
+import jdk.nashorn.internal.ir.JumpStatement;
import jdk.nashorn.internal.ir.LabelNode;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LiteralNode;
@@ -351,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<>();
@@ -382,12 +381,16 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
@Override
public Node leaveBreakNode(final BreakNode breakNode) {
- return copy(breakNode, (Node)Lower.this.lc.getBreakable(breakNode.getLabelName()));
+ return leaveJumpStatement(breakNode);
}
@Override
public Node leaveContinueNode(final ContinueNode continueNode) {
- return copy(continueNode, Lower.this.lc.getContinueTo(continueNode.getLabelName()));
+ return leaveJumpStatement(continueNode);
+ }
+
+ private Node leaveJumpStatement(final JumpStatement jump) {
+ return copy(jump, (Node)jump.getTarget(Lower.this.lc));
}
@Override
@@ -401,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;
}
@@ -627,7 +629,7 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
@Override
public Node leaveContinueNode(final ContinueNode node) {
// all inner loops have been popped.
- if (lex.contains(lex.getContinueTo(node.getLabelName()))) {
+ if (lex.contains(node.getTarget(lex))) {
escapes.add(node);
}
return node;
diff --git a/src/jdk/nashorn/internal/codegen/MethodEmitter.java b/src/jdk/nashorn/internal/codegen/MethodEmitter.java
index 2bd4c4c5..87bb297f 100644
--- a/src/jdk/nashorn/internal/codegen/MethodEmitter.java
+++ b/src/jdk/nashorn/internal/codegen/MethodEmitter.java
@@ -89,23 +89,24 @@ 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;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.RewriteException;
+import jdk.nashorn.internal.runtime.Scope;
import jdk.nashorn.internal.runtime.ScriptObject;
+import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.UnwarrantedOptimismException;
import jdk.nashorn.internal.runtime.linker.Bootstrap;
import jdk.nashorn.internal.runtime.logging.DebugLogger;
@@ -180,9 +181,6 @@ public class MethodEmitter implements Emitter {
/** Bootstrap for array populators */
private static final Handle POPULATE_ARRAY_BOOTSTRAP = new Handle(H_INVOKESTATIC, RewriteException.BOOTSTRAP.className(), RewriteException.BOOTSTRAP.name(), RewriteException.BOOTSTRAP.descriptor());
- /** Bootstrap for global name invalidation */
- private static final Handle INVALIDATE_NAME_BOOTSTRAP = new Handle(H_INVOKESTATIC, Global.BOOTSTRAP.className(), Global.BOOTSTRAP.name(), Global.BOOTSTRAP.descriptor());
-
/**
* Constructor - internal use from ClassEmitter only
* @see ClassEmitter#method
@@ -1050,6 +1048,14 @@ public class MethodEmitter implements Emitter {
return load(getCompilerConstantSymbol(cc), type != null ? type : getCompilerConstantType(cc));
}
+ MethodEmitter loadScope() {
+ return loadCompilerConstant(SCOPE).checkcast(Scope.class);
+ }
+
+ MethodEmitter setSplitState(final int state) {
+ return loadScope().load(state).invoke(Scope.SET_SPLIT_STATE);
+ }
+
void storeCompilerConstant(final CompilerConstants cc) {
storeCompilerConstant(cc, null);
}
@@ -1657,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
@@ -2120,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++) {
@@ -2131,10 +2131,15 @@ public class MethodEmitter implements Emitter {
}
MethodEmitter invalidateSpecialName(final String name) {
- //this is a nop if the global hasn't registered this as a special name - we can just ignore it
- if (Global.instance().isSpecialName(name)) {
- debug("dynamic_invalidate_name", "name=", name);
- method.visitInvokeDynamicInsn(name, "()V", INVALIDATE_NAME_BOOTSTRAP);
+ switch (name) {
+ case "apply":
+ case "call":
+ debug("invalidate_name", "name=", name);
+ load("Function");
+ invoke(ScriptRuntime.INVALIDATE_RESERVED_BUILTIN_NAME);
+ break;
+ default:
+ break;
}
return this;
}
@@ -2575,12 +2580,55 @@ public class MethodEmitter implements Emitter {
*
* @param args debug information to print
*/
+ @SuppressWarnings("unused")
private void debug(final Object... args) {
if (debug) {
debug(30, args);
}
}
+ private void debug(final String arg) {
+ if (debug) {
+ debug(30, arg);
+ }
+ }
+
+ private void debug(final Object arg0, final Object arg1) {
+ if (debug) {
+ debug(30, new Object[] { arg0, arg1 });
+ }
+ }
+
+ private void debug(final Object arg0, final Object arg1, final Object arg2) {
+ if (debug) {
+ debug(30, new Object[] { arg0, arg1, arg2 });
+ }
+ }
+
+ private void debug(final Object arg0, final Object arg1, final Object arg2, final Object arg3) {
+ if (debug) {
+ debug(30, new Object[] { arg0, arg1, arg2, arg3 });
+ }
+ }
+
+ private void debug(final Object arg0, final Object arg1, final Object arg2, final Object arg3, final Object arg4) {
+ if (debug) {
+ debug(30, new Object[] { arg0, arg1, arg2, arg3, arg4 });
+ }
+ }
+
+ private void debug(final Object arg0, final Object arg1, final Object arg2, final Object arg3, final Object arg4, final Object arg5) {
+ if (debug) {
+ debug(30, new Object[] { arg0, arg1, arg2, arg3, arg4, arg5 });
+ }
+ }
+
+ private void debug(final Object arg0, final Object arg1, final Object arg2, final Object arg3, final Object arg4, final Object arg5, final Object arg6) {
+ if (debug) {
+ debug(30, new Object[] { arg0, arg1, arg2, arg3, arg4, arg5, arg6 });
+ }
+ }
+
/**
* Debug function that outputs generated bytecode and stack contents
* for a label - indentation is currently the only thing that differs
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/OptimisticTypesPersistence.java b/src/jdk/nashorn/internal/codegen/OptimisticTypesPersistence.java
index 3c835ac5..05a76872 100644
--- a/src/jdk/nashorn/internal/codegen/OptimisticTypesPersistence.java
+++ b/src/jdk/nashorn/internal/codegen/OptimisticTypesPersistence.java
@@ -429,7 +429,6 @@ public final class OptimisticTypesPersistence {
}
private static void doCleanup() throws IOException {
- final long start = System.nanoTime();
final Path[] files = getAllRegularFilesInLastModifiedOrder();
final int nFiles = files.length;
final int filesToDelete = Math.max(0, nFiles - MAX_FILES);
@@ -444,8 +443,7 @@ public final class OptimisticTypesPersistence {
// does not increase filesDeleted
}
files[i] = null; // gc eligible
- };
- final long duration = System.nanoTime() - start;
+ }
}
private static Path[] getAllRegularFilesInLastModifiedOrder() throws IOException {
@@ -456,7 +454,7 @@ public final class OptimisticTypesPersistence {
@Override
public boolean test(final Path path) {
return !Files.isDirectory(path);
- };
+ }
})
.map(new Function<Path, PathAndTime>() {
@Override
@@ -497,7 +495,7 @@ public final class OptimisticTypesPersistence {
private static long getTime(final Path path) {
try {
return Files.getLastModifiedTime(path).toMillis();
- } catch (IOException e) {
+ } catch (final IOException e) {
// All files for which we can't retrieve the last modified date will be considered oldest.
return -1L;
}
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 d5feac57..00000000
--- a/src/jdk/nashorn/internal/codegen/SplitMethodEmitter.java
+++ /dev/null
@@ -1,111 +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 static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
-
-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;
-import jdk.nashorn.internal.runtime.Scope;
-
-/**
- * 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) {
- loadCompilerConstant(SCOPE);
- checkcast(Scope.class);
- load(index + 1);
- invoke(Scope.SET_SPLIT_STATE);
- loadUndefined(Type.OBJECT);
- _return(functionNode.getReturnType());
- return;
- }
- 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();
- loadCompilerConstant(SCOPE);
- checkcast(Scope.class);
- load(0);
- invoke(Scope.SET_SPLIT_STATE);
- return this;
- }
-
- 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 9ce94037..332ee55e 100644
--- a/src/jdk/nashorn/internal/codegen/types/Type.java
+++ b/src/jdk/nashorn/internal/codegen/types/Type.java
@@ -51,12 +51,15 @@ 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;
import java.lang.invoke.MethodType;
+import java.util.Collections;
import java.util.Map;
import java.util.TreeMap;
+import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import jdk.internal.org.objectweb.asm.Handle;
@@ -87,23 +90,34 @@ 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;
+ /**
+ * Cache for internal types - this is a query that requires complex stringbuilding inside
+ * ASM and it saves startup time to cache the type mappings
+ */
+ private static final Map<Class<?>, jdk.internal.org.objectweb.asm.Type> INTERNAL_TYPE_CACHE =
+ Collections.synchronizedMap(new WeakHashMap<Class<?>, jdk.internal.org.objectweb.asm.Type>());
+
+ /** Internal ASM type for this Type - computed once at construction */
+ 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;
@@ -122,12 +136,13 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
* @param slots how many bytecode slots the type takes up
*/
Type(final String name, final Class<?> clazz, final int weight, final int slots) {
- this.name = name;
- this.clazz = clazz;
- this.descriptor = jdk.internal.org.objectweb.asm.Type.getDescriptor(clazz);
- this.weight = weight;
+ this.name = name;
+ this.clazz = clazz;
+ this.descriptor = jdk.internal.org.objectweb.asm.Type.getDescriptor(clazz);
+ this.weight = weight;
assert weight >= MIN_WEIGHT && weight <= MAX_WEIGHT : "illegal type weight: " + weight;
- this.slots = slots;
+ this.slots = slots;
+ this.internalType = getInternalType(clazz);
}
/**
@@ -299,7 +314,7 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
*
* @param typeMap the type map
* @param output data output
- * @throws IOException
+ * @throws IOException if write cannot be completed
*/
public static void writeTypeMap(final Map<Integer, Type> typeMap, final DataOutput output) throws IOException {
if (typeMap == null) {
@@ -329,7 +344,7 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
*
* @param input data input
* @return type map
- * @throws IOException
+ * @throws IOException if read cannot be completed
*/
public static Map<Integer, Type> readTypeMap(final DataInput input) throws IOException {
final int size = input.readInt();
@@ -357,11 +372,22 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
}
private jdk.internal.org.objectweb.asm.Type getInternalType() {
- return jdk.internal.org.objectweb.asm.Type.getType(getTypeClass());
+ return internalType;
+ }
+
+ private static jdk.internal.org.objectweb.asm.Type lookupInternalType(final Class<?> type) {
+ final Map<Class<?>, jdk.internal.org.objectweb.asm.Type> cache = INTERNAL_TYPE_CACHE;
+ jdk.internal.org.objectweb.asm.Type itype = cache.get(type);
+ if (itype != null) {
+ return itype;
+ }
+ itype = jdk.internal.org.objectweb.asm.Type.getType(type);
+ cache.put(type, itype);
+ return itype;
}
private static jdk.internal.org.objectweb.asm.Type getInternalType(final Class<?> type) {
- return jdk.internal.org.objectweb.asm.Type.getType(type);
+ return lookupInternalType(type);
}
static void invokestatic(final MethodVisitor method, final Call call) {
@@ -911,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);
@@ -938,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);
@@ -965,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);
@@ -999,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";
@@ -1007,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";
@@ -1018,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);
@@ -1069,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>";
@@ -1085,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() {
@@ -1101,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 c127b1ae..4cbf4c02 100644
--- a/src/jdk/nashorn/internal/ir/BreakNode.java
+++ b/src/jdk/nashorn/internal/ir/BreakNode.java
@@ -25,6 +25,7 @@
package jdk.nashorn.internal.ir;
+import jdk.nashorn.internal.codegen.Label;
import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
@@ -33,6 +34,7 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
*/
@Immutable
public final class BreakNode extends JumpStatement {
+ private static final long serialVersionUID = 1L;
/**
* Constructor
@@ -68,4 +70,14 @@ public final class BreakNode extends JumpStatement {
String getStatementName() {
return "break";
}
+
+ @Override
+ public BreakableNode getTarget(final LexicalContext lc) {
+ return lc.getBreakable(getLabelName());
+ }
+
+ @Override
+ public Label getTargetLabel(final BreakableNode target) {
+ return target.getBreakLabel();
+ }
}
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 d5177db6..c58531bd 100644
--- a/src/jdk/nashorn/internal/ir/ContinueNode.java
+++ b/src/jdk/nashorn/internal/ir/ContinueNode.java
@@ -25,6 +25,7 @@
package jdk.nashorn.internal.ir;
+import jdk.nashorn.internal.codegen.Label;
import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
@@ -33,6 +34,8 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
*/
@Immutable
public class ContinueNode extends JumpStatement {
+ private static final long serialVersionUID = 1L;
+
/**
* Constructor
*
@@ -67,5 +70,15 @@ public class ContinueNode extends JumpStatement {
String getStatementName() {
return "continue";
}
-}
+
+ @Override
+ public BreakableNode getTarget(final LexicalContext lc) {
+ return lc.getContinueTo(getLabelName());
+ }
+
+ @Override
+ public Label getTargetLabel(final BreakableNode target) {
+ 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 b911f222..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,56 +209,52 @@ 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.
* Note that even IS_STRICT is one such flag but that requires special handling.
*/
- // parser, lower debugging this function
- public static final int IS_PRINT_PARSE = 1 << 18;
- public static final int IS_PRINT_LOWER_PARSE = 1 << 19;
- public static final int IS_PRINT_AST = 1 << 20;
- public static final int IS_PRINT_LOWER_AST = 1 << 21;
- public static final int IS_PRINT_SYMBOLS = 1 << 22;
+ /** parser, print parse tree */
+ public static final int IS_PRINT_PARSE = 1 << 17;
+ /** parser, print lower parse tree */
+ public static final int IS_PRINT_LOWER_PARSE = 1 << 18;
+ /** parser, print AST */
+ public static final int IS_PRINT_AST = 1 << 19;
+ /** parser, print lower AST */
+ public static final int IS_PRINT_LOWER_AST = 1 << 20;
+ /** parser, print symbols */
+ 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;
- // callsite tracing, profiling within this function
/** 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
@@ -264,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 |
@@ -281,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;
@@ -337,7 +335,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
private FunctionNode(
final FunctionNode functionNode,
final long lastToken,
- Object endParserState,
+ final Object endParserState,
final int flags,
final String name,
final Type returnType,
@@ -346,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;
@@ -361,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;
}
@@ -431,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
*/
@@ -531,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,
@@ -542,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
@@ -618,7 +673,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
body,
parameters,
thisProperties,
- rootClass));
+ rootClass, source, namespace));
}
@Override
@@ -693,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
@@ -766,7 +814,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
body,
parameters,
thisProperties,
- rootClass));
+ rootClass, source, namespace));
}
/**
@@ -834,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);
}
/**
@@ -862,7 +910,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
body,
parameters,
thisProperties,
- rootClass));
+ rootClass, source, namespace));
}
/**
@@ -923,7 +971,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
body,
parameters,
thisProperties,
- rootClass));
+ rootClass, source, namespace));
}
/**
@@ -958,7 +1006,10 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
compilationState,
body,
parameters,
- thisProperties, rootClass));
+ thisProperties,
+ rootClass,
+ source,
+ namespace));
}
/**
@@ -994,7 +1045,9 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
body,
parameters,
thisProperties,
- rootClass));
+ rootClass,
+ source,
+ namespace));
}
/**
@@ -1008,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);
@@ -1060,7 +1113,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
body,
parameters,
thisProperties,
- rootClass));
+ rootClass, source, namespace));
}
/**
@@ -1139,7 +1192,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
body,
parameters,
thisProperties,
- rootClass
+ rootClass, source, namespace
));
}
@@ -1187,7 +1240,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
body,
parameters,
thisProperties,
- rootClass));
+ rootClass, source, namespace));
}
/**
@@ -1243,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 aabb9de7..07707171 100644
--- a/src/jdk/nashorn/internal/ir/JumpStatement.java
+++ b/src/jdk/nashorn/internal/ir/JumpStatement.java
@@ -25,10 +25,13 @@
package jdk.nashorn.internal.ir;
+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;
@@ -82,6 +85,24 @@ public abstract class JumpStatement extends Statement implements JoinPredecessor
abstract String getStatementName();
+ /**
+ * Finds the target for this jump statement in a lexical context.
+ * @param lc the lexical context
+ * @return the target, or null if not found
+ */
+ public abstract BreakableNode getTarget(final LexicalContext lc);
+
+ /**
+ * Returns the label corresponding to this kind of jump statement (either a break or continue label) in the target.
+ * @param target the target. Note that it need not be the target of this jump statement, as the method can retrieve
+ * a label on any passed target as long as the target has a label of the requisite kind. Of course, it is advisable
+ * to invoke the method on a jump statement that targets the breakable.
+ * @return the label of the target corresponding to the kind of jump statement.
+ * @throws ClassCastException if invoked on the kind of breakable node that this jump statement is not prepared to
+ * handle.
+ */
+ public abstract Label getTargetLabel(final BreakableNode target);
+
@Override
public JumpStatement setLocalVariableConversion(final LexicalContext lc, final LocalVariableConversion conversion) {
if(this.conversion == 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 c6ede946..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;
@@ -237,6 +240,10 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
return value;
}
+ private static Expression[] valueToArray(final List<Expression> value) {
+ return value.toArray(new Expression[value.size()]);
+ }
+
/**
* Create a new null literal
*
@@ -266,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);
}
@@ -300,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);
@@ -352,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);
@@ -414,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);
}
@@ -450,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);
}
@@ -493,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?
}
@@ -556,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);
@@ -586,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;
@@ -603,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;
@@ -651,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) {
@@ -981,10 +1002,9 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
* @return the new literal node
*/
public static LiteralNode<Expression[]> newInstance(final long token, final int finish, final List<Expression> value) {
- return new ArrayLiteralNode(token, finish, value.toArray(new Expression[value.size()]));
+ return new ArrayLiteralNode(token, finish, valueToArray(value));
}
-
/**
* Create a new array literal based on a parent node (source, token, finish)
*
@@ -994,7 +1014,7 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
* @return the new literal node
*/
public static LiteralNode<?> newInstance(final Node parent, final List<Expression> value) {
- return new ArrayLiteralNode(parent.getToken(), parent.getFinish(), value.toArray(new Expression[value.size()]));
+ return new ArrayLiteralNode(parent.getToken(), parent.getFinish(), valueToArray(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 c6e4a50f..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)));
@@ -448,14 +435,25 @@ public final class Symbol implements Comparable<Symbol> {
return (flags & IS_FUNCTION_SELF) != 0;
}
+ /**
+ * Is this a block scoped symbol
+ * @return true if block scoped
+ */
public boolean isBlockScoped() {
return isLet() || isConst();
}
+ /**
+ * Has this symbol been declared
+ * @return true if declared
+ */
public boolean hasBeenDeclared() {
return (flags & HAS_BEEN_DECLARED) != 0;
}
+ /**
+ * Mark this symbol as declared
+ */
public void setHasBeenDeclared() {
if (!hasBeenDeclared()) {
flags |= HAS_BEEN_DECLARED;
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/lookup/MethodHandleFactory.java b/src/jdk/nashorn/internal/lookup/MethodHandleFactory.java
index 8ef13d79..8e5120f2 100644
--- a/src/jdk/nashorn/internal/lookup/MethodHandleFactory.java
+++ b/src/jdk/nashorn/internal/lookup/MethodHandleFactory.java
@@ -48,7 +48,7 @@ import jdk.nashorn.internal.runtime.options.Options;
/**
* This class is abstraction for all method handle, switchpoint and method type
* operations. This enables the functionality interface to be subclassed and
- * intrumensted, as it has been proven vital to keep the number of method
+ * instrumented, as it has been proven vital to keep the number of method
* handles in the system down.
*
* All operations of the above type should go through this class, and not
@@ -116,6 +116,10 @@ public final class MethodHandleFactory {
private static final String VOID_TAG = "[VOID]";
+ private static void err(final String str) {
+ Context.getContext().getErr().println(str);
+ }
+
/**
* Tracer that is applied before a value is returned from the traced function. It will output the return
* value and its class
@@ -124,13 +128,16 @@ public final class MethodHandleFactory {
* @return return value unmodified
*/
static Object traceReturn(final DebugLogger logger, final Object value) {
- if (logger.isEnabled()) {
- final String str = " return" +
- (VOID_TAG.equals(value) ?
- ";" :
- " " + stripName(value) + "; // [type=" + (value == null ? "null]" : stripName(value.getClass()) + ']'));
+ final String str = " return" +
+ (VOID_TAG.equals(value) ?
+ ";" :
+ " " + stripName(value) + "; // [type=" + (value == null ? "null]" : stripName(value.getClass()) + ']'));
+ if (logger == null) {
+ err(str);
+ } else if (logger.isEnabled()) {
logger.log(TRACE_LEVEL, str);
}
+
return value;
}
@@ -169,8 +176,11 @@ public final class MethodHandleFactory {
}
}
- assert logger != null;
- logger.log(TRACE_LEVEL, sb);
+ if (logger == null) {
+ err(sb.toString());
+ } else {
+ logger.log(TRACE_LEVEL, sb);
+ }
stacktrace(logger);
}
@@ -181,7 +191,12 @@ public final class MethodHandleFactory {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final PrintStream ps = new PrintStream(baos);
new Throwable().printStackTrace(ps);
- logger.log(TRACE_LEVEL, baos.toString());
+ final String st = baos.toString();
+ if (logger == null) {
+ err(st);
+ } else {
+ logger.log(TRACE_LEVEL, st);
+ }
}
private static String argString(final Object arg) {
@@ -201,7 +216,7 @@ public final class MethodHandleFactory {
if (arg instanceof ScriptObject) {
return arg.toString() +
" (map=" + Debug.id(((ScriptObject)arg).getMap()) +
- ")";
+ ')';
}
return arg.toString();
@@ -209,6 +224,18 @@ public final class MethodHandleFactory {
/**
* Add a debug printout to a method handle, tracing parameters and return values
+ * Output will be unconditional to stderr
+ *
+ * @param mh method handle to trace
+ * @param tag start of trace message
+ * @return traced method handle
+ */
+ public static MethodHandle addDebugPrintout(final MethodHandle mh, final Object tag) {
+ return addDebugPrintout(null, Level.OFF, mh, 0, true, tag);
+ }
+
+ /**
+ * Add a debug printout to a method handle, tracing parameters and return values
*
* @param logger a specific logger to which to write the output
* @param level level over which to print
@@ -222,6 +249,20 @@ public final class MethodHandleFactory {
/**
* Add a debug printout to a method handle, tracing parameters and return values
+ * Output will be unconditional to stderr
+ *
+ * @param mh method handle to trace
+ * @param paramStart first param to print/trace
+ * @param printReturnValue should we print/trace return value if available?
+ * @param tag start of trace message
+ * @return traced method handle
+ */
+ public static MethodHandle addDebugPrintout(final MethodHandle mh, final int paramStart, final boolean printReturnValue, final Object tag) {
+ return addDebugPrintout(null, Level.OFF, mh, paramStart, printReturnValue, tag);
+ }
+
+ /**
+ * Add a debug printout to a method handle, tracing parameters and return values
*
* @param logger a specific logger to which to write the output
* @param level level over which to print
@@ -240,7 +281,6 @@ public final class MethodHandleFactory {
return mh;
}
- assert logger != null;
assert TRACE != null;
MethodHandle trace = MethodHandles.insertArguments(TRACE, 0, logger, tag, paramStart);
@@ -428,6 +468,12 @@ public final class MethodHandleFactory {
}
@Override
+ public MethodHandle identity(final Class<?> type) {
+ final MethodHandle mh = MethodHandles.identity(type);
+ return debug(mh, "identity", type);
+ }
+
+ @Override
public MethodHandle asCollector(final MethodHandle handle, final Class<?> arrayType, final int arrayLength) {
final MethodHandle mh = handle.asCollector(arrayType, arrayLength);
return debug(mh, "asCollector", handle, arrayType, arrayLength);
diff --git a/src/jdk/nashorn/internal/lookup/MethodHandleFunctionality.java b/src/jdk/nashorn/internal/lookup/MethodHandleFunctionality.java
index 756c0b7b..beaa85c2 100644
--- a/src/jdk/nashorn/internal/lookup/MethodHandleFunctionality.java
+++ b/src/jdk/nashorn/internal/lookup/MethodHandleFunctionality.java
@@ -173,6 +173,15 @@ public interface MethodHandleFunctionality {
public MethodHandle constant(Class<?> type, Object value);
/**
+ * Wrapper for {@link java.lang.invoke.MethodHandles#identity(Class)}
+ *
+ * @param type type of value
+ *
+ * @return method handle that returns identity argument
+ */
+ public MethodHandle identity(Class<?> type);
+
+ /**
* Wrapper for {@link java.lang.invoke.MethodHandle#asType(MethodType)}
*
* @param handle method handle for type conversion
diff --git a/src/jdk/nashorn/internal/objects/ArrayBufferView.java b/src/jdk/nashorn/internal/objects/ArrayBufferView.java
index 93506c37..e33fac3b 100644
--- a/src/jdk/nashorn/internal/objects/ArrayBufferView.java
+++ b/src/jdk/nashorn/internal/objects/ArrayBufferView.java
@@ -45,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/Global.java b/src/jdk/nashorn/internal/objects/Global.java
index b8ec90fd..eb56de0e 100644
--- a/src/jdk/nashorn/internal/objects/Global.java
+++ b/src/jdk/nashorn/internal/objects/Global.java
@@ -25,23 +25,19 @@
package jdk.nashorn.internal.objects;
-import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
-
import java.io.IOException;
import java.io.PrintWriter;
-import java.lang.invoke.CallSite;
-import java.lang.invoke.ConstantCallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
-import java.lang.invoke.MethodType;
import java.lang.invoke.SwitchPoint;
import java.lang.reflect.Field;
+import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
@@ -52,8 +48,6 @@ import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
import jdk.nashorn.api.scripting.ClassFilter;
import jdk.nashorn.api.scripting.ScriptObjectMirror;
-import jdk.nashorn.internal.codegen.ApplySpecialization;
-import jdk.nashorn.internal.codegen.CompilerConstants.Call;
import jdk.nashorn.internal.lookup.Lookup;
import jdk.nashorn.internal.objects.annotations.Attribute;
import jdk.nashorn.internal.objects.annotations.Property;
@@ -72,6 +66,7 @@ import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.ScriptingFunctions;
+import jdk.nashorn.internal.runtime.Specialization;
import jdk.nashorn.internal.runtime.arrays.ArrayData;
import jdk.nashorn.internal.runtime.linker.Bootstrap;
import jdk.nashorn.internal.runtime.linker.InvokeByName;
@@ -107,10 +102,6 @@ public final class Global extends ScriptObject implements Scope {
* it's when you start adding property checks for said builtins you have
* problems with guard speed.
*/
- public final Map<String, SwitchPoint> optimisticFunctionMap;
-
- /** Name invalidator for things like call/apply */
- public static final Call BOOTSTRAP = staticCall(MethodHandles.lookup(), Global.class, "invalidateNameBootstrap", CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class);
/** Nashorn extension: arguments array */
@Property(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_CONFIGURABLE)
@@ -428,9 +419,6 @@ public final class Global extends ScriptObject implements Scope {
private static final MethodHandle LOADWITHNEWGLOBAL = findOwnMH_S("loadWithNewGlobal", Object.class, Object.class, Object[].class);
private static final MethodHandle EXIT = findOwnMH_S("exit", Object.class, Object.class, Object.class);
- /** Invalidate a reserved name, such as "apply" or "call" if assigned */
- public MethodHandle INVALIDATE_RESERVED_NAME = MH.bindTo(findOwnMH_V("invalidateReservedName", void.class, String.class), this);
-
// initialized by nasgen
private static PropertyMap $nasgenmap$;
@@ -482,7 +470,6 @@ public final class Global extends ScriptObject implements Scope {
super(checkAndGetMap(context));
this.context = context;
this.setIsScope();
- this.optimisticFunctionMap = new HashMap<>();
//we can only share one instance of Global constants between globals, or we consume way too much
//memory - this is good enough for most programs
while (gcsInstance.get() == null) {
@@ -574,6 +561,7 @@ public final class Global extends ScriptObject implements Scope {
*
* @param engine ScriptEngine to initialize
*/
+ @SuppressWarnings("hiding")
public void initBuiltinObjects(final ScriptEngine engine) {
if (this.builtinObject != null) {
// already initialized, just return
@@ -918,10 +906,12 @@ public final class Global extends ScriptObject implements Scope {
}
switch (nameStr) {
- case "context":
- return sctxt;
- case "engine":
- return global.engine;
+ case "context":
+ return sctxt;
+ case "engine":
+ return global.engine;
+ default:
+ break;
}
if (self == UNDEFINED) {
@@ -1244,6 +1234,40 @@ public final class Global extends ScriptObject implements Scope {
return instance.function == instance.getBuiltinFunction();
}
+ /**
+ * Get the switchpoint used to check property changes for Function.prototype.apply
+ * @return the switchpoint guarding apply (same as guarding call, and everything else in function)
+ */
+ public static SwitchPoint getBuiltinFunctionApplySwitchPoint() {
+ return ScriptFunction.getPrototype(Global.instance().getBuiltinFunction()).getProperty("apply").getBuiltinSwitchPoint();
+ }
+
+ private static boolean isBuiltinFunctionProperty(final String name) {
+ final Global instance = Global.instance();
+ final ScriptFunction builtinFunction = instance.getBuiltinFunction();
+ if (builtinFunction == null) {
+ return false; //conservative for compile-only mode
+ }
+ final boolean isBuiltinFunction = instance.function == builtinFunction;
+ return isBuiltinFunction && ScriptFunction.getPrototype(builtinFunction).getProperty(name).isBuiltin();
+ }
+
+ /**
+ * Check if the Function.prototype.apply has not been replaced
+ * @return true if Function.prototype.apply has been replaced
+ */
+ public static boolean isBuiltinFunctionPrototypeApply() {
+ return isBuiltinFunctionProperty("apply");
+ }
+
+ /**
+ * Check if the Function.prototype.apply has not been replaced
+ * @return true if Function.prototype.call has been replaced
+ */
+ public static boolean isBuiltinFunctionPrototypeCall() {
+ return isBuiltinFunctionProperty("call");
+ }
+
private ScriptFunction getBuiltinJSAdapter() {
return builtinJSAdapter;
}
@@ -1688,6 +1712,13 @@ public final class Global extends ScriptObject implements Scope {
splitState = state;
}
+ private <T extends ScriptObject> T initConstructorAndSwitchPoint(final String name, final Class<T> clazz) {
+ final T func = initConstructor(name, clazz);
+ tagBuiltinProperties(name, func);
+ return func;
+ }
+
+ @SuppressWarnings("hiding")
private void init(final ScriptEngine engine) {
assert Context.getGlobal() == this : "this global is not set as current";
@@ -1702,8 +1733,19 @@ public final class Global extends ScriptObject implements Scope {
// initialize global function properties
this.eval = this.builtinEval = ScriptFunctionImpl.makeFunction("eval", EVAL);
- this.parseInt = ScriptFunctionImpl.makeFunction("parseInt", GlobalFunctions.PARSEINT,
- new MethodHandle[] { GlobalFunctions.PARSEINT_OI, GlobalFunctions.PARSEINT_O });
+ this.parseInt = ScriptFunctionImpl.makeFunction("parseInt", GlobalFunctions.PARSEINT,
+ new Specialization[] {
+ new Specialization(GlobalFunctions.PARSEINT_Z),
+ new Specialization(GlobalFunctions.PARSEINT_I),
+ new Specialization(GlobalFunctions.PARSEINT_J),
+ new Specialization(GlobalFunctions.PARSEINT_OI),
+ new Specialization(GlobalFunctions.PARSEINT_O) });
+ this.parseFloat = ScriptFunctionImpl.makeFunction("parseFloat", GlobalFunctions.PARSEFLOAT);
+ this.isNaN = ScriptFunctionImpl.makeFunction("isNaN", GlobalFunctions.IS_NAN,
+ new Specialization[] {
+ new Specialization(GlobalFunctions.IS_NAN_I),
+ new Specialization(GlobalFunctions.IS_NAN_J),
+ new Specialization(GlobalFunctions.IS_NAN_D) });
this.parseFloat = ScriptFunctionImpl.makeFunction("parseFloat", GlobalFunctions.PARSEFLOAT);
this.isNaN = ScriptFunctionImpl.makeFunction("isNaN", GlobalFunctions.IS_NAN);
this.isFinite = ScriptFunctionImpl.makeFunction("isFinite", GlobalFunctions.IS_FINITE);
@@ -1720,15 +1762,15 @@ public final class Global extends ScriptObject implements Scope {
this.quit = ScriptFunctionImpl.makeFunction("quit", EXIT);
// built-in constructors
- this.builtinArray = initConstructor("Array", ScriptFunction.class);
- this.builtinBoolean = initConstructor("Boolean", ScriptFunction.class);
- this.builtinDate = initConstructor("Date", ScriptFunction.class);
- this.builtinJSON = initConstructor("JSON", ScriptObject.class);
- this.builtinJSAdapter = initConstructor("JSAdapter", ScriptFunction.class);
- this.builtinMath = initConstructor("Math", ScriptObject.class);
- this.builtinNumber = initConstructor("Number", ScriptFunction.class);
- this.builtinRegExp = initConstructor("RegExp", ScriptFunction.class);
- this.builtinString = initConstructor("String", ScriptFunction.class);
+ this.builtinArray = initConstructorAndSwitchPoint("Array", ScriptFunction.class);
+ this.builtinBoolean = initConstructorAndSwitchPoint("Boolean", ScriptFunction.class);
+ this.builtinDate = initConstructorAndSwitchPoint("Date", ScriptFunction.class);
+ this.builtinJSON = initConstructorAndSwitchPoint("JSON", ScriptObject.class);
+ this.builtinJSAdapter = initConstructorAndSwitchPoint("JSAdapter", ScriptFunction.class);
+ this.builtinMath = initConstructorAndSwitchPoint("Math", ScriptObject.class);
+ this.builtinNumber = initConstructorAndSwitchPoint("Number", ScriptFunction.class);
+ this.builtinRegExp = initConstructorAndSwitchPoint("RegExp", ScriptFunction.class);
+ this.builtinString = initConstructorAndSwitchPoint("String", ScriptFunction.class);
// initialize String.prototype.length to 0
// add String.prototype.length
@@ -1830,6 +1872,8 @@ public final class Global extends ScriptObject implements Scope {
// Error.prototype.message = "";
errorProto.set(NativeError.MESSAGE, "", 0);
+ tagBuiltinProperties("Error", builtinError);
+
this.builtinEvalError = initErrorSubtype("EvalError", errorProto);
this.builtinRangeError = initErrorSubtype("RangeError", errorProto);
this.builtinReferenceError = initErrorSubtype("ReferenceError", errorProto);
@@ -1844,6 +1888,7 @@ public final class Global extends ScriptObject implements Scope {
prototype.set(NativeError.NAME, name, 0);
prototype.set(NativeError.MESSAGE, "", 0);
prototype.setInitialProto(errorProto);
+ tagBuiltinProperties(name, cons);
return cons;
}
@@ -1910,17 +1955,18 @@ public final class Global extends ScriptObject implements Scope {
}
private void initTypedArray() {
- this.builtinArrayBuffer = initConstructor("ArrayBuffer", ScriptFunction.class);
- this.builtinDataView = initConstructor("DataView", ScriptFunction.class);
- this.builtinInt8Array = initConstructor("Int8Array", ScriptFunction.class);
- this.builtinUint8Array = initConstructor("Uint8Array", ScriptFunction.class);
- this.builtinUint8ClampedArray = initConstructor("Uint8ClampedArray", ScriptFunction.class);
- this.builtinInt16Array = initConstructor("Int16Array", ScriptFunction.class);
- this.builtinUint16Array = initConstructor("Uint16Array", ScriptFunction.class);
- this.builtinInt32Array = initConstructor("Int32Array", ScriptFunction.class);
- this.builtinUint32Array = initConstructor("Uint32Array", ScriptFunction.class);
- this.builtinFloat32Array = initConstructor("Float32Array", ScriptFunction.class);
- this.builtinFloat64Array = initConstructor("Float64Array", ScriptFunction.class);
+ this.builtinArrayBuffer = initConstructorAndSwitchPoint("ArrayBuffer", ScriptFunction.class);
+ this.builtinDataView = initConstructorAndSwitchPoint("DataView", ScriptFunction.class);
+ this.builtinInt8Array = initConstructorAndSwitchPoint("Int8Array", ScriptFunction.class);
+ this.builtinUint8Array = initConstructorAndSwitchPoint("Uint8Array", ScriptFunction.class);
+ this.builtinUint8ClampedArray = initConstructorAndSwitchPoint("Uint8ClampedArray", ScriptFunction.class);
+ this.builtinInt16Array = initConstructorAndSwitchPoint("Int16Array", ScriptFunction.class);
+ this.builtinUint16Array = initConstructorAndSwitchPoint("Uint16Array", ScriptFunction.class);
+ this.builtinInt32Array = initConstructorAndSwitchPoint("Int32Array", ScriptFunction.class);
+ this.builtinUint32Array = initConstructorAndSwitchPoint("Uint32Array", ScriptFunction.class);
+ this.builtinFloat32Array = initConstructorAndSwitchPoint("Float32Array", ScriptFunction.class);
+ this.builtinFloat64Array = initConstructorAndSwitchPoint("Float64Array", ScriptFunction.class);
+
}
private void copyBuiltins() {
@@ -1993,10 +2039,6 @@ public final class Global extends ScriptObject implements Scope {
return UNDEFINED;
}
- /**
- * These classes are generated by nasgen tool and so we have to use
- * reflection to load and create new instance of these classes.
- */
private <T extends ScriptObject> T initConstructor(final String name, final Class<T> clazz) {
try {
// Assuming class name pattern for built-in JS constructors.
@@ -2021,12 +2063,52 @@ public final class Global extends ScriptObject implements Scope {
}
res.setIsBuiltin();
+
return res;
} catch (final ClassNotFoundException | InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
+ private List<jdk.nashorn.internal.runtime.Property> extractBuiltinProperties(final String name, final ScriptObject func) {
+ final List<jdk.nashorn.internal.runtime.Property> list = new ArrayList<>();
+
+ list.addAll(Arrays.asList(func.getMap().getProperties()));
+
+ if (func instanceof ScriptFunction) {
+ final ScriptObject proto = ScriptFunction.getPrototype((ScriptFunction)func);
+ if (proto != null) {
+ list.addAll(Arrays.asList(proto.getMap().getProperties()));
+ }
+ }
+
+ final jdk.nashorn.internal.runtime.Property prop = getProperty(name);
+ if (prop != null) {
+ list.add(prop);
+ }
+
+ return list;
+ }
+
+ /**
+ * Given a builtin object, traverse its properties recursively and associate them with a name that
+ * will be a key to their invalidation switchpoint.
+ * @param name name for key
+ * @param func builtin script object
+ */
+ private void tagBuiltinProperties(final String name, final ScriptObject func) {
+ SwitchPoint sp = context.getBuiltinSwitchPoint(name);
+ if (sp == null) {
+ sp = context.newBuiltinSwitchPoint(name);
+ }
+
+ //get all builtin properties in this builtin object and register switchpoints keyed on the propery name,
+ //one overwrite destroys all for now, e.g. Function.prototype.apply = 17; also destroys Function.prototype.call etc
+ for (final jdk.nashorn.internal.runtime.Property prop : extractBuiltinProperties(name, func)) {
+ prop.setBuiltinSwitchPoint(sp);
+ }
+ }
+
// Function and Object constructors are inter-dependent. Also,
// Function.prototype
// functions are not properly initialized. We fix the references here.
@@ -2035,7 +2117,8 @@ public final class Global extends ScriptObject implements Scope {
// to play with object references carefully!!
private void initFunctionAndObject() {
// First-n-foremost is Function
- this.builtinFunction = initConstructor("Function", ScriptFunction.class);
+
+ this.builtinFunction = initConstructor("Function", ScriptFunction.class);
// create global anonymous function
final ScriptFunction anon = ScriptFunctionImpl.newAnonymousFunction();
@@ -2101,13 +2184,6 @@ public final class Global extends ScriptObject implements Scope {
}
}
- //make sure apply and call have the same invalidation switchpoint
- final SwitchPoint sp = new SwitchPoint();
- optimisticFunctionMap.put("apply", sp);
- optimisticFunctionMap.put("call", sp);
- getFunctionPrototype().getProperty("apply").setChangeCallback(sp);
- getFunctionPrototype().getProperty("call").setChangeCallback(sp);
-
properties = getObjectPrototype().getMap().getProperties();
for (final jdk.nashorn.internal.runtime.Property property : properties) {
@@ -2125,10 +2201,10 @@ public final class Global extends ScriptObject implements Scope {
}
}
}
- }
- private static MethodHandle findOwnMH_V(final String name, final Class<?> rtype, final Class<?>... types) {
- return MH.findVirtual(MethodHandles.lookup(), Global.class, name, MH.type(rtype, types));
+ tagBuiltinProperties("Object", builtinObject);
+ tagBuiltinProperties("Function", builtinFunction);
+ tagBuiltinProperties("Function", anon);
}
private static MethodHandle findOwnMH_S(final String name, final Class<?> rtype, final Class<?>... types) {
@@ -2147,62 +2223,4 @@ public final class Global extends ScriptObject implements Scope {
protected boolean isGlobal() {
return true;
}
-
- /**
- * Check if there is a switchpoint for a reserved name. If there
- * is, it must be invalidated upon properties with this name
- * @param name property name
- * @return switchpoint for invalidating this property, or null if not registered
- */
- public SwitchPoint getChangeCallback(final String name) {
- return optimisticFunctionMap.get(name);
- }
-
- /**
- * Is this a special name, that might be subject to invalidation
- * on write, such as "apply" or "call"
- * @param name name to check
- * @return true if special name
- */
- public boolean isSpecialName(final String name) {
- return getChangeCallback(name) != null;
- }
-
- /**
- * Check if a reserved property name is invalidated
- * @param name property name
- * @return true if someone has written to it since Global was instantiated
- */
- public boolean isSpecialNameValid(final String name) {
- final SwitchPoint sp = getChangeCallback(name);
- return sp != null && !sp.hasBeenInvalidated();
- }
-
- /**
- * Tag a reserved name as invalidated - used when someone writes
- * to a property with this name - overly conservative, but link time
- * is too late to apply e.g. apply-&gt;call specialization
- * @param name property name
- */
- public void invalidateReservedName(final String name) {
- final SwitchPoint sp = getChangeCallback(name);
- if (sp != null) {
- getContext().getLogger(ApplySpecialization.class).info("Overwrote special name '" + name +"' - invalidating switchpoint");
- SwitchPoint.invalidateAll(new SwitchPoint[] { sp });
- }
- }
-
- /**
- * Bootstrapper for invalidating a builtin name
- * @param lookup lookup
- * @param name name to invalidate
- * @param type methodhandle type
- * @return callsite for invalidator
- */
- public static CallSite invalidateNameBootstrap(final MethodHandles.Lookup lookup, final String name, final MethodType type) {
- final MethodHandle target = MH.insertArguments(Global.instance().INVALIDATE_RESERVED_NAME, 0, name);
- return new ConstantCallSite(target);
- }
-
-
}
diff --git a/src/jdk/nashorn/internal/objects/NativeArray.java b/src/jdk/nashorn/internal/objects/NativeArray.java
index 163e86a4..c83e8883 100644
--- a/src/jdk/nashorn/internal/objects/NativeArray.java
+++ b/src/jdk/nashorn/internal/objects/NativeArray.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -33,8 +33,8 @@ import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex;
import static jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator.arrayLikeIterator;
import static jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator.reverseArrayLikeIterator;
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_STRICT;
-
import java.lang.invoke.MethodHandle;
+import java.lang.invoke.SwitchPoint;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -52,12 +52,13 @@ import jdk.nashorn.internal.objects.annotations.Function;
import jdk.nashorn.internal.objects.annotations.Getter;
import jdk.nashorn.internal.objects.annotations.ScriptClass;
import jdk.nashorn.internal.objects.annotations.Setter;
-import jdk.nashorn.internal.objects.annotations.SpecializedConstructor;
import jdk.nashorn.internal.objects.annotations.SpecializedFunction;
+import jdk.nashorn.internal.objects.annotations.SpecializedFunction.LinkLogic;
import jdk.nashorn.internal.objects.annotations.Where;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.Debug;
import jdk.nashorn.internal.runtime.JSType;
+import jdk.nashorn.internal.runtime.OptimisticBuiltins;
import jdk.nashorn.internal.runtime.PropertyDescriptor;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptFunction;
@@ -67,17 +68,20 @@ import jdk.nashorn.internal.runtime.Undefined;
import jdk.nashorn.internal.runtime.arrays.ArrayData;
import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
import jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator;
+import jdk.nashorn.internal.runtime.arrays.ContinuousArrayData;
+import jdk.nashorn.internal.runtime.arrays.IntElements;
+import jdk.nashorn.internal.runtime.arrays.IntOrLongElements;
import jdk.nashorn.internal.runtime.arrays.IteratorAction;
+import jdk.nashorn.internal.runtime.arrays.NumericElements;
import jdk.nashorn.internal.runtime.linker.Bootstrap;
import jdk.nashorn.internal.runtime.linker.InvokeByName;
-import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
/**
* Runtime representation of a JavaScript array. NativeArray only holds numeric
* keyed values. All other values are stored in spill.
*/
@ScriptClass("Array")
-public final class NativeArray extends ScriptObject {
+public final class NativeArray extends ScriptObject implements OptimisticBuiltins {
private static final Object JOIN = new Object();
private static final Object EVERY_CALLBACK_INVOKER = new Object();
private static final Object SOME_CALLBACK_INVOKER = new Object();
@@ -88,6 +92,16 @@ public final class NativeArray extends ScriptObject {
private static final Object CALL_CMP = new Object();
private static final Object TO_LOCALE_STRING = new Object();
+ private SwitchPoint lengthMadeNotWritableSwitchPoint;
+ private PushLinkLogic pushLinkLogic;
+ private PopLinkLogic popLinkLogic;
+
+ /**
+ * Index for the modification SwitchPoint that triggers when length
+ * becomes not writable
+ */
+ private static final int LENGTH_NOT_WRITABLE_SWITCHPOINT = 0;
+
/*
* Constructors.
*/
@@ -420,6 +434,28 @@ public final class NativeArray extends ScriptObject {
return getArray().asObjectArray();
}
+ @Override
+ public void setIsLengthNotWritable() {
+ super.setIsLengthNotWritable();
+ /*
+ * Switchpoints are created lazily. If we link any push or pop site,
+ * we need to create the "length made not writable" switchpoint, if it
+ * doesn't exist.
+ *
+ * If the switchpoint already exists, we will find it here, and invalidate
+ * it, invalidating all previous callsites that use it.
+ *
+ * If the switchpoint doesn't exist, no push/pop has been linked so far,
+ * because that would create it too. We invalidate it immediately and the
+ * check link logic for all future callsites will fail immediately at link
+ * time
+ */
+ if (lengthMadeNotWritableSwitchPoint == null) {
+ lengthMadeNotWritableSwitchPoint = new SwitchPoint();
+ }
+ SwitchPoint.invalidateAll(new SwitchPoint[] { lengthMadeNotWritableSwitchPoint });
+ }
+
/**
* ECMA 15.4.3.2 Array.isArray ( arg )
*
@@ -638,7 +674,7 @@ public final class NativeArray extends ScriptObject {
* @param self self reference
* @return the new NativeArray
*/
- @SpecializedConstructor
+ @SpecializedFunction(isConstructor=true)
public static NativeArray construct(final boolean newObj, final Object self) {
return new NativeArray(0);
}
@@ -653,7 +689,7 @@ public final class NativeArray extends ScriptObject {
* @param element first element
* @return the new NativeArray
*/
- @SpecializedConstructor
+ @SpecializedFunction(isConstructor=true)
public static Object construct(final boolean newObj, final Object self, final boolean element) {
return new NativeArray(new Object[] { element });
}
@@ -668,7 +704,7 @@ public final class NativeArray extends ScriptObject {
* @param length array length
* @return the new NativeArray
*/
- @SpecializedConstructor
+ @SpecializedFunction(isConstructor=true)
public static NativeArray construct(final boolean newObj, final Object self, final int length) {
if (length >= 0) {
return new NativeArray(length);
@@ -687,7 +723,7 @@ public final class NativeArray extends ScriptObject {
* @param length array length
* @return the new NativeArray
*/
- @SpecializedConstructor
+ @SpecializedFunction(isConstructor=true)
public static NativeArray construct(final boolean newObj, final Object self, final long length) {
if (length >= 0L && length <= JSType.MAX_UINT) {
return new NativeArray(length);
@@ -706,7 +742,7 @@ public final class NativeArray extends ScriptObject {
* @param length array length
* @return the new NativeArray
*/
- @SpecializedConstructor
+ @SpecializedFunction(isConstructor=true)
public static NativeArray construct(final boolean newObj, final Object self, final double length) {
final long uint32length = JSType.toUint32(length);
@@ -721,7 +757,7 @@ public final class NativeArray extends ScriptObject {
* ECMA 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ... ] ] ] )
*
* @param self self reference
- * @param args arguments to concat
+ * @param args arguments
* @return resulting NativeArray
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
@@ -793,6 +829,68 @@ public final class NativeArray extends ScriptObject {
}
/**
+ * Specialization of pop for ContinuousArrayData
+ * The link guard checks that the array is continuous AND not empty.
+ * The runtime guard checks that the guard is continuous (CCE otherwise)
+ *
+ * Primitive specialization, {@link LinkLogic}
+ *
+ * @param self self reference
+ * @return element popped
+ * @throws ClassCastException if array is empty, facilitating Undefined return value
+ */
+ @SpecializedFunction(name="pop", linkLogic=PopLinkLogic.class)
+ public static int popInt(final Object self) {
+ //must be non empty IntArrayData
+ return getContinuousNonEmptyArrayDataCCE(self, IntElements.class).fastPopInt();
+ }
+
+ /**
+ * Specialization of pop for ContinuousArrayData
+ *
+ * Primitive specialization, {@link LinkLogic}
+ *
+ * @param self self reference
+ * @return element popped
+ * @throws ClassCastException if array is empty, facilitating Undefined return value
+ */
+ @SpecializedFunction(name="pop", linkLogic=PopLinkLogic.class)
+ public static long popLong(final Object self) {
+ //must be non empty Int or LongArrayData
+ return getContinuousNonEmptyArrayDataCCE(self, IntOrLongElements.class).fastPopLong();
+ }
+
+ /**
+ * Specialization of pop for ContinuousArrayData
+ *
+ * Primitive specialization, {@link LinkLogic}
+ *
+ * @param self self reference
+ * @return element popped
+ * @throws ClassCastException if array is empty, facilitating Undefined return value
+ */
+ @SpecializedFunction(name="pop", linkLogic=PopLinkLogic.class)
+ public static double popDouble(final Object self) {
+ //must be non empty int long or double array data
+ return getContinuousNonEmptyArrayDataCCE(self, NumericElements.class).fastPopDouble();
+ }
+
+ /**
+ * Specialization of pop for ContinuousArrayData
+ *
+ * Primitive specialization, {@link LinkLogic}
+ *
+ * @param self self reference
+ * @return element popped
+ * @throws ClassCastException if array is empty, facilitating Undefined return value
+ */
+ @SpecializedFunction(name="pop", linkLogic=PopLinkLogic.class)
+ public static Object popObject(final Object self) {
+ //can be any data, because the numeric ones will throw cce and force relink
+ return getContinuousArrayDataCCE(self, null).fastPopObject();
+ }
+
+ /**
* ECMA 15.4.4.6 Array.prototype.pop ()
*
* @param self self reference
@@ -829,6 +927,62 @@ public final class NativeArray extends ScriptObject {
/**
* ECMA 15.4.4.7 Array.prototype.push (args...)
*
+ * Primitive specialization, {@link LinkLogic}
+ *
+ * @param self self reference
+ * @param arg a primitive to push
+ * @return array length after push
+ */
+ @SpecializedFunction(linkLogic=PushLinkLogic.class)
+ public static long push(final Object self, final int arg) {
+ return getContinuousArrayDataCCE(self, Integer.class).fastPush(arg);
+ }
+
+ /**
+ * ECMA 15.4.4.7 Array.prototype.push (args...)
+ *
+ * Primitive specialization, {@link LinkLogic}
+ *
+ * @param self self reference
+ * @param arg a primitive to push
+ * @return array length after push
+ */
+ @SpecializedFunction(linkLogic=PushLinkLogic.class)
+ public static long push(final Object self, final long arg) {
+ return getContinuousArrayDataCCE(self, Long.class).fastPush(arg);
+ }
+
+ /**
+ * ECMA 15.4.4.7 Array.prototype.push (args...)
+ *
+ * Primitive specialization, {@link LinkLogic}
+ *
+ * @param self self reference
+ * @param arg a primitive to push
+ * @return array length after push
+ */
+ @SpecializedFunction(linkLogic=PushLinkLogic.class)
+ public static long push(final Object self, final double arg) {
+ return getContinuousArrayDataCCE(self, Double.class).fastPush(arg);
+ }
+
+ /**
+ * ECMA 15.4.4.7 Array.prototype.push (args...)
+ *
+ * Primitive specialization, {@link LinkLogic}
+ *
+ * @param self self reference
+ * @param arg a primitive to push
+ * @return array length after push
+ */
+ @SpecializedFunction(name="push", linkLogic=PushLinkLogic.class)
+ public static long pushObject(final Object self, final Object arg) {
+ return getContinuousArrayDataCCE(self, Object.class).fastPush(arg);
+ }
+
+ /**
+ * ECMA 15.4.4.7 Array.prototype.push (args...)
+ *
* @param self self reference
* @param args arguments to push
* @return array length after pushes
@@ -857,61 +1011,6 @@ public final class NativeArray extends ScriptObject {
}
/**
- * ECMA 15.4.4.7 Array.prototype.push (args...) specialized for single int argument
- *
- * @param self self reference
- * @param arg argument to push
- * @return array after pushes
- */
-/* @SpecializedFunction
- public static long push(final Object self, final int arg) {
- try {
- final ScriptObject sobj = (ScriptObject)self;
- final ArrayData arrayData = sobj.getArray();
- final long length = arrayData.length();
-
- if (bulkable(sobj) && length + 1 <= JSType.MAX_UINT) {
- sobj.setArray(arrayData.ensure(length).set(ArrayIndex.getArrayIndex(length), arg, true));
- return length + 1;
- }
-
- long len = JSType.toUint32(sobj.getLength());
- sobj.set(len++, arg, true);
- sobj.set("length", len, true);
- return len;
- } catch (final ClassCastException | NullPointerException e) {
- throw typeError("not.an.object", ScriptRuntime.safeToString(self));
- }
- }
-*/
- /**
- * ECMA 15.4.4.7 Array.prototype.push (args...) specialized for single number argument
- *
- * @param self self reference
- * @param arg argument to push
- * @return array after pushes
- */
- /* @SpecializedFunction
- public static long push(final Object self, final double arg) {
- try {
- final ScriptObject sobj = (ScriptObject)self; final ArrayData arrayData = sobj.getArray();
- final long length = arrayData.length();
-
- if (bulkable(sobj) && length + 1 <= JSType.MAX_UINT) {
- sobj.setArray(arrayData.ensure(length).set(ArrayIndex.getArrayIndex(length), arg, true));
- return length + 1;
- }
-
- long len = JSType.toUint32(sobj.getLength());
- sobj.set(len++, arg, true);
- sobj.set("length", len, true);
- return len;
- } catch (final ClassCastException | NullPointerException e) {
- throw typeError("not.an.object", ScriptRuntime.safeToString(self));
- }
- }
-*/
- /**
* ECMA 15.4.4.7 Array.prototype.push (args...) specialized for single object argument
*
* @param self self reference
@@ -925,7 +1024,7 @@ public final class NativeArray extends ScriptObject {
final ArrayData arrayData = sobj.getArray();
final long length = arrayData.length();
if (bulkable(sobj) && length < JSType.MAX_UINT) {
- sobj.setArray(arrayData.push(true, arg)); //ensure(length).set(ArrayIndex.getArrayIndex(length), arg, true));
+ sobj.setArray(arrayData.push(true, arg));
return length + 1;
}
@@ -1584,6 +1683,192 @@ public final class NativeArray extends ScriptObject {
@Override
public String toString() {
- return "NativeArray@" + Debug.id(this) + '@' + getArray().getClass().getSimpleName();
+ return "NativeArray@" + Debug.id(this) + " [" + getArray().getClass().getSimpleName() + ']';
+ }
+
+ @Override
+ public SpecializedFunction.LinkLogic getLinkLogic(final Class<? extends LinkLogic> clazz) {
+ if (clazz == PushLinkLogic.class) {
+ return pushLinkLogic == null ? new PushLinkLogic(this) : pushLinkLogic;
+ } else if (clazz == PopLinkLogic.class) {
+ return popLinkLogic == null ? new PopLinkLogic(this) : pushLinkLogic;
+ }
+ return null;
+ }
+
+ @Override
+ public boolean hasPerInstanceAssumptions() {
+ return true; //length switchpoint
+ }
+
+ /**
+ * This is an abstract super class that contains common functionality for all
+ * specialized optimistic builtins in NativeArray. For example, it handles the
+ * modification switchpoint which is touched when length is written.
+ */
+ private static abstract class ArrayLinkLogic extends SpecializedFunction.LinkLogic {
+ private final NativeArray array;
+
+ protected ArrayLinkLogic(final NativeArray array) {
+ this.array = array;
+ }
+
+ private SwitchPoint getSwitchPoint() {
+ return array.lengthMadeNotWritableSwitchPoint;
+ }
+
+ private SwitchPoint newSwitchPoint() {
+ assert array.lengthMadeNotWritableSwitchPoint == null;
+ final SwitchPoint sp = new SwitchPoint();
+ array.lengthMadeNotWritableSwitchPoint = sp;
+ return sp;
+ }
+
+ protected static ContinuousArrayData getContinuousArrayData(final Object self) {
+ try {
+ //cast to NativeArray, to avoid cases like x = {0:0, 1:1}, x.length = 2, where we can't use the array push/pop
+ return (ContinuousArrayData)((NativeArray)self).getArray();
+ } catch (final Exception e) {
+ return null;
+ }
+ }
+
+ /**
+ * Push and pop callsites can throw ClassCastException as a mechanism to have them
+ * relinked - this enabled fast checks of the kind of ((IntArrayData)arrayData).push(x)
+ * for an IntArrayData only push - if this fails, a CCE will be thrown and we will relink
+ */
+ @Override
+ public Class<? extends Throwable> getRelinkException() {
+ return ClassCastException.class;
+ }
+
+ @Override
+ public boolean hasModificationSwitchPoints() {
+ return getSwitchPoint() != null;
+ }
+
+ @Override
+ public boolean hasModificationSwitchPoint(final int index) {
+ assert index == LENGTH_NOT_WRITABLE_SWITCHPOINT;
+ return hasModificationSwitchPoints();
+ }
+
+ @Override
+ public SwitchPoint getOrCreateModificationSwitchPoint(final int index) {
+ assert index == LENGTH_NOT_WRITABLE_SWITCHPOINT;
+ SwitchPoint sp = getSwitchPoint();
+ if (sp == null) {
+ sp = newSwitchPoint();
+ }
+ return sp;
+ }
+
+ @Override
+ public SwitchPoint[] getOrCreateModificationSwitchPoints() {
+ return new SwitchPoint[] { getOrCreateModificationSwitchPoint(LENGTH_NOT_WRITABLE_SWITCHPOINT) };
+ }
+
+ @Override
+ public void invalidateModificationSwitchPoint(final int index) {
+ assert index == LENGTH_NOT_WRITABLE_SWITCHPOINT;
+ invalidateModificationSwitchPoints();
+ }
+
+ @Override
+ public void invalidateModificationSwitchPoints() {
+ final SwitchPoint sp = getSwitchPoint();
+ assert sp != null : "trying to invalidate non-existant modified SwitchPoint";
+ if (!sp.hasBeenInvalidated()) {
+ SwitchPoint.invalidateAll(new SwitchPoint[] { sp });
+ }
+ }
+
+ @Override
+ public boolean hasInvalidatedModificationSwitchPoint(final int index) {
+ assert index == LENGTH_NOT_WRITABLE_SWITCHPOINT;
+ return hasInvalidatedModificationSwitchPoints();
+ }
+
+ @Override
+ public boolean hasInvalidatedModificationSwitchPoints() {
+ final SwitchPoint sp = getSwitchPoint();
+ return sp != null && !sp.hasBeenInvalidated();
+ }
+ }
+
+ /**
+ * This is linker logic for optimistic pushes
+ */
+ private static final class PushLinkLogic extends ArrayLinkLogic {
+ private PushLinkLogic(final NativeArray array) {
+ super(array);
+ }
+
+ @Override
+ public boolean canLink(final Object self, final CallSiteDescriptor desc, final LinkRequest request) {
+ return getContinuousArrayData(self) != null;
+ }
+ }
+
+ /**
+ * This is linker logic for optimistic pops
+ */
+ private static final class PopLinkLogic extends ArrayLinkLogic {
+ private PopLinkLogic(final NativeArray array) {
+ super(array);
+ }
+
+ /**
+ * We need to check if we are dealing with a continuous non empty array data here,
+ * as pop with a primitive return value returns undefined for arrays with length 0
+ */
+ @Override
+ public boolean canLink(final Object self, final CallSiteDescriptor desc, final LinkRequest request) {
+ final ContinuousArrayData data = getContinuousNonEmptyArrayData(self);
+ if (data != null) {
+ final Class<?> elementType = data.getElementType();
+ final Class<?> returnType = desc.getMethodType().returnType();
+ final boolean typeFits = JSType.getAccessorTypeIndex(returnType) >= JSType.getAccessorTypeIndex(elementType);
+ return typeFits;
+ }
+ return false;
+ }
+
+ private static ContinuousArrayData getContinuousNonEmptyArrayData(final Object self) {
+ final ContinuousArrayData data = getContinuousArrayData(self);
+ if (data != null) {
+ return data.length() == 0 ? null : data;
+ }
+ return null;
+ }
+ }
+
+ //runtime calls for push and pops. they could be used as guards, but they also perform the runtime logic,
+ //so rather than synthesizing them into a guard method handle that would also perform the push on the
+ //retrieved receiver, we use this as runtime logic
+
+ //TODO - fold these into the Link logics, but I'll do that as a later step, as I want to do a checkin
+ //where everything works first
+
+ private static final <T> ContinuousArrayData getContinuousNonEmptyArrayDataCCE(final Object self, final Class<T> clazz) {
+ try {
+ @SuppressWarnings("unchecked")
+ final ContinuousArrayData data = (ContinuousArrayData)(T)((NativeArray)self).getArray();
+ if (data.length() != 0L) {
+ return data; //if length is 0 we cannot pop and have to relink, because then we'd have to return an undefined, which is a wider type than e.g. int
+ }
+ } catch (final NullPointerException e) {
+ //fallthru
+ }
+ throw new ClassCastException();
+ }
+
+ private static final ContinuousArrayData getContinuousArrayDataCCE(final Object self, final Class<?> elementType) {
+ try {
+ return (ContinuousArrayData)((NativeArray)self).getArray(elementType); //ensure element type can fit "elementType"
+ } catch (final NullPointerException e) {
+ throw new ClassCastException();
+ }
}
}
diff --git a/src/jdk/nashorn/internal/objects/NativeDataView.java b/src/jdk/nashorn/internal/objects/NativeDataView.java
index 470803ef..0adde064 100644
--- a/src/jdk/nashorn/internal/objects/NativeDataView.java
+++ b/src/jdk/nashorn/internal/objects/NativeDataView.java
@@ -27,7 +27,6 @@ package jdk.nashorn.internal.objects;
import static jdk.nashorn.internal.runtime.ECMAErrors.rangeError;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
-
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import jdk.nashorn.internal.objects.annotations.Attribute;
@@ -35,7 +34,6 @@ import jdk.nashorn.internal.objects.annotations.Constructor;
import jdk.nashorn.internal.objects.annotations.Function;
import jdk.nashorn.internal.objects.annotations.Property;
import jdk.nashorn.internal.objects.annotations.ScriptClass;
-import jdk.nashorn.internal.objects.annotations.SpecializedConstructor;
import jdk.nashorn.internal.objects.annotations.SpecializedFunction;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.PropertyMap;
@@ -156,7 +154,7 @@ public class NativeDataView extends ScriptObject {
* @param offset offset in bytes from the start of the ArrayBuffer
* @return newly constructed DataView object
*/
- @SpecializedConstructor
+ @SpecializedFunction(isConstructor=true)
public static NativeDataView constructor(final boolean newObj, final Object self, final Object arrBuf, final int offset) {
if (!(arrBuf instanceof NativeArrayBuffer)) {
throw typeError("not.an.arraybuffer.in.dataview");
@@ -174,7 +172,7 @@ public class NativeDataView extends ScriptObject {
* @param length is the number of bytes from the offset that this DataView will reference
* @return newly constructed DataView object
*/
- @SpecializedConstructor
+ @SpecializedFunction(isConstructor=true)
public static NativeDataView constructor(final boolean newObj, final Object self, final Object arrBuf, final int offset, final int length) {
if (!(arrBuf instanceof NativeArrayBuffer)) {
throw typeError("not.an.arraybuffer.in.dataview");
diff --git a/src/jdk/nashorn/internal/objects/NativeDate.java b/src/jdk/nashorn/internal/objects/NativeDate.java
index eac59c46..c85016a5 100644
--- a/src/jdk/nashorn/internal/objects/NativeDate.java
+++ b/src/jdk/nashorn/internal/objects/NativeDate.java
@@ -30,7 +30,6 @@ import static java.lang.Double.isInfinite;
import static java.lang.Double.isNaN;
import static jdk.nashorn.internal.runtime.ECMAErrors.rangeError;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
-
import java.util.Locale;
import java.util.TimeZone;
import java.util.concurrent.Callable;
@@ -38,7 +37,7 @@ import jdk.nashorn.internal.objects.annotations.Attribute;
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.objects.annotations.SpecializedConstructor;
+import jdk.nashorn.internal.objects.annotations.SpecializedFunction;
import jdk.nashorn.internal.objects.annotations.Where;
import jdk.nashorn.internal.parser.DateParser;
import jdk.nashorn.internal.runtime.ConsString;
@@ -155,7 +154,7 @@ public final class NativeDate extends ScriptObject {
* @param self self references
* @return Date representing now
*/
- @SpecializedConstructor
+ @SpecializedFunction(isConstructor=true)
public static Object construct(final boolean isNew, final Object self) {
final NativeDate result = new NativeDate();
return isNew ? result : toStringImpl(result, FORMAT_DATE_TIME);
diff --git a/src/jdk/nashorn/internal/objects/NativeDebug.java b/src/jdk/nashorn/internal/objects/NativeDebug.java
index 20dd85e5..3d8f1095 100644
--- a/src/jdk/nashorn/internal/objects/NativeDebug.java
+++ b/src/jdk/nashorn/internal/objects/NativeDebug.java
@@ -26,7 +26,6 @@
package jdk.nashorn.internal.objects;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
-
import java.io.PrintWriter;
import java.util.LinkedList;
import java.util.Objects;
@@ -262,8 +261,8 @@ public final class NativeDebug extends ScriptObject {
/**
* Set the event queue capacity
- * @param self
- * @param newCapacity
+ * @param self an event queue
+ * @param newCapacity new capacity
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static void setEventQueueCapacity(final Object self, final Object newCapacity) {
diff --git a/src/jdk/nashorn/internal/objects/NativeFloat32Array.java b/src/jdk/nashorn/internal/objects/NativeFloat32Array.java
index b2639c45..e5ef0b5a 100644
--- a/src/jdk/nashorn/internal/objects/NativeFloat32Array.java
+++ b/src/jdk/nashorn/internal/objects/NativeFloat32Array.java
@@ -85,6 +85,11 @@ public final class NativeFloat32Array extends ArrayBufferView {
}
@Override
+ public Class<?> getElementType() {
+ return double.class;
+ }
+
+ @Override
protected MethodHandle getGetElem() {
return GET_ELEM;
}
@@ -137,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 93162952..1ad61b27 100644
--- a/src/jdk/nashorn/internal/objects/NativeFloat64Array.java
+++ b/src/jdk/nashorn/internal/objects/NativeFloat64Array.java
@@ -94,6 +94,11 @@ public final class NativeFloat64Array extends ArrayBufferView {
return SET_ELEM;
}
+ @Override
+ public Class<?> getElementType() {
+ return double.class;
+ }
+
private double getElem(final int index) {
try {
return nb.get(index);
@@ -137,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 ad61cbd2..06148848 100644
--- a/src/jdk/nashorn/internal/objects/NativeInt16Array.java
+++ b/src/jdk/nashorn/internal/objects/NativeInt16Array.java
@@ -95,6 +95,11 @@ public final class NativeInt16Array extends ArrayBufferView {
return SET_ELEM;
}
+ @Override
+ public Class<?> getElementType() {
+ return int.class;
+ }
+
private int getElem(final int index) {
try {
return nb.get(index);
@@ -120,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 b018f04e..5074dc68 100644
--- a/src/jdk/nashorn/internal/objects/NativeInt32Array.java
+++ b/src/jdk/nashorn/internal/objects/NativeInt32Array.java
@@ -113,21 +113,41 @@ public final class NativeInt32Array extends ArrayBufferView {
}
@Override
+ public Class<?> getElementType() {
+ return int.class;
+ }
+
+ @Override
public int getInt(final int index) {
return getElem(index);
}
@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 6f9f6e7b..319168c0 100644
--- a/src/jdk/nashorn/internal/objects/NativeInt8Array.java
+++ b/src/jdk/nashorn/internal/objects/NativeInt8Array.java
@@ -93,6 +93,11 @@ public final class NativeInt8Array extends ArrayBufferView {
return SET_ELEM;
}
+ @Override
+ public Class<?> getElementType() {
+ return int.class;
+ }
+
private int getElem(final int index) {
try {
return nb.get(index);
@@ -118,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/NativeJSAdapter.java b/src/jdk/nashorn/internal/objects/NativeJSAdapter.java
index f2b1bef4..c5ca9101 100644
--- a/src/jdk/nashorn/internal/objects/NativeJSAdapter.java
+++ b/src/jdk/nashorn/internal/objects/NativeJSAdapter.java
@@ -48,7 +48,6 @@ import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.ScriptRuntime;
-import jdk.nashorn.internal.runtime.UnwarrantedOptimismException;
import jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator;
import jdk.nashorn.internal.scripts.JO;
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/NativeRegExp.java b/src/jdk/nashorn/internal/objects/NativeRegExp.java
index 01806d44..33ef2849 100644
--- a/src/jdk/nashorn/internal/objects/NativeRegExp.java
+++ b/src/jdk/nashorn/internal/objects/NativeRegExp.java
@@ -33,13 +33,14 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
+
import jdk.nashorn.internal.objects.annotations.Attribute;
import jdk.nashorn.internal.objects.annotations.Constructor;
import jdk.nashorn.internal.objects.annotations.Function;
import jdk.nashorn.internal.objects.annotations.Getter;
import jdk.nashorn.internal.objects.annotations.Property;
import jdk.nashorn.internal.objects.annotations.ScriptClass;
-import jdk.nashorn.internal.objects.annotations.SpecializedConstructor;
+import jdk.nashorn.internal.objects.annotations.SpecializedFunction;
import jdk.nashorn.internal.objects.annotations.Where;
import jdk.nashorn.internal.runtime.BitVector;
import jdk.nashorn.internal.runtime.JSType;
@@ -143,7 +144,7 @@ public final class NativeRegExp extends ScriptObject {
* @param self self reference
* @return new NativeRegExp
*/
- @SpecializedConstructor
+ @SpecializedFunction(isConstructor=true)
public static NativeRegExp constructor(final boolean isNew, final Object self) {
return new NativeRegExp("", "");
}
@@ -158,7 +159,7 @@ public final class NativeRegExp extends ScriptObject {
* @param pattern pattern
* @return new NativeRegExp
*/
- @SpecializedConstructor
+ @SpecializedFunction(isConstructor=true)
public static NativeRegExp constructor(final boolean isNew, final Object self, final Object pattern) {
return newRegExp(pattern, UNDEFINED);
}
@@ -174,7 +175,7 @@ public final class NativeRegExp extends ScriptObject {
* @param flags flags
* @return new NativeRegExp
*/
- @SpecializedConstructor
+ @SpecializedFunction(isConstructor=true)
public static NativeRegExp constructor(final boolean isNew, final Object self, final Object pattern, final Object flags) {
return newRegExp(pattern, flags);
}
diff --git a/src/jdk/nashorn/internal/objects/NativeString.java b/src/jdk/nashorn/internal/objects/NativeString.java
index 69d34c2d..850af622 100644
--- a/src/jdk/nashorn/internal/objects/NativeString.java
+++ b/src/jdk/nashorn/internal/objects/NativeString.java
@@ -29,7 +29,6 @@ import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.JSType.isRepresentableAsInt;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
-
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
@@ -49,11 +48,12 @@ import jdk.nashorn.internal.objects.annotations.Constructor;
import jdk.nashorn.internal.objects.annotations.Function;
import jdk.nashorn.internal.objects.annotations.Getter;
import jdk.nashorn.internal.objects.annotations.ScriptClass;
-import jdk.nashorn.internal.objects.annotations.SpecializedConstructor;
import jdk.nashorn.internal.objects.annotations.SpecializedFunction;
+import jdk.nashorn.internal.objects.annotations.SpecializedFunction.LinkLogic;
import jdk.nashorn.internal.objects.annotations.Where;
import jdk.nashorn.internal.runtime.ConsString;
import jdk.nashorn.internal.runtime.JSType;
+import jdk.nashorn.internal.runtime.OptimisticBuiltins;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
@@ -67,7 +67,7 @@ import jdk.nashorn.internal.runtime.linker.PrimitiveLookup;
* ECMA 15.5 String Objects.
*/
@ScriptClass("String")
-public final class NativeString extends ScriptObject {
+public final class NativeString extends ScriptObject implements OptimisticBuiltins {
private final CharSequence value;
@@ -568,6 +568,14 @@ public final class NativeString extends ScriptObject {
return pos < 0 || pos >= str.length() ? "" : String.valueOf(str.charAt(pos));
}
+ private static int getValidChar(final Object self, final int pos) {
+ try {
+ return ((CharSequence)self).charAt(pos);
+ } catch (final IndexOutOfBoundsException e) {
+ throw new ClassCastException();
+ }
+ }
+
/**
* ECMA 15.5.4.5 String.prototype.charCodeAt (pos)
* @param self self reference
@@ -576,7 +584,9 @@ public final class NativeString extends ScriptObject {
*/
@Function(attributes = Attribute.NOT_ENUMERABLE)
public static double charCodeAt(final Object self, final Object pos) {
- return charCodeAtImpl(checkObjectToString(self), JSType.toInteger(pos));
+ final String str = checkObjectToString(self);
+ final int idx = JSType.toInteger(pos);
+ return idx < 0 || idx >= str.length() ? Double.NaN : str.charAt(idx);
}
/**
@@ -585,24 +595,32 @@ public final class NativeString extends ScriptObject {
* @param pos position in string
* @return number representing charcode at position
*/
- @SpecializedFunction
- public static double charCodeAt(final Object self, final double pos) {
- return charCodeAt(self, (int) pos);
+ @SpecializedFunction(linkLogic=CharCodeAtLinkLogic.class)
+ public static int charCodeAt(final Object self, final double pos) {
+ return charCodeAt(self, (int)pos); //toInt pos is ok
}
/**
- * ECMA 15.5.4.5 String.prototype.charCodeAt (pos) - specialized version for int position
+ * ECMA 15.5.4.5 String.prototype.charCodeAt (pos) - specialized version for long position
* @param self self reference
* @param pos position in string
* @return number representing charcode at position
*/
- @SpecializedFunction
- public static double charCodeAt(final Object self, final int pos) {
- return charCodeAtImpl(checkObjectToString(self), pos);
+ @SpecializedFunction(linkLogic=CharCodeAtLinkLogic.class)
+ public static int charCodeAt(final Object self, final long pos) {
+ return charCodeAt(self, (int)pos);
}
- private static double charCodeAtImpl(final String str, final int pos) {
- return pos < 0 || pos >= str.length() ? Double.NaN : str.charAt(pos);
+ /**
+ * ECMA 15.5.4.5 String.prototype.charCodeAt (pos) - specialized version for int position
+ * @param self self reference
+ * @param pos position in string
+ * @return number representing charcode at position
+ */
+
+ @SpecializedFunction(linkLogic=CharCodeAtLinkLogic.class)
+ public static int charCodeAt(final Object self, final int pos) {
+ return getValidChar(self, pos);
}
/**
@@ -1097,7 +1115,6 @@ public final class NativeString extends ScriptObject {
*/
@Function(attributes = Attribute.NOT_ENUMERABLE)
public static String trim(final Object self) {
-
final String str = checkObjectToString(self);
int start = 0;
int end = str.length() - 1;
@@ -1181,7 +1198,7 @@ public final class NativeString extends ScriptObject {
*
* @return new NativeString ("")
*/
- @SpecializedConstructor
+ @SpecializedFunction(isConstructor=true)
public static Object constructor(final boolean newObj, final Object self) {
return newObj ? newObj("") : "";
}
@@ -1197,7 +1214,7 @@ public final class NativeString extends ScriptObject {
*
* @return new NativeString (arg)
*/
- @SpecializedConstructor
+ @SpecializedFunction(isConstructor=true)
public static Object constructor(final boolean newObj, final Object self, final Object arg) {
final CharSequence str = JSType.toCharSequence(arg);
return newObj ? newObj(str) : str.toString();
@@ -1214,8 +1231,42 @@ public final class NativeString extends ScriptObject {
*
* @return new NativeString containing the string representation of the arg
*/
- @SpecializedConstructor
+ @SpecializedFunction(isConstructor=true)
public static Object constructor(final boolean newObj, final Object self, final int arg) {
+ final String str = Integer.toString(arg);
+ return newObj ? newObj(str) : str;
+ }
+
+ /**
+ * ECMA 15.5.2.1 new String ( [ value ] ) - special version with exactly one {@code int} arg
+ *
+ * Constructor
+ *
+ * @param newObj is this constructor invoked with the new operator
+ * @param self self reference
+ * @param arg the arg
+ *
+ * @return new NativeString containing the string representation of the arg
+ */
+ @SpecializedFunction(isConstructor=true)
+ public static Object constructor(final boolean newObj, final Object self, final long arg) {
+ final String str = Long.toString(arg);
+ return newObj ? newObj(str) : str;
+ }
+
+ /**
+ * ECMA 15.5.2.1 new String ( [ value ] ) - special version with exactly one {@code int} arg
+ *
+ * Constructor
+ *
+ * @param newObj is this constructor invoked with the new operator
+ * @param self self reference
+ * @param arg the arg
+ *
+ * @return new NativeString containing the string representation of the arg
+ */
+ @SpecializedFunction(isConstructor=true)
+ public static Object constructor(final boolean newObj, final Object self, final double arg) {
final String str = JSType.toString(arg);
return newObj ? newObj(str) : str;
}
@@ -1231,9 +1282,9 @@ public final class NativeString extends ScriptObject {
*
* @return new NativeString containing the string representation of the arg
*/
- @SpecializedConstructor
+ @SpecializedFunction(isConstructor=true)
public static Object constructor(final boolean newObj, final Object self, final boolean arg) {
- final String str = JSType.toString(arg);
+ final String str = Boolean.toString(arg);
return newObj ? newObj(str) : str;
}
@@ -1281,7 +1332,7 @@ public final class NativeString extends ScriptObject {
} else if (self != null && self == Global.instance().getStringPrototype()) {
return "";
} else {
- throw typeError( "not.a.string", ScriptRuntime.safeToString(self));
+ throw typeError("not.a.string", ScriptRuntime.safeToString(self));
}
}
@@ -1310,4 +1361,50 @@ public final class NativeString extends ScriptObject {
return MH.findStatic(MethodHandles.lookup(), NativeString.class, name, type);
}
+ @Override
+ public LinkLogic getLinkLogic(final Class<? extends LinkLogic> clazz) {
+ if (clazz == CharCodeAtLinkLogic.class) {
+ return CharCodeAtLinkLogic.INSTANCE;
+ }
+ return null;
+ }
+
+ @Override
+ public boolean hasPerInstanceAssumptions() {
+ return false;
+ }
+
+ /**
+ * This is linker logic charCodeAt - when we specialize further methods in NativeString
+ * It may be expanded. It's link check makes sure that we are dealing with a char
+ * sequence and that we are in range
+ */
+ private static final class CharCodeAtLinkLogic extends SpecializedFunction.LinkLogic {
+
+ private static final CharCodeAtLinkLogic INSTANCE = new CharCodeAtLinkLogic();
+
+ @Override
+ public boolean canLink(final Object self, final CallSiteDescriptor desc, final LinkRequest request) {
+ try {
+ //check that it's a char sequence or throw cce
+ final CharSequence cs = (CharSequence)self;
+ //check that the index, representable as an int, is inside the array
+ final int intIndex = JSType.toInteger(request.getArguments()[1]);
+ return intIndex >= 0 && intIndex < cs.length(); //can link
+ } catch (final ClassCastException | IndexOutOfBoundsException e) {
+ //fallthru
+ }
+ return false;
+ }
+
+ /**
+ * charCodeAt callsites can throw ClassCastException as a mechanism to have them
+ * relinked - this enabled fast checks of the kind of ((IntArrayData)arrayData).push(x)
+ * for an IntArrayData only push - if this fails, a CCE will be thrown and we will relink
+ */
+ @Override
+ public Class<? extends Throwable> getRelinkException() {
+ return ClassCastException.class;
+ }
+ }
}
diff --git a/src/jdk/nashorn/internal/objects/NativeUint16Array.java b/src/jdk/nashorn/internal/objects/NativeUint16Array.java
index 68bd410c..7f8a1923 100644
--- a/src/jdk/nashorn/internal/objects/NativeUint16Array.java
+++ b/src/jdk/nashorn/internal/objects/NativeUint16Array.java
@@ -119,21 +119,41 @@ public final class NativeUint16Array extends ArrayBufferView {
}
@Override
+ public Class<?> getElementType() {
+ return int.class;
+ }
+
+ @Override
public int getInt(final int index) {
return getElem(index);
}
@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 47ee08da..4772b977 100644
--- a/src/jdk/nashorn/internal/objects/NativeUint32Array.java
+++ b/src/jdk/nashorn/internal/objects/NativeUint32Array.java
@@ -128,6 +128,11 @@ public final class NativeUint32Array extends ArrayBufferView {
}
@Override
+ public Class<?> getElementType() {
+ return long.class;
+ }
+
+ @Override
public int getInt(final int index) {
return (int)getLong(index);
}
@@ -138,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 15e01245..be7eb368 100644
--- a/src/jdk/nashorn/internal/objects/NativeUint8Array.java
+++ b/src/jdk/nashorn/internal/objects/NativeUint8Array.java
@@ -119,21 +119,41 @@ public final class NativeUint8Array extends ArrayBufferView {
}
@Override
+ public Class<?> getElementType() {
+ return int.class;
+ }
+
+ @Override
public int getInt(final int index) {
return getElem(index);
}
@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 db19ab37..125ac0a2 100644
--- a/src/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java
+++ b/src/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java
@@ -98,6 +98,11 @@ public final class NativeUint8ClampedArray extends ArrayBufferView {
return SET_ELEM;
}
+ @Override
+ public Class<?> getElementType() {
+ return int.class;
+ }
+
private int getElem(final int index) {
try {
return nb.get(index) & 0xff;
@@ -154,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/objects/ScriptFunctionImpl.java b/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java
index a52051ca..36e7716b 100644
--- a/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java
+++ b/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java
@@ -30,6 +30,7 @@ import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
import java.lang.invoke.MethodHandle;
import java.util.ArrayList;
+
import jdk.nashorn.internal.runtime.AccessorProperty;
import jdk.nashorn.internal.runtime.GlobalFunctions;
import jdk.nashorn.internal.runtime.Property;
@@ -38,6 +39,7 @@ import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptFunctionData;
import jdk.nashorn.internal.runtime.ScriptObject;
+import jdk.nashorn.internal.runtime.Specialization;
/**
* Concrete implementation of ScriptFunction. This sets correct map for the
@@ -58,7 +60,7 @@ public class ScriptFunctionImpl extends ScriptFunction {
// Marker object for lazily initialized prototype object
private static final Object LAZY_PROTOTYPE = new Object();
- private ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final MethodHandle[] specs, final Global global) {
+ private ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final Specialization[] specs, final Global global) {
super(name, invokeHandle, map$, null, specs, ScriptFunctionData.IS_BUILTIN_CONSTRUCTOR);
init(global);
}
@@ -71,11 +73,11 @@ public class ScriptFunctionImpl extends ScriptFunction {
* @param invokeHandle handle for invocation
* @param specs specialized versions of this method, if available, null otherwise
*/
- ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final MethodHandle[] specs) {
+ ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final Specialization[] specs) {
this(name, invokeHandle, specs, Global.instance());
}
- private ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final PropertyMap map, final MethodHandle[] specs, final Global global) {
+ private ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final PropertyMap map, final Specialization[] specs, final Global global) {
super(name, invokeHandle, map.addAll(map$), null, specs, ScriptFunctionData.IS_BUILTIN_CONSTRUCTOR);
init(global);
}
@@ -89,11 +91,11 @@ public class ScriptFunctionImpl extends ScriptFunction {
* @param map initial property map
* @param specs specialized versions of this method, if available, null otherwise
*/
- ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final PropertyMap map, final MethodHandle[] specs) {
+ ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final PropertyMap map, final Specialization[] specs) {
this(name, invokeHandle, map, specs, Global.instance());
}
- private ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final MethodHandle[] specs, final int flags, final Global global) {
+ private ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final Specialization[] specs, final int flags, final Global global) {
super(name, methodHandle, getMap(isStrict(flags)), scope, specs, flags);
init(global);
}
@@ -107,7 +109,7 @@ public class ScriptFunctionImpl extends ScriptFunction {
* @param specs specialized versions of this method, if available, null otherwise
* @param flags {@link ScriptFunctionData} flags
*/
- ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final MethodHandle[] specs, final int flags) {
+ ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final Specialization[] specs, final int flags) {
this(name, methodHandle, scope, specs, flags, Global.instance());
}
@@ -184,7 +186,7 @@ public class ScriptFunctionImpl extends ScriptFunction {
return new AnonymousFunction();
}
- private static ScriptFunction makeFunction(final String name, final MethodHandle methodHandle, final MethodHandle[] specs, final int flags) {
+ private static ScriptFunction makeFunction(final String name, final MethodHandle methodHandle, final Specialization[] specs, final int flags) {
final ScriptFunctionImpl func = new ScriptFunctionImpl(name, methodHandle, null, specs, flags);
func.setPrototype(UNDEFINED);
// Non-constructor built-in functions do not have "prototype" property
@@ -201,7 +203,7 @@ public class ScriptFunctionImpl extends ScriptFunction {
* @param specs specialized versions of function if available, null otherwise
* @return new ScriptFunction
*/
- static ScriptFunction makeFunction(final String name, final MethodHandle methodHandle, final MethodHandle[] specs) {
+ static ScriptFunction makeFunction(final String name, final MethodHandle methodHandle, final Specialization[] specs) {
return makeFunction(name, methodHandle, specs, ScriptFunctionData.IS_BUILTIN);
}
diff --git a/src/jdk/nashorn/internal/objects/annotations/SpecializedFunction.java b/src/jdk/nashorn/internal/objects/annotations/SpecializedFunction.java
index 1e75d2a7..36f47b27 100644
--- a/src/jdk/nashorn/internal/objects/annotations/SpecializedFunction.java
+++ b/src/jdk/nashorn/internal/objects/annotations/SpecializedFunction.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -29,18 +29,315 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.SwitchPoint;
+import jdk.internal.dynalink.CallSiteDescriptor;
+import jdk.internal.dynalink.linker.LinkRequest;
+import jdk.nashorn.internal.runtime.ScriptFunction;
/**
- * The SpecializedFunction annotation is used to flag more type specific functions than the standard one in
- * Native objects. For example {@link jdk.nashorn.internal.objects.NativeMath#max} takes an arbitrary number of
- * Object elements as an array. Call this function, including the varargs collector that allocates the array
- * upon each invocation, is much more expensive than calling a specialized function that takes two arguments
- * of, e.g. int type from the call site, such as {@link jdk.nashorn.internal.objects.NativeMath#max(Object, int, int)}.
- * {@link jdk.nashorn.internal.runtime.ScriptFunction} will try to look up the most specific function when
- * linking the callsite.
+ * The SpecializedFunction annotation is used to flag more type specific
+ * functions than the standard one in the native objects
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface SpecializedFunction {
- //empty
+
+ /**
+ * Functionality for testing if we are allowed to link a specialized
+ * function the first time we encounter it. Then the guard will handle the
+ * rest of the invocations
+ *
+ * This is the same for all callsites in Nashorn, the first time callsite is
+ * linked, we have to manually check that the linkage is OK. Even if we add
+ * a guard and it fails upon the first try, this is not good enough.
+ * (Symmetrical to how it works everywhere else in the Nashorn runtime).
+ *
+ * Here we abstract out a few of the most common link guard checks.
+ */
+ public static abstract class LinkLogic {
+ /**
+ * Empty link logic instance - this is the default
+ * "no special linking or runtime guard behavior"
+ */
+ public static final LinkLogic EMPTY_INSTANCE = new Empty();
+
+ private static final SwitchPoint[] INVALIDATED_SWITCHPOINTS = new SwitchPoint[0];
+
+ private SwitchPoint[] modificationSwitchPoints; //cache
+
+ /** Empty link logic class - allow all linking, no guards */
+ private static final class Empty extends LinkLogic {
+ @Override
+ public boolean canLink(final Object self, final CallSiteDescriptor desc, final LinkRequest request) {
+ return true;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return true;
+ }
+ }
+
+ /**
+ * Get the class representing the empty link logic
+ * @return class representing empty link logic
+ */
+ public static Class<? extends LinkLogic> getEmptyLinkLogicClass() {
+ return Empty.class;
+ }
+
+ /**
+ * Should this callsite relink when an exception is thrown
+ *
+ * @return the relink exception, or null if none
+ */
+ public Class<? extends Throwable> getRelinkException() {
+ return null;
+ }
+
+ /**
+ * Is this link logic class empty - i.e. no special linking logic
+ * supplied
+ *
+ * @param clazz class to check
+ *
+ * @return true if this link logic is empty
+ */
+ public static boolean isEmpty(final Class<? extends LinkLogic> clazz) {
+ return clazz == Empty.class;
+ }
+
+ /**
+ * Is this link logic instance empty - i.e. no special linking logic
+ * supplied
+ *
+ * @return true if this link logic instance is empty
+ */
+ public boolean isEmpty() {
+ return false;
+ }
+
+ /**
+ * Given a callsite, can we link this method based on the receiver and
+ * parameters?
+ *
+ * @param self receiver
+ * @param desc callsite descriptor
+ * @param request link request
+ *
+ * @return true if we can link this callsite at this time
+ */
+ public abstract boolean canLink(final Object self, final CallSiteDescriptor desc, final LinkRequest request);
+
+ /**
+ * Given a callsite, do we require an extra guard for specialization to
+ * go through?
+ *
+ * @param self receiver
+ *
+ * @return true if a guard is to be woven into the callsite
+ */
+ public boolean needsGuard(final Object self) {
+ return true;
+ }
+
+ /**
+ * Given a callsite, and optional arguments, do we need an extra guard
+ * for specialization to go through - this guard can be a function of
+ * the arguments too
+ *
+ * @param self receiver
+ * @param args arguments
+ *
+ * @return true if a guard is to be woven into the callsite
+ */
+ public boolean needsGuard(final Object self, final Object... args) {
+ return true;
+ }
+
+ /**
+ * Given a callsite, and optional arguments, return any extra guard we
+ * might need for specialization as a method handle.
+ *
+ * @return methodhandle for guard, or null if no guard is needed
+ */
+ public MethodHandle getGuard() {
+ return null;
+ }
+
+ /**
+ * Return the modification SwitchPoint of a particular index from this OptimisticBuiltins
+ * If none exists, one is created and that one is return.
+ *
+ * The implementor must map indexes to specific SwitchPoints for specific events and keep
+ * track of what they mean, for example NativeArray.LENGTH_NOT_WRITABLE_SWITCHPOINT
+ * might be a useful index mapping
+ *
+ * @param index index for SwitchPoint to get or create
+ * @return modification SwitchPoint of particular index for the receiver
+ */
+ public SwitchPoint getOrCreateModificationSwitchPoint(final int index) {
+ return null;
+ }
+
+ /**
+ * Return the modification SwitchPoint from this OptimisticBuiltins. If none
+ * exists, one is created and that one is return.
+ *
+ * @return modification SwitchPoint for the receiver
+ */
+ public SwitchPoint[] getOrCreateModificationSwitchPoints() {
+ return null;
+ }
+
+ /**
+ * Hook to invalidate a modification SwitchPoint by index.
+ *
+ * @param index index for SwitchPoint to invalidate
+ */
+ public void invalidateModificationSwitchPoint(final int index) {
+ //empty
+ }
+
+ /**
+ * Hook to invalidate all modification SwitchPoints for a receiver
+ */
+ public void invalidateModificationSwitchPoints() {
+ //empty
+ }
+
+ /**
+ * Check whether the receiver has an invalidated modification SwitchPoint.
+ *
+ * @param index index for the modification SwitchPoint
+ * @return true if the particular SwitchPoint at the index is invalidated
+ */
+ public boolean hasInvalidatedModificationSwitchPoint(final int index) {
+ return false;
+ }
+
+ /**
+ * Check whether at least one of the modification SwitchPoints has been
+ * invalidated
+ * @return true if one of the SwitchPoints has been invalidated
+ */
+ public boolean hasInvalidatedModificationSwitchPoints() {
+ return false;
+ }
+
+ /**
+ * Check whether this OptimisticBuiltins has a SwitchPoints of particular
+ * index.
+ *
+ * As creation overhead for a SwitchPoint is non-zero, we have to create them lazily instead of,
+ * e.g. in the constructor of every subclass.
+ *
+ * @param index index for the modification SwitchPoint
+ * @return true if a modification SwitchPoint exists, no matter if it has been invalidated or not
+ */
+ public boolean hasModificationSwitchPoint(final int index) {
+ return false;
+ }
+
+ /**
+ * Check whether this OptimisticBuiltins has SwitchPoints.
+ *
+ * As creation overhead for a SwitchPoint is non-zero, we have to create them lazily instead of,
+ * e.g. in the constructor of every subclass.
+ *
+ * @return true if a modification SwitchPoint exists, no matter if it has been invalidated or not
+ */
+ public boolean hasModificationSwitchPoints() {
+ return false;
+ }
+
+ /**
+ * Check, given a link request and a receiver, if this specialization
+ * fits This is used by the linker in {@link ScriptFunction} to figure
+ * out if an optimistic builtin can be linked when first discovered
+ *
+ * @param self receiver
+ * @param desc callsite descriptor
+ * @param request link request
+
+ * @return true if we can link, false otherwise - that means we have to
+ * pick a non specialized target
+ */
+ public boolean checkLinkable(final Object self, final CallSiteDescriptor desc, final LinkRequest request) {
+ // no matter what the modification switchpoints are, if any of them are invalidated,
+ // we can't link. Side effect is that if it's the first time we see this callsite,
+ // we have to create the SwitchPoint(s) so future modification switchpoint invalidations
+ // relink it
+ final SwitchPoint[] sps = getOrCreateModificationSwitchPoints(self);
+ if (sps == INVALIDATED_SWITCHPOINTS) {
+ // nope, can't do the fast link as this assumption
+ // has been invalidated already, e.g. length of an
+ // array set to not writable
+ return false;
+ }
+ modificationSwitchPoints = sps; //cache
+
+ // check the link guard, if it says we can link, go ahead
+ return canLink(self, desc, request);
+ }
+
+ private SwitchPoint[] getOrCreateModificationSwitchPoints(final Object self) {
+ final SwitchPoint[] sps = getOrCreateModificationSwitchPoints(); //ask for all my switchpoints
+ if (sps != null) { //switchpoint exists, but some may be invalidated
+ for (final SwitchPoint sp : sps) {
+ if (sp.hasBeenInvalidated()) {
+ return INVALIDATED_SWITCHPOINTS;
+ }
+ }
+ }
+ return sps;
+ }
+
+ /**
+ * Get the cached modification switchpoints. Only possible to do after a link
+ * check call has been performed, one that has answered "true", or you will get the
+ * wrong information.
+ *
+ * Should be used only from {@link ScriptFunction#findCallMethod}
+ *
+ * @return cached modification switchpoints for this callsite, null if none
+ */
+ public SwitchPoint[] getModificationSwitchPoints() {
+ return modificationSwitchPoints == null ? null : modificationSwitchPoints.clone();
+ }
+ }
+
+ /**
+ * name override for return value polymorphism, for example we can't have
+ * pop(V)I and pop(V)D in the same Java class, so they need to be named,
+ * e.g. popInt(V)I and popDouble(V)D for disambiguation, however, their
+ * names still need to resolve to "pop" to JavaScript so we can still
+ * specialize on return values and so that the linker can find them
+ *
+ * @return name, "" means no override, use the Java function name, e.g.
+ * "push"
+ */
+ String name() default "";
+
+ /**
+ * Return the guard for this specialized function. The default is no guard.
+ *
+ * @return guard
+ */
+ Class<?> linkLogic() default LinkLogic.Empty.class;
+
+ /**
+ * Is this a specialized constructor?
+ */
+ boolean isConstructor() default false;
+
+ /**
+ * Can this function throw UnwarrantedOptimismExceptions? This works just
+ * like the normal functions, but we need the function to be
+ * immutable/non-state modifying, as we can't generate continuations for
+ * native code. Luckily a lot of the methods we want to specialize have this
+ * property
+ */
+ boolean isOptimistic() default false;
}
diff --git a/src/jdk/nashorn/internal/parser/AbstractParser.java b/src/jdk/nashorn/internal/parser/AbstractParser.java
index 589fb9c1..3eb45cfb 100644
--- a/src/jdk/nashorn/internal/parser/AbstractParser.java
+++ b/src/jdk/nashorn/internal/parser/AbstractParser.java
@@ -30,7 +30,6 @@ import static jdk.nashorn.internal.parser.TokenType.DIRECTIVE_COMMENT;
import static jdk.nashorn.internal.parser.TokenType.EOF;
import static jdk.nashorn.internal.parser.TokenType.EOL;
import static jdk.nashorn.internal.parser.TokenType.IDENT;
-
import java.util.HashMap;
import java.util.Map;
import jdk.nashorn.internal.ir.IdentNode;
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/AccessorProperty.java b/src/jdk/nashorn/internal/runtime/AccessorProperty.java
index 44b3c3b0..e2935368 100644
--- a/src/jdk/nashorn/internal/runtime/AccessorProperty.java
+++ b/src/jdk/nashorn/internal/runtime/AccessorProperty.java
@@ -36,7 +36,6 @@ import static jdk.nashorn.internal.lookup.MethodHandleFactory.stripName;
import static jdk.nashorn.internal.runtime.JSType.getAccessorTypeIndex;
import static jdk.nashorn.internal.runtime.JSType.getNumberOfAccessorTypes;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
-
import java.io.IOException;
import java.io.ObjectInputStream;
import java.lang.invoke.MethodHandle;
@@ -57,9 +56,7 @@ public class AccessorProperty extends Property {
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
private static final MethodHandle REPLACE_MAP = findOwnMH_S("replaceMap", Object.class, Object.class, PropertyMap.class);
- private static final MethodHandle INVALIDATE_SP = findOwnMH_S("invalidateSwitchPoint", Object.class, Object.class, SwitchPoint.class);
-
- private static final SwitchPoint NO_CHANGE_CALLBACK = new SwitchPoint();
+ private static final MethodHandle INVALIDATE_SP = findOwnMH_S("invalidateSwitchPoint", Object.class, AccessorProperty.class, Object.class);
private static final int NOOF_TYPES = getNumberOfAccessorTypes();
private static final long serialVersionUID = 3371720170182154920L;
@@ -221,7 +218,7 @@ public class AccessorProperty extends Property {
* @param setter the property setter or null if non writable, non configurable
*/
private AccessorProperty(final String key, final int flags, final int slot, final MethodHandle getter, final MethodHandle setter) {
- super(key, flags | (getter.type().returnType().isPrimitive() ? IS_NASGEN_PRIMITIVE : 0), slot);
+ super(key, flags | IS_BUILTIN | (getter.type().returnType().isPrimitive() ? IS_NASGEN_PRIMITIVE : 0), slot);
assert !isSpill();
// we don't need to prep the setters these will never be invalidated as this is a nasgen
@@ -602,7 +599,6 @@ public class AccessorProperty extends Property {
private Property getWiderProperty(final Class<?> type) {
return copy(type); //invalidate cache of new property
-
}
private PropertyMap getWiderMap(final PropertyMap oldMap, final Property newProperty) {
@@ -627,8 +623,10 @@ public class AccessorProperty extends Property {
}
@SuppressWarnings("unused")
- private static Object invalidateSwitchPoint(final Object obj, final SwitchPoint sp) {
- SwitchPoint.invalidateAll(new SwitchPoint[] { sp });
+ private static Object invalidateSwitchPoint(final AccessorProperty property, final Object obj) {
+ if (!property.builtinSwitchPoint.hasBeenInvalidated()) {
+ SwitchPoint.invalidateAll(new SwitchPoint[] { property.builtinSwitchPoint });
+ }
return obj;
}
@@ -668,12 +666,8 @@ public class AccessorProperty extends Property {
mh = generateSetter(!forType.isPrimitive() ? Object.class : forType, type);
}
- /**
- * Check if this is a special global name that requires switchpoint invalidation
- */
- final SwitchPoint ccb = getChangeCallback();
- if (ccb != null && ccb != NO_CHANGE_CALLBACK) {
- mh = MH.filterArguments(mh, 0, MH.insertArguments(debugInvalidate(getKey(), ccb), 1, changeCallback));
+ if (isBuiltin()) {
+ mh = MH.filterArguments(mh, 0, debugInvalidate(MH.insertArguments(INVALIDATE_SP, 0, this), getKey()));
}
assert mh.type().returnType() == void.class : mh.type();
@@ -681,25 +675,6 @@ public class AccessorProperty extends Property {
return mh;
}
- /**
- * Get the change callback for this property
- * @return switchpoint that is invalidated when property changes
- */
- protected SwitchPoint getChangeCallback() {
- if (changeCallback == null) {
- try {
- changeCallback = Global.instance().getChangeCallback(getKey());
- } catch (final NullPointerException e) {
- assert !"apply".equals(getKey()) && !"call".equals(getKey());
- //empty
- }
- if (changeCallback == null) {
- changeCallback = NO_CHANGE_CALLBACK;
- }
- }
- return changeCallback;
- }
-
@Override
public final boolean canChangeType() {
if (OBJECT_FIELDS_ONLY) {
@@ -724,7 +699,6 @@ public class AccessorProperty extends Property {
return currentType;
}
-
private MethodHandle debug(final MethodHandle mh, final Class<?> forType, final Class<?> type, final String tag) {
if (!Context.DEBUG || !Global.hasInstance()) {
return mh;
@@ -780,9 +754,9 @@ public class AccessorProperty extends Property {
return mh;
}
- private static MethodHandle debugInvalidate(final String key, final SwitchPoint sp) {
+ private static MethodHandle debugInvalidate(final MethodHandle invalidator, final String key) {
if (!Context.DEBUG || !Global.hasInstance()) {
- return INVALIDATE_SP;
+ return invalidator;
}
final Context context = Context.getContextTrusted();
@@ -790,11 +764,11 @@ public class AccessorProperty extends Property {
return context.addLoggingToHandle(
ObjectClassGenerator.class,
- INVALIDATE_SP,
+ invalidator,
new Supplier<String>() {
@Override
public String get() {
- return "Field change callback for " + key + " triggered: " + sp;
+ return "Field change callback for " + key + " triggered ";
}
});
}
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/CodeInstaller.java b/src/jdk/nashorn/internal/runtime/CodeInstaller.java
index 7a7df467..e2b5afe8 100644
--- a/src/jdk/nashorn/internal/runtime/CodeInstaller.java
+++ b/src/jdk/nashorn/internal/runtime/CodeInstaller.java
@@ -81,13 +81,17 @@ public interface CodeInstaller<T> {
/**
* Store a compiled script for later reuse
+ *
+ * @param cacheKey key to use in cache
* @param source the script source
* @param mainClassName the main class name
* @param classBytes map of class names to class bytes
+ * @param initializers compilation id -> FunctionInitializer map
* @param constants constants array
+ * @param compilationId compilation id
*/
- public void storeScript(String cacheKey, Source source, String mainClassName, Map<String, byte[]> classBytes,
- Map<Integer, FunctionInitializer> initializers, Object[] constants, int compilationId);
+ public void storeScript(final String cacheKey, final Source source, final String mainClassName, final Map<String, byte[]> classBytes,
+ final Map<Integer, FunctionInitializer> initializers, final Object[] constants, final int compilationId);
/**
* Load a previously compiled script
@@ -96,4 +100,21 @@ public interface CodeInstaller<T> {
* @return compiled script data
*/
public StoredScript loadScript(Source source, String functionKey);
+
+ /**
+ * Returns a new code installer that shares most of the functionality of this code installer, but uses a
+ * new, independent class loader.
+ * @return a new code installer with a new independent class loader.
+ */
+ public CodeInstaller<T> withNewLoader();
+
+ /**
+ * Returns true if this code installer is compatible with the other code installer. Compatibility is expected to be
+ * an equivalence relation, and installers are supposed to be compatible with those they create using
+ * {@link #withNewLoader()}.
+ * @param other the other code installer tested for compatibility with this code installer.
+ * @return true if this code installer is compatible with the other code installer.
+ */
+ public boolean isCompatibleWith(CodeInstaller<T> other);
+
}
diff --git a/src/jdk/nashorn/internal/runtime/CodeStore.java b/src/jdk/nashorn/internal/runtime/CodeStore.java
index a736cc36..0748ccc3 100644
--- a/src/jdk/nashorn/internal/runtime/CodeStore.java
+++ b/src/jdk/nashorn/internal/runtime/CodeStore.java
@@ -118,6 +118,8 @@ public abstract class CodeStore implements Loggable {
* @param initializers the function initializers
* @param constants the constants array
* @param compilationId the compilation id
+ *
+ * @return stored script
*/
public StoredScript store(final String functionKey,
final Source source,
@@ -153,11 +155,13 @@ public abstract class CodeStore implements Loggable {
/**
* Returns a new StoredScript instance.
*
+ * @param source the source
* @param mainClassName the main class name
* @param classBytes a map of class bytes
* @param initializers function initializers
* @param constants the constants array
* @param compilationId the compilation id
+ *
* @return The compiled script
*/
public StoredScript storedScriptFor(final Source source, final String mainClassName,
@@ -206,7 +210,7 @@ public abstract class CodeStore implements Loggable {
/**
* Constructor
*
- * @throws IOException
+ * @throws IOException if there are read/write problems with the cache and cache directory
*/
public DirectoryCodeStore() throws IOException {
this(Options.getStringProperty("nashorn.persistent.code.cache", "nashorn_code_cache"), false, DEFAULT_MIN_SIZE);
@@ -216,8 +220,9 @@ public abstract class CodeStore implements Loggable {
* Constructor
*
* @param path directory to store code in
+ * @param readOnly is this a read only code store
* @param minSize minimum file size for caching scripts
- * @throws IOException
+ * @throws IOException if there are read/write problems with the cache and cache directory
*/
public DirectoryCodeStore(final String path, final boolean readOnly, final int minSize) throws IOException {
this.dir = checkDirectory(path, readOnly);
diff --git a/src/jdk/nashorn/internal/runtime/CompiledFunction.java b/src/jdk/nashorn/internal/runtime/CompiledFunction.java
index 28a81dea..b37f94fa 100644
--- a/src/jdk/nashorn/internal/runtime/CompiledFunction.java
+++ b/src/jdk/nashorn/internal/runtime/CompiledFunction.java
@@ -27,18 +27,20 @@ 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;
import java.lang.invoke.MethodType;
import java.lang.invoke.MutableCallSite;
import java.lang.invoke.SwitchPoint;
+import java.util.Collection;
+import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
import java.util.function.Supplier;
import java.util.logging.Level;
-
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.nashorn.internal.codegen.Compiler;
import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
@@ -46,6 +48,7 @@ import jdk.nashorn.internal.codegen.TypeMap;
import jdk.nashorn.internal.codegen.types.ArrayType;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.objects.annotations.SpecializedFunction.LinkLogic;
import jdk.nashorn.internal.runtime.events.RecompilationEvent;
import jdk.nashorn.internal.runtime.linker.Bootstrap;
import jdk.nashorn.internal.runtime.logging.DebugLogger;
@@ -63,6 +66,8 @@ final class CompiledFunction {
private final DebugLogger log;
+ static final Collection<CompiledFunction> NO_FUNCTIONS = Collections.emptySet();
+
/**
* The method type may be more specific than the invoker, if. e.g.
* the invoker is guarded, and a guard with a generic object only
@@ -75,19 +80,38 @@ final class CompiledFunction {
private final int flags; // from FunctionNode
private final MethodType callSiteType;
+ private final Specialization specialization;
+
CompiledFunction(final MethodHandle invoker) {
- this(invoker, null);
+ this(invoker, null, null);
}
- static CompiledFunction createBuiltInConstructor(final MethodHandle invoker) {
- return new CompiledFunction(MH.insertArguments(invoker, 0, false), createConstructorFromInvoker(MH.insertArguments(invoker, 0, true)));
+ static CompiledFunction createBuiltInConstructor(final MethodHandle invoker, final Specialization specialization) {
+ return new CompiledFunction(MH.insertArguments(invoker, 0, false), createConstructorFromInvoker(MH.insertArguments(invoker, 0, true)), specialization);
}
- CompiledFunction(final MethodHandle invoker, final MethodHandle constructor) {
- this(invoker, constructor, 0, null, DebugLogger.DISABLED_LOGGER);
+ CompiledFunction(final MethodHandle invoker, final MethodHandle constructor, final Specialization specialization) {
+ this(invoker, constructor, 0, null, specialization, DebugLogger.DISABLED_LOGGER);
}
- CompiledFunction(final MethodHandle invoker, final MethodHandle constructor, final int flags, final MethodType callSiteType, final DebugLogger log) {
+ CompiledFunction(final MethodHandle invoker, final MethodHandle constructor, final int flags, final MethodType callSiteType, final Specialization specialization, final DebugLogger log) {
+ this.specialization = specialization;
+ if (specialization != null && specialization.isOptimistic()) {
+ /*
+ * An optimistic builtin with isOptimistic=true works like any optimistic generated function, i.e. it
+ * can throw unwarranted optimism exceptions. As native functions trivially can't have parts of them
+ * regenerated as restof methods, this only works if the methods are atomic/functional in their behavior
+ * and doesn't modify state before an UOE can be thrown. If they aren't, we can reexecute a wider version
+ * of the same builtin in a recompilation handler for FinalScriptFunctionData. There are several
+ * candidate methods in Native* that would benefit from this, but I haven't had time to implement any
+ * of them currently. In order to fit in with the relinking framework, the current thinking is
+ * that the methods still take a program point to fit in with other optimistic functions, but
+ * it is set to "first", which is the beginning of the method. The relinker can tell the difference
+ * between builtin and JavaScript functions. This might change. TODO
+ */
+ this.invoker = MH.insertArguments(invoker, invoker.type().parameterCount() - 1, UnwarrantedOptimismException.FIRST_PROGRAM_POINT);
+ throw new AssertionError("Optimistic (UnwarrantedOptimismException throwing) builtin functions are currently not in use");
+ }
this.invoker = invoker;
this.constructor = constructor;
this.flags = flags;
@@ -97,7 +121,7 @@ final class CompiledFunction {
CompiledFunction(final MethodHandle invoker, final RecompilableScriptFunctionData functionData,
final Map<Integer, Type> invalidatedProgramPoints, final MethodType callSiteType, final int flags) {
- this(invoker, null, flags, callSiteType, functionData.getLogger());
+ this(invoker, null, flags, callSiteType, null, functionData.getLogger());
if ((flags & FunctionNode.IS_DEOPTIMIZABLE) != 0) {
optimismInfo = new OptimismInfo(functionData, invalidatedProgramPoints);
} else {
@@ -105,10 +129,45 @@ final class CompiledFunction {
}
}
+ static CompiledFunction createBuiltInConstructor(final MethodHandle invoker) {
+ return new CompiledFunction(MH.insertArguments(invoker, 0, false), createConstructorFromInvoker(MH.insertArguments(invoker, 0, true)), null);
+ }
+
+ boolean isSpecialization() {
+ return specialization != null;
+ }
+
+ boolean hasLinkLogic() {
+ return getLinkLogicClass() != null;
+ }
+
+ Class<? extends LinkLogic> getLinkLogicClass() {
+ if (isSpecialization()) {
+ final Class<? extends LinkLogic> linkLogicClass = specialization.getLinkLogicClass();
+ assert !LinkLogic.isEmpty(linkLogicClass) : "empty link logic classes should have been removed by nasgen";
+ return linkLogicClass;
+ }
+ return null;
+ }
+
int getFlags() {
return flags;
}
+ /**
+ * An optimistic specialization is one that can throw UnwarrantedOptimismException.
+ * This is allowed for native methods, as long as they are functional, i.e. don't change
+ * any state between entering and throwing the UOE. Then we can re-execute a wider version
+ * of the method in the continuation. Rest-of method generation for optimistic builtins is
+ * of course not possible, but this approach works and fits into the same relinking
+ * framework
+ *
+ * @return true if optimistic builtin
+ */
+ boolean isOptimistic() {
+ return isSpecialization() ? specialization.isOptimistic() : false;
+ }
+
boolean isApplyToCall() {
return (flags & FunctionNode.HAS_APPLY_TO_CALL_SPECIALIZATION) != 0;
}
@@ -119,7 +178,19 @@ final class CompiledFunction {
@Override
public String toString() {
- return "[invokerType=" + invoker.type() + " ctor=" + constructor + " weight=" + weight() + " isApplyToCall=" + isApplyToCall() + "]";
+ final StringBuilder sb = new StringBuilder();
+ final Class<? extends LinkLogic> linkLogicClass = getLinkLogicClass();
+
+ sb.append("[invokerType=").
+ append(invoker.type()).
+ append(" ctor=").
+ append(constructor).
+ append(" weight=").
+ append(weight()).
+ append(" linkLogic=").
+ append(linkLogicClass != null ? linkLogicClass.getSimpleName() : "none");
+
+ return sb.toString();
}
boolean needsCallee() {
@@ -281,10 +352,12 @@ final class CompiledFunction {
if (other == null) {
return true;
}
- return betterThanFinal(type(), other.type(), callSiteMethodType);
+ return betterThanFinal(this, other, callSiteMethodType);
}
- static boolean betterThanFinal(final MethodType thisMethodType, final MethodType otherMethodType, final MethodType callSiteMethodType) {
+ private static boolean betterThanFinal(final CompiledFunction cf, final CompiledFunction other, final MethodType callSiteMethodType) {
+ final MethodType thisMethodType = cf.type();
+ final MethodType otherMethodType = other.type();
final int thisParamCount = getParamCount(thisMethodType);
final int otherParamCount = getParamCount(otherMethodType);
final int callSiteRawParamCount = getParamCount(callSiteMethodType);
@@ -406,7 +479,17 @@ final class CompiledFunction {
return false;
}
- throw new AssertionError(thisMethodType + " identically applicable to " + otherMethodType + " for " + callSiteMethodType); // Signatures are identical
+ //if they are equal, pick the specialized one first
+ if (cf.isSpecialization() != other.isSpecialization()) {
+ return cf.isSpecialization(); //always pick the specialized version if we can
+ }
+
+ if (cf.isSpecialization() && other.isSpecialization()) {
+ return cf.getLinkLogicClass() != null; //pick link logic specialization above generic specializations
+ }
+
+ // Signatures are identical
+ throw new AssertionError(thisMethodType + " identically applicable to " + otherMethodType + " for " + callSiteMethodType);
}
private static Type[] toTypeWithoutCallee(final MethodType type, final int thisIndex) {
@@ -427,8 +510,8 @@ final class CompiledFunction {
return ((ArrayType)paramTypes[paramTypes.length - 1]).getElementType();
}
- boolean matchesCallSite(final MethodType callSiteType, final boolean pickVarArg) {
- if (callSiteType.equals(this.callSiteType)) {
+ boolean matchesCallSite(final MethodType other, final boolean pickVarArg) {
+ if (other.equals(this.callSiteType)) {
return true;
}
final MethodType type = type();
@@ -438,7 +521,7 @@ final class CompiledFunction {
return pickVarArg;
}
- final int csParamCount = getParamCount(callSiteType);
+ final int csParamCount = getParamCount(other);
final boolean csIsVarArg = csParamCount == Integer.MAX_VALUE;
final int thisThisIndex = needsCallee() ? 1 : 0; // Index of "this" parameter in this function's type
@@ -447,7 +530,7 @@ final class CompiledFunction {
// We must match all incoming parameters, except "this". Starting from 1 to skip "this".
for(int i = 1; i < minParams; ++i) {
final Type fnType = Type.typeFor(type.parameterType(i + thisThisIndex));
- final Type csType = csIsVarArg ? Type.OBJECT : Type.typeFor(callSiteType.parameterType(i + 1));
+ final Type csType = csIsVarArg ? Type.OBJECT : Type.typeFor(other.parameterType(i + 1));
if(!fnType.isEquivalentTo(csType)) {
return false;
}
@@ -669,9 +752,9 @@ final class CompiledFunction {
return sb.toString();
}
- private void logRecompile(final String reason, final FunctionNode fn, final MethodType callSiteType, final Map<Integer, Type> ipp) {
+ private void logRecompile(final String reason, final FunctionNode fn, final MethodType type, final Map<Integer, Type> ipp) {
if (log.isEnabled()) {
- log.info(reason, DebugLogger.quote(fn.getName()), " signature: ", callSiteType, " ", toStringInvalidations(ipp));
+ log.info(reason, DebugLogger.quote(fn.getName()), " signature: ", type, " ", toStringInvalidations(ipp));
}
}
@@ -704,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) {
@@ -711,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;
@@ -732,8 +816,6 @@ final class CompiledFunction {
compiler.persistClassInfo(cacheKey, normalFn);
}
- FunctionNode fn2 = effectiveOptInfo.reparse();
- fn2 = compiler.compile(fn2, CompilationPhases.COMPILE_UPTO_BYTECODE);
log.info("Done.");
final boolean canBeDeoptimized = normalFn.canBeDeoptimized();
@@ -749,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) {
@@ -841,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 aa3306a4..23f0e3ab 100644
--- a/src/jdk/nashorn/internal/runtime/Context.java
+++ b/src/jdk/nashorn/internal/runtime/Context.java
@@ -33,13 +33,13 @@ import static jdk.nashorn.internal.runtime.CodeStore.newCodeStore;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
import static jdk.nashorn.internal.runtime.Source.sourceFor;
-
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
+import java.lang.invoke.SwitchPoint;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.reflect.Field;
@@ -127,6 +127,16 @@ public final class Context {
private static MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
private static MethodType CREATE_PROGRAM_FUNCTION_TYPE = MethodType.methodType(ScriptFunction.class, ScriptObject.class);
+ /**
+ * Keeps track of which builtin prototypes and properties have been relinked
+ * Currently we are conservative and associate the name of a builtin class with all
+ * its properties, so it's enough to invalidate a property to break all assumptions
+ * about a prototype. This can be changed to a more fine grained approach, but no one
+ * ever needs this, given the very rare occurance of swapping out only parts of
+ * a builtin v.s. the entire builtin object
+ */
+ private final Map<String, SwitchPoint> builtinSwitchPoints = new HashMap<>();
+
/* Force DebuggerSupport to be loaded. */
static {
DebuggerSupport.FORCELOAD = true;
@@ -140,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;
@@ -148,7 +165,7 @@ public final class Context {
}
/**
- * Return the context for this installer
+ * Return the script environment for this installer
* @return ScriptEnvironment
*/
@Override
@@ -158,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);
}
@@ -212,6 +231,24 @@ public final class Context {
}
return null;
}
+
+ @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);
+ }
+
+ @Override
+ public boolean isCompatibleWith(final CodeInstaller<ScriptEnvironment> other) {
+ if (other instanceof ContextCodeInstaller) {
+ final ContextCodeInstaller cci = (ContextCodeInstaller)other;
+ return cci.context == context && cci.codeSource == codeSource;
+ }
+ return false;
+ }
}
/** Is Context global debug mode enabled ? */
@@ -1113,6 +1150,9 @@ public final class Context {
StoredScript storedScript = null;
FunctionNode functionNode = null;
+ // We only use the code store here if optimistic types are disabled. With optimistic types,
+ // code is stored per function in RecompilableScriptFunctionData.
+ // TODO: This should really be triggered by lazy compilation, not optimistic types.
final boolean useCodeStore = env._persistent_cache && !env._parse_only && !env._optimistic_types;
final String cacheKey = useCodeStore ? CodeStore.getCacheKey(0, null) : null;
@@ -1199,7 +1239,7 @@ public final class Context {
final String mainClassName = storedScript.getMainClassName();
final byte[] mainClassBytes = classBytes.get(mainClassName);
final Class<?> mainClass = installer.install(mainClassName, mainClassBytes);
- final Map<Integer, FunctionInitializer> initialzers = storedScript.getInitializers();
+ final Map<Integer, FunctionInitializer> initializers = storedScript.getInitializers();
installedClasses.put(mainClassName, mainClass);
@@ -1219,8 +1259,8 @@ public final class Context {
if (constant instanceof RecompilableScriptFunctionData) {
final RecompilableScriptFunctionData data = (RecompilableScriptFunctionData) constant;
data.initTransients(source, installer);
- if (initialzers != null) {
- final FunctionInitializer initializer = initialzers.get(data.getFunctionNodeId());
+ final FunctionInitializer initializer = initializers.get(data.getFunctionNodeId());
+ if (initializer != null) {
initializer.setCode(installedClasses.get(initializer.getClassName()));
data.initializeCode(initializer);
}
@@ -1371,4 +1411,34 @@ public final class Context {
return null;
}
+ /**
+ * This is a special kind of switchpoint used to guard builtin
+ * properties and prototypes. In the future it might contain
+ * logic to e.g. multiple switchpoint classes.
+ */
+ public static final class BuiltinSwitchPoint extends SwitchPoint {
+ //empty
+ }
+
+ /**
+ * Create a new builtin switchpoint and return it
+ * @param name key name
+ * @return new builtin switchpoint
+ */
+ public SwitchPoint newBuiltinSwitchPoint(final String name) {
+ assert builtinSwitchPoints.get(name) == null;
+ final SwitchPoint sp = new BuiltinSwitchPoint();
+ builtinSwitchPoints.put(name, sp);
+ return sp;
+ }
+
+ /**
+ * Return the builtin switchpoint for a particular key name
+ * @param name key name
+ * @return builtin switchpoint or null if none
+ */
+ public SwitchPoint getBuiltinSwitchPoint(final String name) {
+ return builtinSwitchPoints.get(name);
+ }
+
}
diff --git a/src/jdk/nashorn/internal/runtime/Debug.java b/src/jdk/nashorn/internal/runtime/Debug.java
index c152418a..a2d136fc 100644
--- a/src/jdk/nashorn/internal/runtime/Debug.java
+++ b/src/jdk/nashorn/internal/runtime/Debug.java
@@ -26,7 +26,6 @@
package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.parser.TokenType.EOF;
-
import jdk.nashorn.internal.parser.Lexer;
import jdk.nashorn.internal.parser.Token;
import jdk.nashorn.internal.parser.TokenStream;
@@ -42,12 +41,12 @@ public final class Debug {
/**
* Return the topmost JavaScript frame in a stack trace
- * @param e
+ * @param t throwable that contains the stack trace
* @return line describing the topmost JavaScript frame
*/
- public static String firstJSFrame(final Throwable e) {
- for (final StackTraceElement ste : e.getStackTrace()) {
- if(ECMAErrors.isScriptFrame(ste)) {
+ public static String firstJSFrame(final Throwable t) {
+ for (final StackTraceElement ste : t.getStackTrace()) {
+ if (ECMAErrors.isScriptFrame(ste)) {
return ste.toString();
}
}
diff --git a/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java b/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java
index f75dc829..c868aab2 100644
--- a/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java
+++ b/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java
@@ -60,13 +60,13 @@ final class FinalScriptFunctionData extends ScriptFunctionData {
* @param specs specializations
* @param flags {@link ScriptFunctionData} flags
*/
- FinalScriptFunctionData(final String name, final MethodHandle mh, final MethodHandle[] specs, final int flags) {
+ FinalScriptFunctionData(final String name, final MethodHandle mh, final Specialization[] specs, final int flags) {
super(name, methodHandleArity(mh), flags);
addInvoker(mh);
if (specs != null) {
- for (final MethodHandle spec : specs) {
- addInvoker(spec);
+ for (final Specialization spec : specs) {
+ addInvoker(spec.getMethodHandle(), spec);
}
}
}
@@ -114,16 +114,25 @@ final class FinalScriptFunctionData extends ScriptFunctionData {
return MethodType.genericMethodType(max + 1);
}
- private void addInvoker(final MethodHandle mh) {
+ private CompiledFunction addInvoker(final MethodHandle mh, final Specialization specialization) {
assert !needsCallee(mh);
+
+ final CompiledFunction invoker;
if (isConstructor(mh)) {
// only nasgen constructors: (boolean, self, args) are subject to binding a boolean newObj. isConstructor
// is too conservative a check. However, isConstructor(mh) always implies isConstructor param
assert isConstructor();
- code.add(CompiledFunction.createBuiltInConstructor(mh));
+ invoker = CompiledFunction.createBuiltInConstructor(mh);
} else {
- code.add(new CompiledFunction(mh));
+ invoker = new CompiledFunction(mh, null, specialization);
}
+ code.add(invoker);
+
+ return invoker;
+ }
+
+ private CompiledFunction addInvoker(final MethodHandle mh) {
+ return addInvoker(mh, null);
}
private static int methodHandleArity(final MethodHandle mh) {
diff --git a/src/jdk/nashorn/internal/runtime/FindProperty.java b/src/jdk/nashorn/internal/runtime/FindProperty.java
index 06f682bf..b4e00124 100644
--- a/src/jdk/nashorn/internal/runtime/FindProperty.java
+++ b/src/jdk/nashorn/internal/runtime/FindProperty.java
@@ -79,6 +79,8 @@ public final class FindProperty {
*
* @param type type of getter, e.g. int.class if we want a function with {@code get()I} signature
* @param programPoint program point, or INVALID_PROGRAM_POINT if pessimistic
+ * @param request link request
+ *
* @return method handle for the getter
*/
public MethodHandle getGetter(final Class<?> type, final int programPoint, final LinkRequest request) {
@@ -102,6 +104,7 @@ public final class FindProperty {
*
* @param type type of setter, e.g. int.class if we want a function with {@code set(I)V} signature
* @param strict are we in strict mode
+ * @param request link request
*
* @return method handle for the getter
*/
diff --git a/src/jdk/nashorn/internal/runtime/GlobalConstants.java b/src/jdk/nashorn/internal/runtime/GlobalConstants.java
index 3a104812..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;
}
@@ -358,8 +359,12 @@ public final class GlobalConstants implements Loggable {
* @param c constant value
* @return method handle (with dummy receiver) that returns this constant
*/
+ public static MethodHandle staticConstantGetter(final Object c) {
+ return MH.dropArguments(JSType.unboxConstant(c), 0, Object.class);
+ }
+
private MethodHandle constantGetter(final Object c) {
- final MethodHandle mh = MH.dropArguments(JSType.unboxConstant(c), 0, Object.class);
+ final MethodHandle mh = staticConstantGetter(c);
if (log.isEnabled()) {
return MethodHandleFactory.addDebugPrintout(log, Level.FINEST, mh, "getting as constant");
}
@@ -400,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/GlobalFunctions.java b/src/jdk/nashorn/internal/runtime/GlobalFunctions.java
index 2776cc66..c7094adc 100644
--- a/src/jdk/nashorn/internal/runtime/GlobalFunctions.java
+++ b/src/jdk/nashorn/internal/runtime/GlobalFunctions.java
@@ -42,12 +42,30 @@ public final class GlobalFunctions {
/** Methodhandle (specialized) to implementation of ECMA 15.1.2.2, parseInt */
public static final MethodHandle PARSEINT_OI = findOwnMH("parseInt", double.class, Object.class, Object.class, int.class);
+ /** ParseInt - NaN for booleans (thru string conversion to number conversion) */
+ public static final MethodHandle PARSEINT_Z = MH.dropArguments(MH.dropArguments(MH.constant(double.class, Double.NaN), 0, boolean.class), 0, Object.class);
+
+ /** ParseInt - identity for ints */
+ public static final MethodHandle PARSEINT_I = MH.dropArguments(MH.identity(int.class), 0, Object.class);
+
+ /** ParseInt - identity for longs */
+ public static final MethodHandle PARSEINT_J = MH.dropArguments(MH.identity(long.class), 0, Object.class);
+
/** Methodhandle (specialized) to implementation of ECMA 15.1.2.2, parseInt */
public static final MethodHandle PARSEINT_O = findOwnMH("parseInt", double.class, Object.class, Object.class);
/** Methodhandle to implementation of ECMA 15.1.2.3, parseFloat */
public static final MethodHandle PARSEFLOAT = findOwnMH("parseFloat", double.class, Object.class, Object.class);
+ /** isNan for integers - always false */
+ public static final MethodHandle IS_NAN_I = MH.dropArguments(MH.constant(boolean.class, false), 0, Object.class);
+
+ /** isNan for longs - always false */
+ public static final MethodHandle IS_NAN_J = MH.dropArguments(MH.constant(boolean.class, false), 0, Object.class);
+
+ /** IsNan for doubles - use Double.isNaN */
+ public static final MethodHandle IS_NAN_D = MH.dropArguments(MH.findStatic(MethodHandles.lookup(), Double.class, "isNaN", MH.type(boolean.class, double.class)), 0, Object.class);
+
/** Methodhandle to implementation of ECMA 15.1.2.4, isNaN */
public static final MethodHandle IS_NAN = findOwnMH("isNaN", boolean.class, Object.class, Object.class);
diff --git a/src/jdk/nashorn/internal/runtime/OptimisticBuiltins.java b/src/jdk/nashorn/internal/runtime/OptimisticBuiltins.java
new file mode 100644
index 00000000..6829ddeb
--- /dev/null
+++ b/src/jdk/nashorn/internal/runtime/OptimisticBuiltins.java
@@ -0,0 +1,65 @@
+/*
+ * 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 jdk.nashorn.internal.objects.annotations.SpecializedFunction;
+import jdk.nashorn.internal.objects.annotations.SpecializedFunction.LinkLogic;
+
+/**
+ * This is an interface for classes that need custom linkage logic. This means Native objects
+ * that contain optimistic native methods, that need special/extra rules for linking, guards and
+ * SwitchPointing, known and internal to the Native object for its linkage
+ */
+public interface OptimisticBuiltins {
+
+ /**
+ * Return an instance of the linking logic we need for a particular LinkLogic
+ * subclass, gotten from the compile time annotation of a specialized builtin method
+ * No assumptions can be made about the lifetime of the instance. The receiver may
+ * keep it as a perpetual final instance field or create new linking logic depending
+ * on its current state for each call, depending on if the global state has changed
+ * or other factors
+ *
+ * @param clazz linking logic class
+ * @return linking logic instance for this class
+ */
+ public SpecializedFunction.LinkLogic getLinkLogic(final Class<? extends LinkLogic> clazz);
+
+ /**
+ * Does this link logic vary depending on which instance we are working with.
+ * Then we have to sort out certain primitives, as they are created as new
+ * objects in the wrapFilter by JavaScript semantics. An example of instance only
+ * assumptions are switchPoints per instance, as in NativeArray. NativeString is
+ * fine, as it's only static.
+ *
+ * TODO: finer granularity on this, on the function level so certain functions
+ * are forbidden only. Currently we don't have enough specialization to bump into this
+ *
+ * @return true if there are per instance assumptions for the optimism
+ */
+ public boolean hasPerInstanceAssumptions();
+
+}
diff --git a/src/jdk/nashorn/internal/runtime/Property.java b/src/jdk/nashorn/internal/runtime/Property.java
index 1f9f1459..f57246ca 100644
--- a/src/jdk/nashorn/internal/runtime/Property.java
+++ b/src/jdk/nashorn/internal/runtime/Property.java
@@ -28,7 +28,6 @@ package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.runtime.PropertyDescriptor.CONFIGURABLE;
import static jdk.nashorn.internal.runtime.PropertyDescriptor.ENUMERABLE;
import static jdk.nashorn.internal.runtime.PropertyDescriptor.WRITABLE;
-
import java.io.Serializable;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.SwitchPoint;
@@ -84,6 +83,9 @@ public abstract class Property implements Serializable {
*/
public static final int IS_NASGEN_PRIMITIVE = 1 << 6;
+ /** Is this a builtin property, e.g. Function.prototype.apply */
+ public static final int IS_BUILTIN = 1 << 7;
+
/** Is this property bound to a receiver? This means get/set operations will be delegated to
* a statically defined object instead of the object passed as callsite parameter. */
public static final int IS_BOUND = 1 << 7;
@@ -101,7 +103,7 @@ public abstract class Property implements Serializable {
private final int slot;
/** SwitchPoint that is invalidated when property is changed, optional */
- protected transient SwitchPoint changeCallback;
+ protected transient SwitchPoint builtinSwitchPoint;
private static final long serialVersionUID = 2099814273074501176L;
@@ -125,10 +127,10 @@ public abstract class Property implements Serializable {
* @param property source property
*/
Property(final Property property, final int flags) {
- this.key = property.key;
- this.slot = property.slot;
- this.changeCallback = property.changeCallback;
- this.flags = flags;
+ this.key = property.key;
+ this.slot = property.slot;
+ this.builtinSwitchPoint = property.builtinSwitchPoint;
+ this.flags = flags;
}
/**
@@ -182,8 +184,26 @@ public abstract class Property implements Serializable {
* changed
* @param sp SwitchPoint to use for change callback
*/
- public final void setChangeCallback(final SwitchPoint sp) {
- this.changeCallback = sp;
+ public final void setBuiltinSwitchPoint(final SwitchPoint sp) {
+ this.builtinSwitchPoint = sp;
+ }
+
+ /**
+ * Builtin properties have an invalidation switchpoint that is
+ * invalidated when they are set, this is a getter for it
+ * @return builtin switchpoint, or null if none
+ */
+ public final SwitchPoint getBuiltinSwitchPoint() {
+ return builtinSwitchPoint;
+ }
+
+ /**
+ * Checks if this is a builtin property, this means that it has
+ * a builtin switchpoint that hasn't been invalidated by a setter
+ * @return true if builtin, untouched (unset) property
+ */
+ public boolean isBuiltin() {
+ return builtinSwitchPoint != null && !builtinSwitchPoint.hasBeenInvalidated();
}
/**
diff --git a/src/jdk/nashorn/internal/runtime/PropertyMap.java b/src/jdk/nashorn/internal/runtime/PropertyMap.java
index d8e76082..61912332 100644
--- a/src/jdk/nashorn/internal/runtime/PropertyMap.java
+++ b/src/jdk/nashorn/internal/runtime/PropertyMap.java
@@ -950,7 +950,7 @@ public final class PropertyMap implements Iterable<Object>, Serializable {
@Override
public void remove() {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException("remove");
}
}
diff --git a/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java b/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java
index 2d5ccc05..6b2c7620 100644
--- a/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java
+++ b/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java
@@ -31,6 +31,7 @@ import java.io.IOException;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -42,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;
@@ -78,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;
@@ -126,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,
@@ -133,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),
@@ -157,6 +164,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
nfn.setParent(this);
}
+ this.serializedAst = serializedAst;
createLogger();
}
@@ -211,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;
}
/**
@@ -268,7 +273,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
if (this.source == null && this.installer == null) {
this.source = src;
this.installer = inst;
- } else if (this.source != src || this.installer != inst) {
+ } else if (this.source != src || !this.installer.isCompatibleWith(inst)) {
// Existing values must be same as those passed as parameters
throw new IllegalArgumentException();
}
@@ -353,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(
@@ -362,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);
@@ -377,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;
}
@@ -407,6 +432,17 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
return getCompiler(fn, actualCallSiteType, newLocals(runtimeScope), null, null);
}
+ /**
+ * Returns a code installer for installing new code. If we're using either optimistic typing or loader-per-compile,
+ * then asks for a code installer with a new class loader; otherwise just uses the current installer. We use
+ * a new class loader with optimistic typing so that deoptimized code can get reclaimed by GC.
+ * @return a code installer for installing new code.
+ */
+ private CodeInstaller<ScriptEnvironment> getInstallerForNewCode() {
+ final ScriptEnvironment env = installer.getOwner();
+ return env._optimistic_types || env._loader_per_compile ? installer.withNewLoader() : installer;
+ }
+
Compiler getCompiler(final FunctionNode functionNode, final MethodType actualCallSiteType,
final ScriptObject runtimeScope, final Map<Integer, Type> invalidatedProgramPoints,
final int[] continuationEntryPoints) {
@@ -417,7 +453,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
return new Compiler(
context,
context.getEnv(),
- installer,
+ getInstallerForNewCode(),
functionNode.getSource(), // source
context.getErrorManager(),
isStrict() | functionNode.isStrict(), // is strict
@@ -454,7 +490,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
// CompilationEnvironment#declareLocalSymbol()).
if (log.isEnabled()) {
- log.info("Type specialization of '", functionName, "' signature: ", actualCallSiteType);
+ log.info("Parameter type specialization of '", functionName, "' signature: ", actualCallSiteType);
}
final boolean persistentCache = usePersistentCodeCache() && persist;
@@ -463,17 +499,19 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
final TypeMap typeMap = typeMap(actualCallSiteType);
final Type[] paramTypes = typeMap == null ? null : typeMap.getParameterTypes(functionNodeId);
cacheKey = CodeStore.getCacheKey(functionNodeId, paramTypes);
- final StoredScript script = installer.loadScript(source, cacheKey);
+ final CodeInstaller<ScriptEnvironment> newInstaller = getInstallerForNewCode();
+ final StoredScript script = newInstaller.loadScript(source, cacheKey);
if (script != null) {
Compiler.updateCompilationId(script.getCompilationId());
- return install(script);
+ return installStoredScript(script, newInstaller);
}
}
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);
@@ -481,15 +519,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
return new FunctionInitializer(compiledFn, compiler.getInvalidatedProgramPoints());
}
-
- /**
- * Install this script using the given {@code installer}.
- *
- * @param script the compiled script
- * @return the function initializer
- */
- private FunctionInitializer install(final StoredScript script) {
-
+ private static Map<String, Class<?>> installStoredScriptClasses(final StoredScript script, final CodeInstaller<ScriptEnvironment> installer) {
final Map<String, Class<?>> installedClasses = new HashMap<>();
final Map<String, byte[]> classBytes = script.getClassBytes();
final String mainClassName = script.getMainClassName();
@@ -501,14 +531,25 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
for (final Map.Entry<String, byte[]> entry : classBytes.entrySet()) {
final String className = entry.getKey();
- final byte[] code = entry.getValue();
+ final byte[] bytecode = entry.getValue();
if (className.equals(mainClassName)) {
continue;
}
- installedClasses.put(className, installer.install(className, code));
+ installedClasses.put(className, installer.install(className, bytecode));
}
+ return installedClasses;
+ }
+
+ /**
+ * Install this script using the given {@code installer}.
+ *
+ * @param script the compiled script
+ * @return the function initializer
+ */
+ private FunctionInitializer installStoredScript(final StoredScript script, final CodeInstaller<ScriptEnvironment> newInstaller) {
+ final Map<String, Class<?>> installedClasses = installStoredScriptClasses(script, newInstaller);
final Map<Integer, FunctionInitializer> initializers = script.getInitializers();
assert initializers != null;
@@ -523,7 +564,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
}
}
- installer.initialize(installedClasses.values(), source, constants);
+ newInstaller.initialize(installedClasses.values(), source, constants);
initializer.setCode(installedClasses.get(initializer.getClassName()));
return initializer;
}
@@ -588,15 +629,19 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
return lookupCodeMethod(fn.getCompileUnit().getCode(), type);
}
- MethodHandle lookupCodeMethod(final Class<?> code, final MethodType targetType) {
- log.info("Looking up ", DebugLogger.quote(name), " type=", targetType);
- return MH.findStatic(LOOKUP, code, functionName, targetType);
+ MethodHandle lookupCodeMethod(final Class<?> codeClass, final MethodType targetType) {
+ if (log.isEnabled()) {
+ log.info("Looking up ", DebugLogger.quote(functionName), " type=", targetType);
+ }
+ return MH.findStatic(LOOKUP, codeClass, functionName, targetType);
}
/**
* Initializes this function data with the eagerly generated version of the code. This method can only be invoked
* by the compiler internals in Nashorn and is public for implementation reasons only. Attempting to invoke it
* externally will result in an exception.
+ *
+ * @param initializer FunctionInitializer for this data
*/
public void initializeCode(final FunctionInitializer initializer) {
// Since the method is public, we double-check that we aren't invoked with an inappropriate compile unit.
@@ -656,10 +701,26 @@ 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) {
- CompiledFunction existingBest = super.getBest(callSiteType, runtimeScope);
+ synchronized CompiledFunction getBest(final MethodType callSiteType, final ScriptObject runtimeScope, final Collection<CompiledFunction> forbidden) {
+ CompiledFunction existingBest = super.getBest(callSiteType, runtimeScope, forbidden);
if (existingBest == null) {
existingBest = addCode(compileTypeSpecialization(callSiteType, runtimeScope, true), callSiteType);
}
@@ -723,6 +784,10 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
return functionNodeId;
}
+ /**
+ * Get the source for the script
+ * @return source
+ */
public Source getSource() {
return source;
}
@@ -777,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/ScriptEnvironment.java b/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java
index e6034bf5..cd528a01 100644
--- a/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java
+++ b/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java
@@ -181,9 +181,6 @@ public final class ScriptEnvironment {
/** print symbols and their contents for the script */
public final boolean _print_symbols;
- /** range analysis for known types */
- public final boolean _range_analysis;
-
/** is this environment in scripting mode? */
public final boolean _scripting;
@@ -255,7 +252,6 @@ public final class ScriptEnvironment {
_print_parse = options.getBoolean("print.parse");
_print_lower_parse = options.getBoolean("print.lower.parse");
_print_symbols = options.getBoolean("print.symbols");
- _range_analysis = options.getBoolean("range.analysis");
_scripting = options.getBoolean("scripting");
_strict = options.getBoolean("strict");
_version = options.getBoolean("version");
diff --git a/src/jdk/nashorn/internal/runtime/ScriptFunction.java b/src/jdk/nashorn/internal/runtime/ScriptFunction.java
index d9f71a2a..0ba06b3a 100644
--- a/src/jdk/nashorn/internal/runtime/ScriptFunction.java
+++ b/src/jdk/nashorn/internal/runtime/ScriptFunction.java
@@ -30,26 +30,29 @@ import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
-
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.SwitchPoint;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
-
+import java.util.HashSet;
+import java.util.List;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
import jdk.internal.dynalink.support.Guards;
import jdk.nashorn.internal.codegen.ApplySpecialization;
+import jdk.nashorn.internal.codegen.Compiler;
import jdk.nashorn.internal.codegen.CompilerConstants.Call;
import jdk.nashorn.internal.objects.Global;
import jdk.nashorn.internal.objects.NativeFunction;
-import jdk.nashorn.internal.runtime.ScriptFunctionData;
-import jdk.nashorn.internal.runtime.ScriptObject;
-import jdk.nashorn.internal.runtime.ScriptRuntime;
+import jdk.nashorn.internal.objects.annotations.SpecializedFunction.LinkLogic;
import jdk.nashorn.internal.runtime.linker.Bootstrap;
import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
+import jdk.nashorn.internal.runtime.logging.DebugLogger;
/**
* Runtime representation of a JavaScript function.
@@ -114,7 +117,7 @@ public abstract class ScriptFunction extends ScriptObject {
final MethodHandle methodHandle,
final PropertyMap map,
final ScriptObject scope,
- final MethodHandle[] specs,
+ final Specialization[] specs,
final int flags) {
this(new FinalScriptFunctionData(name, methodHandle, specs, flags), map, scope);
@@ -468,13 +471,12 @@ public abstract class ScriptFunction extends ScriptObject {
protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc, final LinkRequest request) {
final MethodType type = desc.getMethodType();
assert desc.getMethodType().returnType() == Object.class && !NashornCallSiteDescriptor.isOptimistic(desc);
- final CompiledFunction cf = data.getBestConstructor(type, scope);
+ final CompiledFunction cf = data.getBestConstructor(type, scope, CompiledFunction.NO_FUNCTIONS);
final GuardedInvocation bestCtorInv = cf.createConstructorInvocation();
//TODO - ClassCastException
return new GuardedInvocation(pairArguments(bestCtorInv.getInvocation(), type), getFunctionGuard(this, cf.getFlags()), bestCtorInv.getSwitchPoints(), null);
}
- @SuppressWarnings("unused")
private static Object wrapFilter(final Object obj) {
if (obj instanceof ScriptObject || !ScriptFunctionData.isPrimitiveThis(obj)) {
return obj;
@@ -490,6 +492,35 @@ public abstract class ScriptFunction extends ScriptObject {
}
/**
+ * Some receivers are primitive, in that case, according to the Spec we create a new
+ * native object per callsite with the wrap filter. We can only apply optimistic builtins
+ * if there is no per instance state saved for these wrapped objects (e.g. currently NativeStrings),
+ * otherwise we can't create optimistic versions
+ *
+ * @param self receiver
+ * @param linkLogicClass linkLogicClass, or null if no link logic exists
+ * @return link logic instance, or null if one could not be constructed for this receiver
+ */
+ private static LinkLogic getLinkLogic(final Object self, final Class<? extends LinkLogic> linkLogicClass) {
+ if (linkLogicClass == null) {
+ return LinkLogic.EMPTY_INSTANCE; //always OK to link this, specialization but without special linking logic
+ }
+
+ if (!Context.getContextTrusted().getEnv()._optimistic_types) {
+ return null; //if optimistic types are off, optimistic builtins are too
+ }
+
+ final Object wrappedSelf = wrapFilter(self);
+ if (wrappedSelf instanceof OptimisticBuiltins) {
+ if (wrappedSelf != self && ((OptimisticBuiltins)wrappedSelf).hasPerInstanceAssumptions()) {
+ return null; //pessimistic - we created a wrapped object different from the primitive, but the assumptions have instance state
+ }
+ return ((OptimisticBuiltins)wrappedSelf).getLinkLogic(linkLogicClass);
+ }
+ return null;
+ }
+
+ /**
* dyn:call call site signature: (callee, thiz, [args...])
* generated method signature: (callee, thiz, [args...])
*
@@ -547,8 +578,53 @@ public abstract class ScriptFunction extends ScriptObject {
}
} //else just fall through and link as ordinary function or unstable apply
- final int programPoint = NashornCallSiteDescriptor.isOptimistic(desc) ? NashornCallSiteDescriptor.getProgramPoint(desc) : INVALID_PROGRAM_POINT;
- final CompiledFunction cf = data.getBestInvoker(type, scope);
+ int programPoint = INVALID_PROGRAM_POINT;
+ if (NashornCallSiteDescriptor.isOptimistic(desc)) {
+ programPoint = NashornCallSiteDescriptor.getProgramPoint(desc);
+ }
+
+ CompiledFunction cf = data.getBestInvoker(type, scope, CompiledFunction.NO_FUNCTIONS);
+ final Object self = request.getArguments()[1];
+ final Collection<CompiledFunction> forbidden = new HashSet<>();
+
+ //check for special fast versions of the compiled function
+ final List<SwitchPoint> sps = new ArrayList<>();
+ Class<? extends Throwable> exceptionGuard = null;
+
+ while (cf.isSpecialization()) {
+ final Class<? extends LinkLogic> linkLogicClass = cf.getLinkLogicClass();
+ //if linklogic is null, we can always link with the standard mechanism, it's still a specialization
+ final LinkLogic linkLogic = getLinkLogic(self, linkLogicClass);
+
+ if (linkLogic != null && linkLogic.checkLinkable(self, desc, request)) {
+ final DebugLogger log = Context.getContextTrusted().getLogger(Compiler.class);
+
+ if (log.isEnabled()) {
+ log.info("Linking optimistic builtin function: '", name, "' args=", Arrays.toString(request.getArguments()), " desc=", desc);
+ }
+
+ final SwitchPoint[] msps = linkLogic.getModificationSwitchPoints();
+ if (msps != null) {
+ for (final SwitchPoint sp : msps) {
+ if (sp != null) {
+ assert !sp.hasBeenInvalidated();
+ sps.add(sp);
+ }
+ }
+ }
+
+ exceptionGuard = linkLogic.getRelinkException();
+
+ break;
+ }
+
+ //could not link this specialization because link check failed
+ forbidden.add(cf);
+ final CompiledFunction oldCf = cf;
+ cf = data.getBestInvoker(type, scope, forbidden);
+ assert oldCf != cf;
+ }
+
final GuardedInvocation bestInvoker = cf.createFunctionInvocation(type.returnType(), programPoint);
final MethodHandle callHandle = bestInvoker.getInvocation();
@@ -588,7 +664,20 @@ public abstract class ScriptFunction extends ScriptObject {
boundHandle = pairArguments(boundHandle, type);
- return new GuardedInvocation(boundHandle, guard == null ? getFunctionGuard(this, cf.getFlags()) : guard, bestInvoker.getSwitchPoints(), null);
+ if (bestInvoker.getSwitchPoints() != null) {
+ sps.addAll(Arrays.asList(bestInvoker.getSwitchPoints()));
+ }
+ final SwitchPoint[] spsArray = sps.isEmpty() ? null : sps.toArray(new SwitchPoint[sps.size()]);
+
+ return new GuardedInvocation(
+ boundHandle,
+ guard == null ?
+ getFunctionGuard(
+ this,
+ cf.getFlags()) :
+ guard,
+ spsArray,
+ exceptionGuard);
}
private GuardedInvocation createApplyOrCallCall(final boolean isApply, final CallSiteDescriptor desc, final LinkRequest request, final Object[] args) {
@@ -610,7 +699,7 @@ public abstract class ScriptFunction extends ScriptObject {
//box call back to apply
CallSiteDescriptor appliedDesc = desc;
- final SwitchPoint applyToCallSwitchPoint = Global.instance().getChangeCallback("apply");
+ final SwitchPoint applyToCallSwitchPoint = Global.getBuiltinFunctionApplySwitchPoint();
//enough to change the proto switchPoint here
final boolean isApplyToCall = NashornCallSiteDescriptor.isApplyToCall(desc);
@@ -656,7 +745,7 @@ public abstract class ScriptFunction extends ScriptObject {
}
}
- appliedDesc = appliedDesc.changeMethodType(appliedType);
+ appliedDesc = appliedDesc.changeMethodType(appliedType); //no extra args
// Create the same arguments for the delegate linking request that would be passed in an actual apply'd invocation
final Object[] appliedArgs = new Object[isApply ? 3 : appliedType.parameterCount()];
@@ -681,6 +770,7 @@ public abstract class ScriptFunction extends ScriptObject {
// Ask the linker machinery for an invocation of the target function
final LinkRequest appliedRequest = request.replaceArguments(appliedDesc, appliedArgs);
+
GuardedInvocation appliedInvocation;
try {
appliedInvocation = Bootstrap.getLinkerServices().getGuardedInvocation(appliedRequest);
@@ -742,7 +832,7 @@ public abstract class ScriptFunction extends ScriptObject {
// We need to account for the dropped (apply|call) function argument.
guard = MH.dropArguments(guard, 0, descType.parameterType(0));
// Take the "isApplyFunction" guard, and bind it to this function.
- MethodHandle applyFnGuard = MH.insertArguments(IS_APPLY_FUNCTION, 2, this);
+ MethodHandle applyFnGuard = MH.insertArguments(IS_APPLY_FUNCTION, 2, this); //TODO replace this with switchpoint
// Adapt the guard to receive all the arguments that the original guard does.
applyFnGuard = MH.dropArguments(applyFnGuard, 2, guardType.parameterArray());
// Fold the original function guard into our apply guard.
@@ -894,6 +984,7 @@ public abstract class ScriptFunction extends ScriptObject {
return self instanceof ScriptFunction && ((ScriptFunction)self).data == data && arg instanceof ScriptObject;
}
+ //TODO this can probably be removed given that we have builtin switchpoints in the context
@SuppressWarnings("unused")
private static boolean isApplyFunction(final boolean appliedFnCondition, final Object self, final Object expectedSelf) {
// NOTE: we're using self == expectedSelf as we're only using this with built-in functions apply() and call()
diff --git a/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java b/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java
index d92b12d9..3472cda1 100644
--- a/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java
+++ b/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java
@@ -28,13 +28,13 @@ package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
-
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
+import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
@@ -136,7 +136,7 @@ public abstract class ScriptFunctionData implements Serializable {
final MethodHandle boundInvoker = bindInvokeHandle(originalInv.createComposableInvoker(), fn, self, args);
if (isConstructor()) {
- return new CompiledFunction(boundInvoker, bindConstructHandle(originalInv.createComposableConstructor(), fn, args));
+ return new CompiledFunction(boundInvoker, bindConstructHandle(originalInv.createComposableConstructor(), fn, args), null);
}
return new CompiledFunction(boundInvoker);
@@ -224,18 +224,22 @@ public abstract class ScriptFunctionData implements Serializable {
* @param callSiteType callsite type
* @return compiled function object representing the best invoker.
*/
- final CompiledFunction getBestInvoker(final MethodType callSiteType, final ScriptObject runtimeScope) {
- final CompiledFunction cf = getBest(callSiteType, runtimeScope);
+ final CompiledFunction getBestInvoker(final MethodType callSiteType, final ScriptObject runtimeScope) {
+ return getBestInvoker(callSiteType, runtimeScope, CompiledFunction.NO_FUNCTIONS);
+ }
+
+ final CompiledFunction getBestInvoker(final MethodType callSiteType, final ScriptObject runtimeScope, final Collection<CompiledFunction> forbidden) {
+ final CompiledFunction cf = getBest(callSiteType, runtimeScope, forbidden);
assert cf != null;
return cf;
}
- final CompiledFunction getBestConstructor(final MethodType callSiteType, final ScriptObject runtimeScope) {
+ final CompiledFunction getBestConstructor(final MethodType callSiteType, final ScriptObject runtimeScope, final Collection<CompiledFunction> forbidden) {
if (!isConstructor()) {
throw typeError("not.a.constructor", toSource());
}
// Constructor call sites don't have a "this", but getBest is meant to operate on "callee, this, ..." style
- final CompiledFunction cf = getBest(callSiteType.insertParameterTypes(1, Object.class), runtimeScope);
+ final CompiledFunction cf = getBest(callSiteType.insertParameterTypes(1, Object.class), runtimeScope, forbidden);
return cf;
}
@@ -350,7 +354,7 @@ public abstract class ScriptFunctionData implements Serializable {
* scope is not known, but that might cause compilation of code that will need more deoptimization passes.
* @return the best function for the specified call site type.
*/
- CompiledFunction getBest(final MethodType callSiteType, final ScriptObject runtimeScope) {
+ CompiledFunction getBest(final MethodType callSiteType, final ScriptObject runtimeScope, final Collection<CompiledFunction> forbidden) {
assert callSiteType.parameterCount() >= 2 : callSiteType; // Must have at least (callee, this)
assert callSiteType.parameterType(0).isAssignableFrom(ScriptFunction.class) : callSiteType; // Callee must be assignable from script function
@@ -363,8 +367,8 @@ public abstract class ScriptFunctionData implements Serializable {
}
CompiledFunction best = null;
- for(final CompiledFunction candidate: code) {
- if(candidate.betterThanFinal(best, callSiteType)) {
+ for (final CompiledFunction candidate: code) {
+ if (!forbidden.contains(candidate) && candidate.betterThanFinal(best, callSiteType)) {
best = candidate;
}
}
@@ -376,7 +380,7 @@ public abstract class ScriptFunctionData implements Serializable {
abstract boolean isRecompilable();
CompiledFunction getGeneric(final ScriptObject runtimeScope) {
- return getBest(getGenericType(), runtimeScope);
+ return getBest(getGenericType(), runtimeScope, CompiledFunction.NO_FUNCTIONS);
}
/**
@@ -420,7 +424,7 @@ public abstract class ScriptFunctionData implements Serializable {
final List<CompiledFunction> boundList = new LinkedList<>();
final ScriptObject runtimeScope = fn.getScope();
- final CompiledFunction bindTarget = new CompiledFunction(getGenericInvoker(runtimeScope), getGenericConstructor(runtimeScope));
+ final CompiledFunction bindTarget = new CompiledFunction(getGenericInvoker(runtimeScope), getGenericConstructor(runtimeScope), null);
boundList.add(bind(bindTarget, fn, self, allArgs));
return new FinalScriptFunctionData(name, Math.max(0, getArity() - length), boundList, boundFlags);
diff --git a/src/jdk/nashorn/internal/runtime/ScriptObject.java b/src/jdk/nashorn/internal/runtime/ScriptObject.java
index 8d616b58..87d28976 100644
--- a/src/jdk/nashorn/internal/runtime/ScriptObject.java
+++ b/src/jdk/nashorn/internal/runtime/ScriptObject.java
@@ -47,7 +47,6 @@ import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.getArrayIndex;
import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex;
import static jdk.nashorn.internal.runtime.linker.NashornGuards.explicitInstanceOfCheck;
-
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
@@ -820,6 +819,19 @@ public abstract class ScriptObject implements PropertyAccess {
return false;
}
+ private SwitchPoint findBuiltinSwitchPoint(final String key) {
+ for (ScriptObject myProto = getProto(); myProto != null; myProto = myProto.getProto()) {
+ final Property prop = myProto.getMap().findProperty(key);
+ if (prop != null) {
+ final SwitchPoint sp = prop.getBuiltinSwitchPoint();
+ if (sp != null && !sp.hasBeenInvalidated()) {
+ return sp;
+ }
+ }
+ }
+ return null;
+ }
+
/**
* Add a new property to the object.
* <p>
@@ -923,10 +935,10 @@ public abstract class ScriptObject implements PropertyAccess {
* creating setters that probably aren't used. Inject directly into the spill pool
* the defaults for "arguments" and "caller"
*
- * @param key
- * @param propertyFlags
- * @param getter
- * @param setter
+ * @param key property key
+ * @param propertyFlags flags
+ * @param getter getter for {@link UserAccessorProperty}, null if not present or N/A
+ * @param setter setter for {@link UserAccessorProperty}, null if not present or N/A
*/
protected final void initUserAccessors(final String key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
final int slot = spillLength;
@@ -1514,6 +1526,23 @@ public abstract class ScriptObject implements PropertyAccess {
}
/**
+ * Get the {@link ArrayData}, for this ScriptObject, ensuring it is of a type
+ * that can handle elementType
+ * @param elementType elementType
+ * @return array data
+ */
+ public final ArrayData getArray(final Class<?> elementType) {
+ if (elementType == null) {
+ return arrayData;
+ }
+ final ArrayData newArrayData = arrayData.convert(elementType);
+ if (newArrayData != arrayData) {
+ arrayData = newArrayData;
+ }
+ return newArrayData;
+ }
+
+ /**
* Get the {@link ArrayData} for this ScriptObject if it is an array
* @return array data
*/
@@ -1720,8 +1749,8 @@ public abstract class ScriptObject implements PropertyAccess {
*/
public Object put(final Object key, final Object value, final boolean strict) {
final Object oldValue = get(key);
- final int flags = strict ? NashornCallSiteDescriptor.CALLSITE_STRICT : 0;
- set(key, value, flags);
+ final int scriptObjectFlags = strict ? NashornCallSiteDescriptor.CALLSITE_STRICT : 0;
+ set(key, value, scriptObjectFlags);
return oldValue;
}
@@ -1734,9 +1763,9 @@ public abstract class ScriptObject implements PropertyAccess {
* @param strict strict mode or not
*/
public void putAll(final Map<?, ?> otherMap, final boolean strict) {
- final int flags = strict ? NashornCallSiteDescriptor.CALLSITE_STRICT : 0;
+ final int scriptObjectFlags = strict ? NashornCallSiteDescriptor.CALLSITE_STRICT : 0;
for (final Map.Entry<?, ?> entry : otherMap.entrySet()) {
- set(entry.getKey(), entry.getValue(), flags);
+ set(entry.getKey(), entry.getValue(), scriptObjectFlags);
}
}
@@ -1916,17 +1945,6 @@ public abstract class ScriptObject implements PropertyAccess {
return MH.filterArguments(methodHandle, 0, filter.asType(filter.type().changeReturnType(methodHandle.type().parameterType(0))));
}
- //this will only return true if apply is still builtin
- private static SwitchPoint checkReservedName(final CallSiteDescriptor desc, final LinkRequest request) {
- final boolean isApplyToCall = NashornCallSiteDescriptor.isApplyToCall(desc);
- final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
- if ("apply".equals(name) && isApplyToCall && Global.instance().isSpecialNameValid(name)) {
- assert Global.instance().getChangeCallback("apply") == Global.instance().getChangeCallback("call");
- return Global.instance().getChangeCallback("apply");
- }
- return null;
- }
-
/**
* Find the appropriate GET method for an invoke dynamic call.
*
@@ -1938,14 +1956,13 @@ public abstract class ScriptObject implements PropertyAccess {
*/
protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
final boolean explicitInstanceOfCheck = explicitInstanceOfCheck(desc, request);
- final String name;
- final SwitchPoint reservedNameSwitchPoint;
- reservedNameSwitchPoint = checkReservedName(desc, request);
- if (reservedNameSwitchPoint != null) {
- name = "call"; //turn apply into call, it is the builtin apply and has been modified to explode args
- } else {
- name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
+ String name;
+ name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
+ if (NashornCallSiteDescriptor.isApplyToCall(desc)) {
+ if (Global.isBuiltinFunctionPrototypeApply()) {
+ name = "call";
+ }
}
if (request.isCallSiteUnstable() || hasWithScope()) {
@@ -2006,7 +2023,7 @@ public abstract class ScriptObject implements PropertyAccess {
assert OBJECT_FIELDS_ONLY || guard != null : "we always need a map guard here";
final GuardedInvocation inv = new GuardedInvocation(mh, guard, protoSwitchPoint, exception);
- return inv.addSwitchPoint(reservedNameSwitchPoint);
+ return inv.addSwitchPoint(findBuiltinSwitchPoint(name));
}
private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name, final boolean isMethod) {
@@ -2029,7 +2046,7 @@ public abstract class ScriptObject implements PropertyAccess {
// Marks a property as declared and sets its value. Used as slow path for block-scoped LET and CONST
@SuppressWarnings("unused")
private void declareAndSet(final String key, final Object value) {
- final PropertyMap map = getMap();
+ final PropertyMap oldMap = getMap();
final FindProperty find = findProperty(key, false);
assert find != null;
@@ -2037,7 +2054,7 @@ public abstract class ScriptObject implements PropertyAccess {
assert property != null;
assert property.needsDeclaration();
- final PropertyMap newMap = map.replaceProperty(property, property.removeFlags(Property.NEEDS_DECLARATION));
+ final PropertyMap newMap = oldMap.replaceProperty(property, property.removeFlags(Property.NEEDS_DECLARATION));
setMap(newMap);
set(key, value, 0);
}
@@ -2166,7 +2183,7 @@ public abstract class ScriptObject implements PropertyAccess {
}
}
- final GuardedInvocation inv = new SetMethodCreator(this, find, desc, request).createGuardedInvocation();
+ final GuardedInvocation inv = new SetMethodCreator(this, find, desc, request).createGuardedInvocation(findBuiltinSwitchPoint(name));
final GuardedInvocation cinv = Global.getConstants().findSetMethod(find, this, inv, desc, request);
if (cinv != null) {
@@ -2429,7 +2446,7 @@ public abstract class ScriptObject implements PropertyAccess {
@Override
public void remove() {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException("remove");
}
}
diff --git a/src/jdk/nashorn/internal/runtime/ScriptRuntime.java b/src/jdk/nashorn/internal/runtime/ScriptRuntime.java
index 47b2a8da..e927d4fc 100644
--- a/src/jdk/nashorn/internal/runtime/ScriptRuntime.java
+++ b/src/jdk/nashorn/internal/runtime/ScriptRuntime.java
@@ -32,9 +32,9 @@ import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError;
import static jdk.nashorn.internal.runtime.ECMAErrors.syntaxError;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.JSType.isRepresentableAsInt;
-
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
+import java.lang.invoke.SwitchPoint;
import java.lang.reflect.Array;
import java.util.Collections;
import java.util.Iterator;
@@ -46,6 +46,7 @@ import java.util.Objects;
import jdk.internal.dynalink.beans.StaticClass;
import jdk.nashorn.api.scripting.JSObject;
import jdk.nashorn.api.scripting.ScriptObjectMirror;
+import jdk.nashorn.internal.codegen.ApplySpecialization;
import jdk.nashorn.internal.codegen.CompilerConstants;
import jdk.nashorn.internal.codegen.CompilerConstants.Call;
import jdk.nashorn.internal.ir.debug.JSONWriter;
@@ -113,6 +114,11 @@ public final class ScriptRuntime {
public static final Call THROW_REFERENCE_ERROR = staticCall(MethodHandles.lookup(), ScriptRuntime.class, "throwReferenceError", void.class, String.class);
/**
+ * Used to invalidate builtin names, e.g "Function" mapping to all properties in Function.prototype and Function.prototype itself.
+ */
+ public static final Call INVALIDATE_RESERVED_BUILTIN_NAME = staticCallNoLookup(ScriptRuntime.class, "invalidateReservedBuiltinName", void.class, String.class);
+
+ /**
* Converts a switch tag value to a simple integer. deflt value if it can't.
*
* @param tag Switch statement tag value.
@@ -290,7 +296,7 @@ public final class ScriptRuntime {
@Override
public void remove() {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException("remove");
}
}
@@ -328,7 +334,7 @@ public final class ScriptRuntime {
@Override
public void remove() {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException("remove");
}
};
}
@@ -998,4 +1004,19 @@ public final class ScriptRuntime {
return nx < ny;
}
+ /**
+ * Tag a reserved name as invalidated - used when someone writes
+ * to a property with this name - overly conservative, but link time
+ * is too late to apply e.g. apply-&gt;call specialization
+ * @param name property name
+ */
+ public static void invalidateReservedBuiltinName(final String name) {
+ final Context context = Context.getContextTrusted();
+ final SwitchPoint sp = context.getBuiltinSwitchPoint(name);
+ assert sp != null;
+ if (sp != null) {
+ context.getLogger(ApplySpecialization.class).info("Overwrote special name '" + name +"' - invalidating switchpoint");
+ SwitchPoint.invalidateAll(new SwitchPoint[] { sp });
+ }
+ }
}
diff --git a/src/jdk/nashorn/internal/runtime/SetMethodCreator.java b/src/jdk/nashorn/internal/runtime/SetMethodCreator.java
index 99ec3135..90bbf43c 100644
--- a/src/jdk/nashorn/internal/runtime/SetMethodCreator.java
+++ b/src/jdk/nashorn/internal/runtime/SetMethodCreator.java
@@ -28,7 +28,6 @@ package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError;
import static jdk.nashorn.internal.runtime.JSType.getAccessorTypeIndex;
-
import java.lang.invoke.MethodHandle;
import java.lang.invoke.SwitchPoint;
import jdk.internal.dynalink.CallSiteDescriptor;
@@ -81,8 +80,8 @@ final class SetMethodCreator {
* Creates the actual guarded invocation that represents the dynamic setter method for the property.
* @return the actual guarded invocation that represents the dynamic setter method for the property.
*/
- GuardedInvocation createGuardedInvocation() {
- return createSetMethod().createGuardedInvocation();
+ GuardedInvocation createGuardedInvocation(final SwitchPoint builtinSwitchPoint) {
+ return createSetMethod(builtinSwitchPoint).createGuardedInvocation();
}
/**
@@ -119,7 +118,7 @@ final class SetMethodCreator {
}
}
- private SetMethod createSetMethod() {
+ private SetMethod createSetMethod(final SwitchPoint builtinSwitchPoint) {
if (find != null) {
return createExistingPropertySetter();
}
@@ -130,7 +129,7 @@ final class SetMethodCreator {
return createGlobalPropertySetter();
}
- return createNewPropertySetter();
+ return createNewPropertySetter(builtinSwitchPoint);
}
private void checkStrictCreateNewVariable() {
@@ -185,8 +184,8 @@ final class SetMethodCreator {
return new SetMethod(MH.filterArguments(global.addSpill(type, getName()), 0, ScriptObject.GLOBALFILTER), null);
}
- private SetMethod createNewPropertySetter() {
- final SetMethod sm = map.getFreeFieldSlot() > -1 ? createNewFieldSetter() : createNewSpillPropertySetter();
+ private SetMethod createNewPropertySetter(final SwitchPoint builtinSwitchPoint) {
+ final SetMethod sm = map.getFreeFieldSlot() > -1 ? createNewFieldSetter(builtinSwitchPoint) : createNewSpillPropertySetter(builtinSwitchPoint);
final PropertyListeners listeners = map.getListeners();
if (listeners != null) {
listeners.propertyAdded(sm.property);
@@ -194,7 +193,9 @@ final class SetMethodCreator {
return sm;
}
- private SetMethod createNewSetter(final Property property) {
+ private SetMethod createNewSetter(final Property property, final SwitchPoint builtinSwitchPoint) {
+ property.setBuiltinSwitchPoint(builtinSwitchPoint);
+
final PropertyMap oldMap = getMap();
final PropertyMap newMap = getNewMap(property);
final boolean isStrict = NashornCallSiteDescriptor.isStrict(desc);
@@ -230,12 +231,12 @@ final class SetMethodCreator {
return new SetMethod(MH.asType(MH.guardWithTest(extCheck, casGuard, nop), fastSetter.type()), property);
}
- private SetMethod createNewFieldSetter() {
- return createNewSetter(new AccessorProperty(getName(), 0, sobj.getClass(), getMap().getFreeFieldSlot(), type));
+ private SetMethod createNewFieldSetter(final SwitchPoint builtinSwitchPoint) {
+ return createNewSetter(new AccessorProperty(getName(), 0, sobj.getClass(), getMap().getFreeFieldSlot(), type), builtinSwitchPoint);
}
- private SetMethod createNewSpillPropertySetter() {
- return createNewSetter(new SpillProperty(getName(), 0, getMap().getFreeSpillSlot(), type));
+ private SetMethod createNewSpillPropertySetter(final SwitchPoint builtinSwitchPoint) {
+ return createNewSetter(new SpillProperty(getName(), 0, getMap().getFreeSpillSlot(), type), builtinSwitchPoint);
}
private PropertyMap getNewMap(final Property property) {
diff --git a/src/jdk/nashorn/internal/runtime/Specialization.java b/src/jdk/nashorn/internal/runtime/Specialization.java
new file mode 100644
index 00000000..9807cc61
--- /dev/null
+++ b/src/jdk/nashorn/internal/runtime/Specialization.java
@@ -0,0 +1,114 @@
+/*
+ * 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.lang.invoke.MethodHandle;
+import jdk.nashorn.internal.objects.annotations.SpecializedFunction;
+import jdk.nashorn.internal.objects.annotations.SpecializedFunction.LinkLogic;
+
+/**
+ * Specialization info for a {@link SpecializedFunction}
+ */
+public final class Specialization {
+ private final MethodHandle mh;
+ private final Class<? extends LinkLogic> linkLogicClass;
+ private final boolean isOptimistic;
+
+ /**
+ * Constructor
+ *
+ * @param mh invoker method handler
+ */
+ public Specialization(final MethodHandle mh) {
+ this(mh, false);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param mh invoker method handler
+ * @param isOptimistic is this an optimistic native method, i.e. can it throw {@link UnwarrantedOptimismException}
+ * which would have to lead to a relink and return value processing
+ */
+ public Specialization(final MethodHandle mh, final boolean isOptimistic) {
+ this(mh, null, isOptimistic);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param mh invoker method handler
+ * @param linkLogicClass extra link logic needed for this function. Instances of this class also contains logic for checking
+ * if this can be linked on its first encounter, which is needed as per our standard linker semantics
+ * @param isOptimistic is this an optimistic native method, i.e. can it throw {@link UnwarrantedOptimismException}
+ * which would have to lead to a relink and return value processing
+ */
+ public Specialization(final MethodHandle mh, final Class<? extends LinkLogic> linkLogicClass, final boolean isOptimistic) {
+ this.mh = mh;
+ this.isOptimistic = isOptimistic;
+ if (linkLogicClass != null) {
+ //null out the "empty" link logic class for optimization purposes
+ //we only use the empty instance because we can't default class annotations
+ //to null
+ this.linkLogicClass = LinkLogic.isEmpty(linkLogicClass) ? null : linkLogicClass;
+ } else {
+ this.linkLogicClass = null;
+ }
+ }
+
+ /**
+ * Get the method handle for the invoker of this ScriptFunction
+ * @return the method handle
+ */
+ public MethodHandle getMethodHandle() {
+ return mh;
+ }
+
+ /**
+ * Get the link logic class for this ScriptFunction
+ * @return link logic class info, i.e. one whose instance contains stuff like
+ * "do we need exception check for every call", and logic to check if we may link
+ */
+ public Class<? extends LinkLogic> getLinkLogicClass() {
+ return linkLogicClass;
+ }
+
+ /**
+ * An optimistic specialization is one that can throw UnwarrantedOptimismException.
+ * This is allowed for native methods, as long as they are functional, i.e. don't change
+ * any state between entering and throwing the UOE. Then we can re-execute a wider version
+ * of the method in the continuation. Rest-of method generation for optimistic builtins is
+ * of course not possible, but this approach works and fits into the same relinking
+ * framework
+ *
+ * @return true if optimistic
+ */
+ public boolean isOptimistic() {
+ return isOptimistic;
+ }
+
+}
+
diff --git a/src/jdk/nashorn/internal/runtime/StoredScript.java b/src/jdk/nashorn/internal/runtime/StoredScript.java
index 5b6a77b2..14a0ced0 100644
--- a/src/jdk/nashorn/internal/runtime/StoredScript.java
+++ b/src/jdk/nashorn/internal/runtime/StoredScript.java
@@ -55,8 +55,10 @@ public final class StoredScript implements Serializable {
/**
* Constructor.
*
+ * @param compilationId compilation id
* @param mainClassName main class name
* @param classBytes map of class names to class bytes
+ * @param initializers initializer map, id -> FunctionInitializer
* @param constants constants array
*/
public StoredScript(final int compilationId, final String mainClassName, final Map<String, byte[]> classBytes, final Map<Integer, FunctionInitializer> initializers, final Object[] constants) {
@@ -67,6 +69,10 @@ public final class StoredScript implements Serializable {
this.initializers = initializers;
}
+ /**
+ * Get the compilation id for this StoredScript
+ * @return compilation id
+ */
public int getCompilationId() {
return compilationId;
}
diff --git a/src/jdk/nashorn/internal/runtime/UserAccessorProperty.java b/src/jdk/nashorn/internal/runtime/UserAccessorProperty.java
index 7350ff43..5fdec009 100644
--- a/src/jdk/nashorn/internal/runtime/UserAccessorProperty.java
+++ b/src/jdk/nashorn/internal/runtime/UserAccessorProperty.java
@@ -25,7 +25,6 @@
package jdk.nashorn.internal.runtime;
-import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.JSType.CONVERT_OBJECT_OPTIMISTIC;
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/ArrayData.java b/src/jdk/nashorn/internal/runtime/arrays/ArrayData.java
index 897636eb..9e606ee6 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/ArrayData.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/ArrayData.java
@@ -26,7 +26,6 @@
package jdk.nashorn.internal.runtime.arrays;
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
-
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.nio.ByteBuffer;
@@ -58,7 +57,7 @@ public abstract class ArrayData {
/**
* Length of the array data. Not necessarily length of the wrapped array.
*/
- private long length;
+ protected long length;
/**
* Method handle to throw an {@link UnwarrantedOptimismException} when getting an element
@@ -520,7 +519,7 @@ public abstract class ArrayData {
* @param type new element type
* @return new array data
*/
- protected abstract ArrayData convert(Class<?> type);
+ public abstract ArrayData convert(Class<?> type);
/**
* Push an array of items to the end of the array
@@ -537,7 +536,7 @@ public abstract class ArrayData {
final Class<?> widest = widestType(items);
ArrayData newData = convert(widest);
- long pos = newData.length();
+ long pos = newData.length;
for (final Object item : items) {
newData = newData.ensure(pos); //avoid sparse array
newData.set((int)pos++, item, strict);
@@ -655,7 +654,7 @@ public abstract class ArrayData {
* @param size current size
* @return next size to allocate for internal array
*/
- protected static int nextSize(final int size) {
+ public static int nextSize(final int size) {
return alignUp(size + 1) * 2;
}
@@ -681,6 +680,18 @@ public abstract class ArrayData {
}
}
+ /**
+ * Find a fast call if one exists
+ *
+ * @param clazz array data class
+ * @param desc callsite descriptor
+ * @param request link request
+ * @return fast property getter if one is found
+ */
+ public GuardedInvocation findFastCallMethod(final Class<? extends ArrayData> clazz, final CallSiteDescriptor desc, final LinkRequest request) {
+ return null;
+ }
+
/**
* Find a fast property getter if one exists
*
diff --git a/src/jdk/nashorn/internal/runtime/arrays/ArrayFilter.java b/src/jdk/nashorn/internal/runtime/arrays/ArrayFilter.java
index be41673b..8d71cc04 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/ArrayFilter.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/ArrayFilter.java
@@ -39,7 +39,7 @@ abstract class ArrayFilter extends ArrayData {
protected ArrayData underlying;
ArrayFilter(final ArrayData underlying) {
- super(underlying.length());
+ super(underlying.length);
this.underlying = underlying;
}
@@ -70,13 +70,13 @@ abstract class ArrayFilter extends ArrayData {
@Override
public void shiftLeft(final int by) {
underlying.shiftLeft(by);
- setLength(underlying.length());
+ setLength(underlying.length);
}
@Override
public ArrayData shiftRight(final int by) {
underlying = underlying.shiftRight(by);
- setLength(underlying.length());
+ setLength(underlying.length);
return this;
}
@@ -84,7 +84,7 @@ abstract class ArrayFilter extends ArrayData {
@Override
public ArrayData ensure(final long safeIndex) {
underlying = underlying.ensure(safeIndex);
- setLength(underlying.length());
+ setLength(underlying.length);
return this;
}
@@ -92,7 +92,7 @@ abstract class ArrayFilter extends ArrayData {
@Override
public ArrayData shrink(final long newLength) {
underlying = underlying.shrink(newLength);
- setLength(underlying.length());
+ setLength(underlying.length);
return this;
}
@@ -100,7 +100,7 @@ abstract class ArrayFilter extends ArrayData {
@Override
public ArrayData set(final int index, final Object value, final boolean strict) {
underlying = underlying.set(index, value, strict);
- setLength(underlying.length());
+ setLength(underlying.length);
return this;
}
@@ -108,7 +108,7 @@ abstract class ArrayFilter extends ArrayData {
@Override
public ArrayData set(final int index, final int value, final boolean strict) {
underlying = underlying.set(index, value, strict);
- setLength(underlying.length());
+ setLength(underlying.length);
return this;
}
@@ -116,7 +116,7 @@ abstract class ArrayFilter extends ArrayData {
@Override
public ArrayData set(final int index, final long value, final boolean strict) {
underlying = underlying.set(index, value, strict);
- setLength(underlying.length());
+ setLength(underlying.length);
return this;
}
@@ -124,7 +124,7 @@ abstract class ArrayFilter extends ArrayData {
@Override
public ArrayData set(final int index, final double value, final boolean strict) {
underlying = underlying.set(index, value, strict);
- setLength(underlying.length());
+ setLength(underlying.length);
return this;
}
@@ -189,28 +189,28 @@ abstract class ArrayFilter extends ArrayData {
@Override
public ArrayData delete(final int index) {
underlying = underlying.delete(index);
- setLength(underlying.length());
+ setLength(underlying.length);
return this;
}
@Override
public ArrayData delete(final long from, final long to) {
underlying = underlying.delete(from, to);
- setLength(underlying.length());
+ setLength(underlying.length);
return this;
}
@Override
- protected ArrayData convert(final Class<?> type) {
+ public ArrayData convert(final Class<?> type) {
underlying = underlying.convert(type);
- setLength(underlying.length());
+ setLength(underlying.length);
return this;
}
@Override
public Object pop() {
final Object value = underlying.pop();
- setLength(underlying.length());
+ setLength(underlying.length);
return value;
}
diff --git a/src/jdk/nashorn/internal/runtime/arrays/ContinuousArrayData.java b/src/jdk/nashorn/internal/runtime/arrays/ContinuousArrayData.java
index f6e4d38c..8724d64a 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/ContinuousArrayData.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/ContinuousArrayData.java
@@ -38,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;
@@ -77,11 +78,11 @@ public abstract class ContinuousArrayData extends ArrayData {
* array without reallocating, or if we are overwriting an already
* allocated element
*
- * @param index
+ * @param index index to check
* @return true if we don't need to do any array reallocation to fit an element at index
*/
public final boolean hasRoomFor(final int index) {
- return has(index) || (index == length() && ensure(index) == this);
+ return has(index) || (index == length && ensure(index) == this);
}
/**
@@ -116,6 +117,17 @@ public abstract class ContinuousArrayData extends ArrayData {
}
/**
+ * Returns the type used to store an element in this array
+ * @return element type
+ */
+ 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
* @param returnType return type
@@ -175,11 +187,6 @@ public abstract class ContinuousArrayData extends ArrayData {
return MH.asType(setHas, setHas.type().changeParameterType(2, elementType).changeParameterType(0, clazz));
}
- @Override
- public GuardedInvocation findFastGetMethod(final Class<? extends ArrayData> clazz, final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
- return null;
- }
-
/** Fast access guard - it is impractical for JIT performance reasons to use only CCE asType as guard :-(, also we need
the null case explicitly, which is the one that CCE doesn't handle */
protected static final MethodHandle FAST_ACCESS_GUARD =
@@ -269,4 +276,72 @@ public abstract class ContinuousArrayData extends ArrayData {
return null;
}
+
+ /**
+ * Specialization - fast push implementation
+ * @param arg argument
+ * @return new array length
+ */
+ public long fastPush(final int arg) {
+ throw new ClassCastException(String.valueOf(getClass())); //type is wrong, relink
+ }
+
+ /**
+ * Specialization - fast push implementation
+ * @param arg argument
+ * @return new array length
+ */
+ public long fastPush(final long arg) {
+ throw new ClassCastException(String.valueOf(getClass())); //type is wrong, relink
+ }
+
+ /**
+ * Specialization - fast push implementation
+ * @param arg argument
+ * @return new array length
+ */
+ public long fastPush(final double arg) {
+ throw new ClassCastException(String.valueOf(getClass())); //type is wrong, relink
+ }
+
+ /**
+ * Specialization - fast push implementation
+ * @param arg argument
+ * @return new array length
+ */
+ public long fastPush(final Object arg) {
+ throw new ClassCastException(String.valueOf(getClass())); //type is wrong, relink
+ }
+
+ /**
+ * Specialization - fast pop implementation
+ * @return element value
+ */
+ public int fastPopInt() {
+ throw new ClassCastException(String.valueOf(getClass())); //type is wrong, relink
+ }
+
+ /**
+ * Specialization - fast pop implementation
+ * @return element value
+ */
+ public long fastPopLong() {
+ throw new ClassCastException(String.valueOf(getClass())); //type is wrong, relink
+ }
+
+ /**
+ * Specialization - fast pop implementation
+ * @return element value
+ */
+ public double fastPopDouble() {
+ throw new ClassCastException(String.valueOf(getClass())); //type is wrong, relink
+ }
+
+ /**
+ * Specialization - fast pop implementation
+ * @return element value
+ */
+ public Object fastPopObject() {
+ throw new ClassCastException(String.valueOf(getClass())); //type is wrong, relink
+ }
}
diff --git a/src/jdk/nashorn/internal/runtime/arrays/DeletedArrayFilter.java b/src/jdk/nashorn/internal/runtime/arrays/DeletedArrayFilter.java
index bf120eac..4f54b639 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/DeletedArrayFilter.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/DeletedArrayFilter.java
@@ -26,7 +26,6 @@
package jdk.nashorn.internal.runtime.arrays;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
-
import java.lang.reflect.Array;
import jdk.nashorn.internal.runtime.BitVector;
@@ -40,7 +39,7 @@ final class DeletedArrayFilter extends ArrayFilter {
DeletedArrayFilter(final ArrayData underlying) {
super(underlying);
- this.deleted = new BitVector(underlying.length());
+ this.deleted = new BitVector(underlying.length);
}
@Override
@@ -80,25 +79,25 @@ final class DeletedArrayFilter extends ArrayFilter {
@Override
public void shiftLeft(final int by) {
super.shiftLeft(by);
- deleted.shiftLeft(by, length());
+ deleted.shiftLeft(by, length);
}
@Override
public ArrayData shiftRight(final int by) {
super.shiftRight(by);
- deleted.shiftRight(by, length());
+ deleted.shiftRight(by, length);
return this;
}
@Override
public ArrayData ensure(final long safeIndex) {
- if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH && safeIndex >= length()) {
+ if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH && safeIndex >= length) {
return new SparseArrayData(this, safeIndex + 1);
}
super.ensure(safeIndex);
- deleted.resize(length());
+ deleted.resize(length);
return this;
}
@@ -106,7 +105,7 @@ final class DeletedArrayFilter extends ArrayFilter {
@Override
public ArrayData shrink(final long newLength) {
super.shrink(newLength);
- deleted.resize(length());
+ deleted.resize(length);
return this;
}
@@ -147,7 +146,7 @@ final class DeletedArrayFilter extends ArrayFilter {
@Override
public ArrayData delete(final int index) {
final long longIndex = ArrayIndex.toLongIndex(index);
- assert longIndex >= 0 && longIndex < length();
+ assert longIndex >= 0 && longIndex < length;
deleted.set(longIndex);
underlying.setEmpty(index);
return this;
@@ -155,7 +154,7 @@ final class DeletedArrayFilter extends ArrayFilter {
@Override
public ArrayData delete(final long fromIndex, final long toIndex) {
- assert fromIndex >= 0 && fromIndex <= toIndex && toIndex < length();
+ assert fromIndex >= 0 && fromIndex <= toIndex && toIndex < length;
deleted.setRange(fromIndex, toIndex + 1);
underlying.setEmpty(fromIndex, toIndex);
return this;
@@ -163,7 +162,7 @@ final class DeletedArrayFilter extends ArrayFilter {
@Override
public Object pop() {
- final long index = length() - 1;
+ final long index = length - 1;
if (super.has((int)index)) {
final boolean isDeleted = deleted.isSet(index);
@@ -180,7 +179,7 @@ final class DeletedArrayFilter extends ArrayFilter {
final ArrayData newArray = underlying.slice(from, to);
final DeletedArrayFilter newFilter = new DeletedArrayFilter(newArray);
newFilter.getDeleted().copy(deleted);
- newFilter.getDeleted().shiftLeft(from, newFilter.length());
+ newFilter.getDeleted().shiftLeft(from, newFilter.length);
return newFilter;
}
diff --git a/src/jdk/nashorn/internal/runtime/arrays/DeletedRangeArrayFilter.java b/src/jdk/nashorn/internal/runtime/arrays/DeletedRangeArrayFilter.java
index b74d0782..cd5cadb9 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/DeletedRangeArrayFilter.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/DeletedRangeArrayFilter.java
@@ -45,7 +45,7 @@ final class DeletedRangeArrayFilter extends ArrayFilter {
if(hi < SparseArrayData.MAX_DENSE_LENGTH || underlying instanceof SparseArrayData) {
return underlying;
}
- return new SparseArrayData(underlying, underlying.length());
+ return new SparseArrayData(underlying, underlying.length);
}
private boolean isEmpty() {
@@ -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);
}
}
@@ -93,7 +93,7 @@ final class DeletedRangeArrayFilter extends ArrayFilter {
@Override
public ArrayData ensure(final long safeIndex) {
- if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH && safeIndex >= length()) {
+ if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH && safeIndex >= length) {
return new SparseArrayData(this, safeIndex + 1);
}
@@ -110,8 +110,9 @@ final class DeletedRangeArrayFilter extends ArrayFilter {
@Override
public ArrayData shiftRight(final int by) {
super.shiftRight(by);
- lo = Math.min(length(), lo + by);
- hi = Math.min(length() - 1, hi + by);
+ final long len = length;
+ lo = Math.min(len, lo + by);
+ hi = Math.min(len - 1, hi + by);
return isEmpty() ? getUnderlying() : this;
}
@@ -237,7 +238,7 @@ final class DeletedRangeArrayFilter extends ArrayFilter {
@Override
public Object pop() {
- final int index = (int)(length() - 1);
+ final int index = (int)length - 1;
if (super.has(index)) {
final boolean isDeleted = isDeleted(index);
final Object value = super.pop();
diff --git a/src/jdk/nashorn/internal/runtime/arrays/IntArrayData.java b/src/jdk/nashorn/internal/runtime/arrays/IntArrayData.java
index 57d0cd90..0792b6b6 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/IntArrayData.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/IntArrayData.java
@@ -30,7 +30,6 @@ 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;
@@ -38,7 +37,7 @@ import jdk.nashorn.internal.runtime.ScriptRuntime;
* Implementation of {@link ArrayData} as soon as an int has been
* written to the array. This is the default data for new arrays
*/
-final class IntArrayData extends ContinuousArrayData {
+final class IntArrayData extends ContinuousArrayData implements IntElements {
/**
* The wrapped array
*/
@@ -64,9 +63,19 @@ final class IntArrayData extends ContinuousArrayData {
this.array = array;
}
+ @Override
+ public Class<?> getElementType() {
+ return int.class;
+ }
+
private static final MethodHandle HAS_GET_ELEM = specialCall(MethodHandles.lookup(), IntArrayData.class, "getElem", int.class, int.class).methodHandle();
private static final MethodHandle SET_ELEM = specialCall(MethodHandles.lookup(), IntArrayData.class, "setElem", void.class, int.class, int.class).methodHandle();
+ @Override
+ public Object[] asObjectArray() {
+ return toObjectArray(true);
+ }
+
@SuppressWarnings("unused")
private int getElem(final int index) {
if (has(index)) {
@@ -96,25 +105,20 @@ final class IntArrayData extends ContinuousArrayData {
@Override
public ArrayData copy() {
- return new IntArrayData(array.clone(), (int) length());
- }
-
- @Override
- public Object[] asObjectArray() {
- return toObjectArray(array, (int) length());
+ return new IntArrayData(array.clone(), (int)length);
}
@Override
public Object asArrayOfType(final Class<?> componentType) {
if (componentType == int.class) {
- return array.length == length() ? array.clone() : Arrays.copyOf(array, (int) length());
+ return array.length == length ? array.clone() : Arrays.copyOf(array, (int)length);
}
return super.asArrayOfType(componentType);
}
- private static Object[] toObjectArray(final int[] 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] = Integer.valueOf(array[index]);
@@ -123,7 +127,7 @@ final class IntArrayData extends ContinuousArrayData {
return oarray;
}
- private static double[] toDoubleArray(final int[] array, final int length) {
+ private double[] toDoubleArray() {
assert length <= array.length : "length exceeds internal array size";
final double[] darray = new double[array.length];
@@ -134,7 +138,7 @@ final class IntArrayData extends ContinuousArrayData {
return darray;
}
- private static long[] toLongArray(final int[] array, final int length) {
+ private long[] toLongArray() {
assert length <= array.length : "length exceeds internal array size";
final long[] larray = new long[array.length];
@@ -145,18 +149,30 @@ final class IntArrayData extends ContinuousArrayData {
return larray;
}
+ private LongArrayData convertToLong() {
+ return new LongArrayData(toLongArray(), (int)length);
+ }
+
+ private NumberArrayData convertToDouble() {
+ return new NumberArrayData(toDoubleArray(), (int)length);
+ }
+
+ private ObjectArrayData convertToObject() {
+ return new ObjectArrayData(toObjectArray(false), (int)length);
+ }
+
@Override
public ArrayData convert(final Class<?> type) {
if (type == Integer.class) {
return this;
}
- final int length = (int) length();
if (type == Long.class) {
- return new LongArrayData(IntArrayData.toLongArray(array, length), length);
+ return convertToLong();
} else if (type == Double.class) {
- return new NumberArrayData(IntArrayData.toDoubleArray(array, length), length);
+ return convertToDouble();
} else {
- return new ObjectArrayData(IntArrayData.toObjectArray(array, length), length);
+ assert type == null || (!Number.class.isAssignableFrom(type) && !type.isPrimitive());
+ return convertToObject();
}
}
@@ -167,7 +183,7 @@ final class IntArrayData extends ContinuousArrayData {
@Override
public ArrayData shiftRight(final int by) {
- final ArrayData newData = ensure(by + length() - 1);
+ final ArrayData newData = ensure(by + length - 1);
if (newData != this) {
newData.shiftRight(by);
return newData;
@@ -213,7 +229,7 @@ final class IntArrayData extends ContinuousArrayData {
@Override
public ArrayData set(final int index, final int value, final boolean strict) {
array[index] = value;
- setLength(Math.max(index + 1, length()));
+ setLength(Math.max(index + 1, length));
return this;
}
@@ -222,7 +238,7 @@ final class IntArrayData extends ContinuousArrayData {
public ArrayData set(final int index, final long value, final boolean strict) {
if (JSType.isRepresentableAsInt(value)) {
array[index] = JSType.toInt32(value);
- setLength(Math.max(index + 1, length()));
+ setLength(Math.max(index + 1, length));
return this;
}
@@ -233,7 +249,7 @@ final class IntArrayData extends ContinuousArrayData {
public ArrayData set(final int index, final double value, final boolean strict) {
if (JSType.isRepresentableAsInt(value)) {
array[index] = (int)(long)value;
- setLength(Math.max(index + 1, length()));
+ setLength(Math.max(index + 1, length));
return this;
}
@@ -241,11 +257,6 @@ final class IntArrayData extends ContinuousArrayData {
}
@Override
- public Type getOptimisticType() {
- return Type.INT;
- }
-
- @Override
public int getInt(final int index) {
return array[index];
}
@@ -282,7 +293,7 @@ final class IntArrayData extends ContinuousArrayData {
@Override
public boolean has(final int index) {
- return 0 <= index && index < length();
+ return 0 <= index && index < length;
}
@Override
@@ -297,11 +308,11 @@ final class IntArrayData extends ContinuousArrayData {
@Override
public Object pop() {
- if (length() == 0) {
+ if (length == 0) {
return ScriptRuntime.UNDEFINED;
}
- final int newLength = (int) length() - 1;
+ final int newLength = (int)length - 1;
final int elem = array[newLength];
array[newLength] = 0;
setLength(newLength);
@@ -311,7 +322,7 @@ final class IntArrayData extends ContinuousArrayData {
@Override
public ArrayData slice(final long from, final long to) {
- final long start = from < 0 ? from + length() : from;
+ final long start = from < 0 ? from + length : from;
final long newLength = to - start;
return new IntArrayData(Arrays.copyOfRange(array, (int)from, (int)to), (int)newLength);
@@ -319,18 +330,18 @@ final class IntArrayData extends ContinuousArrayData {
@Override
public final ArrayData push(final boolean strict, final int item) {
- final long length = length();
- final ArrayData newData = ensure(length);
+ final long len = length;
+ final ArrayData newData = ensure(len);
if (newData == this) {
- array[(int)length] = item;
+ array[(int)len] = item;
return this;
}
- return newData.set((int)length, item, strict);
+ return newData.set((int)len, item, strict);
}
@Override
public ArrayData fastSplice(final int start, final int removed, final int added) throws UnsupportedOperationException {
- final long oldLength = length();
+ final long oldLength = length;
final long newLength = oldLength - removed + added;
if (newLength > SparseArrayData.MAX_DENSE_LENGTH && newLength > array.length) {
throw new UnsupportedOperationException();
@@ -355,4 +366,41 @@ final class IntArrayData extends ContinuousArrayData {
return returnValue;
}
+
+ @Override
+ public long fastPush(final int arg) {
+ final int len = (int)length;
+ if (len == array.length) {
+ array = Arrays.copyOf(array, nextSize(len));
+ }
+ array[len] = arg;
+ return ++length;
+ }
+
+ //length must not be zero
+ @Override
+ public int fastPopInt() {
+ if (length == 0) {
+ throw new ClassCastException(); //relink
+ }
+ final int newLength = (int)--length;
+ final int elem = array[newLength];
+ array[newLength] = 0;
+ return elem;
+ }
+
+ @Override
+ public long fastPopLong() {
+ return fastPopInt();
+ }
+
+ @Override
+ public double fastPopDouble() {
+ return fastPopInt();
+ }
+
+ @Override
+ public Object fastPopObject() {
+ return fastPopInt();
+ }
}
diff --git a/src/jdk/nashorn/internal/objects/annotations/SpecializedConstructor.java b/src/jdk/nashorn/internal/runtime/arrays/IntElements.java
index 9e6bd0ad..5bd26843 100644
--- a/src/jdk/nashorn/internal/objects/annotations/SpecializedConstructor.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/IntElements.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -22,26 +22,13 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
-
-package jdk.nashorn.internal.objects.annotations;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
+package jdk.nashorn.internal.runtime.arrays;
/**
- * The SpecializedConstructor annotation is used to flag more type specific constructors than the standard one in
- * Native objects. For example {@link jdk.nashorn.internal.objects.NativeArray#construct} takes an arbitrary number of
- * Object elements as an array. Call this constructor, including the varargs collector that allocates the array
- * upon each invocation, is much more expensive than calling a specialized constructor that takes one arguments
- * of, e.g. int type from the call site, such as
- * {@link jdk.nashorn.internal.objects.NativeArray#construct(boolean, Object, int)}.
- * {@link jdk.nashorn.internal.runtime.ScriptFunction} will try to look up the most specific function when
- * linking the callsite.
+ * Marker interface for any ContinuousArray with int elements
+ * Used for type checks that throw ClassCastExceptions and force relinks
+ * for fast NativeArray specializations of builtin methods
*/
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.METHOD)
-public @interface SpecializedConstructor {
+public interface IntElements extends IntOrLongElements {
//empty
}
diff --git a/src/jdk/nashorn/internal/runtime/arrays/IntOrLongElements.java b/src/jdk/nashorn/internal/runtime/arrays/IntOrLongElements.java
new file mode 100644
index 00000000..f176d757
--- /dev/null
+++ b/src/jdk/nashorn/internal/runtime/arrays/IntOrLongElements.java
@@ -0,0 +1,34 @@
+/*
+ * 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.arrays;
+
+/**
+ * Marker interface for any ContinuousArray with int or long elements
+ * Used for type checks that throw ClassCastExceptions and force relinks
+ * for fast NativeArray specializations of builtin methods
+ */
+public interface IntOrLongElements extends NumericElements {
+ //empty
+}
diff --git a/src/jdk/nashorn/internal/runtime/arrays/LongArrayData.java b/src/jdk/nashorn/internal/runtime/arrays/LongArrayData.java
index 4fc0c63e..f41ee15a 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/LongArrayData.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/LongArrayData.java
@@ -31,7 +31,6 @@ 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;
@@ -39,7 +38,7 @@ import jdk.nashorn.internal.runtime.ScriptRuntime;
* Implementation of {@link ArrayData} as soon as a long has been
* written to the array
*/
-final class LongArrayData extends ContinuousArrayData {
+final class LongArrayData extends ContinuousArrayData implements IntOrLongElements {
/**
* The wrapped array
*/
@@ -57,18 +56,23 @@ final class LongArrayData extends ContinuousArrayData {
}
@Override
+ public Class<?> getElementType() {
+ return long.class;
+ }
+
+ @Override
public ArrayData copy() {
- return new LongArrayData(array.clone(), (int)length());
+ return new LongArrayData(array.clone(), (int)length);
}
@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]);
@@ -80,12 +84,12 @@ final class LongArrayData extends ContinuousArrayData {
@Override
public Object asArrayOfType(final Class<?> componentType) {
if (componentType == long.class) {
- return array.length == length() ? array.clone() : Arrays.copyOf(array, (int)length());
+ return array.length == length ? array.clone() : Arrays.copyOf(array, (int)length);
}
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];
@@ -101,11 +105,11 @@ final class LongArrayData extends ContinuousArrayData {
if (type == Integer.class || type == Long.class) {
return this;
}
- final int length = (int) length();
+ final int len = (int)length;
if (type == Double.class) {
- return new NumberArrayData(LongArrayData.toDoubleArray(array, length), length);
+ return new NumberArrayData(toDoubleArray(), len);
}
- return new ObjectArrayData(LongArrayData.toObjectArray(array, length), length);
+ return new ObjectArrayData(toObjectArray(false), len);
}
@Override
@@ -115,7 +119,7 @@ final class LongArrayData extends ContinuousArrayData {
@Override
public ArrayData shiftRight(final int by) {
- final ArrayData newData = ensure(by + length() - 1);
+ final ArrayData newData = ensure(by + length - 1);
if (newData != this) {
newData.shiftRight(by);
return newData;
@@ -161,14 +165,14 @@ final class LongArrayData extends ContinuousArrayData {
@Override
public ArrayData set(final int index, final int value, final boolean strict) {
array[index] = value;
- setLength(Math.max(index + 1, length()));
+ setLength(Math.max(index + 1, length));
return this;
}
@Override
public ArrayData set(final int index, final long value, final boolean strict) {
array[index] = value;
- setLength(Math.max(index + 1, length()));
+ setLength(Math.max(index + 1, length));
return this;
}
@@ -176,17 +180,12 @@ final class LongArrayData extends ContinuousArrayData {
public ArrayData set(final int index, final double value, final boolean strict) {
if (JSType.isRepresentableAsLong(value)) {
array[index] = (long)value;
- setLength(Math.max(index + 1, length()));
+ setLength(Math.max(index + 1, length));
return this;
}
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();
@@ -252,7 +251,7 @@ final class LongArrayData extends ContinuousArrayData {
@Override
public boolean has(final int index) {
- return 0 <= index && index < length();
+ return 0 <= index && index < length;
}
@Override
@@ -267,11 +266,11 @@ final class LongArrayData extends ContinuousArrayData {
@Override
public Object pop() {
- if (length() == 0) {
+ if (length == 0) {
return ScriptRuntime.UNDEFINED;
}
- final int newLength = (int) (length() - 1);
+ final int newLength = (int)length - 1;
final long elem = array[newLength];
array[newLength] = 0;
setLength(newLength);
@@ -281,25 +280,25 @@ final class LongArrayData extends ContinuousArrayData {
@Override
public ArrayData slice(final long from, final long to) {
- final long start = from < 0 ? from + length() : from;
+ final long start = from < 0 ? from + length : from;
final long newLength = to - start;
return new LongArrayData(Arrays.copyOfRange(array, (int)from, (int)to), (int)newLength);
}
@Override
public final ArrayData push(final boolean strict, final long item) {
- final long length = length();
- final ArrayData newData = ensure(length);
+ final long len = length;
+ final ArrayData newData = ensure(len);
if (newData == this) {
- array[(int)length] = item;
+ array[(int)len] = item;
return this;
}
- return newData.set((int)length, item, strict);
+ return newData.set((int)len, item, strict);
}
@Override
public ArrayData fastSplice(final int start, final int removed, final int added) throws UnsupportedOperationException {
- final long oldLength = length();
+ final long oldLength = length;
final long newLength = oldLength - removed + added;
if (newLength > SparseArrayData.MAX_DENSE_LENGTH && newLength > array.length) {
throw new UnsupportedOperationException();
@@ -324,4 +323,40 @@ final class LongArrayData extends ContinuousArrayData {
return returnValue;
}
+
+ @Override
+ public long fastPush(final int arg) {
+ return fastPush((long)arg);
+ }
+
+ @Override
+ public long fastPush(final long arg) {
+ final int len = (int)length;
+ if (len == array.length) {
+ array = Arrays.copyOf(array, nextSize(len));
+ }
+ array[len] = arg;
+ return ++length;
+ }
+
+ @Override
+ public long fastPopLong() {
+ if (length == 0) {
+ throw new ClassCastException();
+ }
+ final int newLength = (int)--length;
+ final long elem = array[newLength];
+ array[newLength] = 0;
+ return elem;
+ }
+
+ @Override
+ public double fastPopDouble() {
+ return fastPopLong();
+ }
+
+ @Override
+ public Object fastPopObject() {
+ return fastPopLong();
+ }
}
diff --git a/src/jdk/nashorn/internal/runtime/arrays/NoTypeArrayData.java b/src/jdk/nashorn/internal/runtime/arrays/NoTypeArrayData.java
index 4e0ce373..143cd221 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/NoTypeArrayData.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/NoTypeArrayData.java
@@ -58,19 +58,19 @@ final class NoTypeArrayData extends ArrayData {
@Override
public ArrayData convert(final Class<?> type) {
- final long length = length();
+ final long len = length;
final ArrayData arrayData;
if (type == Long.class) {
- arrayData = new LongArrayData(new long[ArrayData.nextSize((int)length)], (int)length);
+ arrayData = new LongArrayData(new long[ArrayData.nextSize((int)len)], (int)len);
} else if (type == Double.class) {
- arrayData = new NumberArrayData(new double[ArrayData.nextSize((int)length)], (int)length);
+ arrayData = new NumberArrayData(new double[ArrayData.nextSize((int)len)], (int)len);
} else if (type == Integer.class) {
- arrayData = new IntArrayData(new int[ArrayData.nextSize((int)length)], (int)length);
+ arrayData = new IntArrayData(new int[ArrayData.nextSize((int)len)], (int)len);
} else {
assert !type.isPrimitive();
- arrayData = new ObjectArrayData(new Object[ArrayData.nextSize((int)length)], (int)length);
+ arrayData = new ObjectArrayData(new Object[ArrayData.nextSize((int)len)], (int)len);
}
- return length == 0 ? arrayData : new DeletedRangeArrayFilter(arrayData, 0, length - 1);
+ return length == 0 ? arrayData : new DeletedRangeArrayFilter(arrayData, 0, len - 1);
}
@Override
@@ -90,11 +90,11 @@ final class NoTypeArrayData extends ArrayData {
}
// Don't trample the shared EMPTY_ARRAY.
- if (length() == 0) {
- return new NoTypeArrayData(Math.max(safeIndex + 1, length()));
+ if (length == 0) {
+ return new NoTypeArrayData(Math.max(safeIndex + 1, length));
}
- setLength(Math.max(safeIndex + 1, length()));
+ setLength(Math.max(safeIndex + 1, length));
return this;
}
diff --git a/src/jdk/nashorn/internal/runtime/arrays/NumberArrayData.java b/src/jdk/nashorn/internal/runtime/arrays/NumberArrayData.java
index 0739ae8e..2c57208f 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/NumberArrayData.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/NumberArrayData.java
@@ -32,13 +32,12 @@ 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
* written to the array
*/
-final class NumberArrayData extends ContinuousArrayData {
+final class NumberArrayData extends ContinuousArrayData implements NumericElements {
/**
* The wrapped array
*/
@@ -56,18 +55,23 @@ final class NumberArrayData extends ContinuousArrayData {
}
@Override
+ public Class<?> getElementType() {
+ return double.class;
+ }
+
+ @Override
public ArrayData copy() {
- return new NumberArrayData(array.clone(), (int) length());
+ return new NumberArrayData(array.clone(), (int)length);
}
@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]);
@@ -78,7 +82,7 @@ final class NumberArrayData extends ContinuousArrayData {
@Override
public Object asArrayOfType(final Class<?> componentType) {
if(componentType == double.class) {
- return array.length == length() ? array.clone() : Arrays.copyOf(array, (int) length());
+ return array.length == length ? array.clone() : Arrays.copyOf(array, (int)length);
}
return super.asArrayOfType(componentType);
}
@@ -86,8 +90,8 @@ final class NumberArrayData extends ContinuousArrayData {
@Override
public ArrayData convert(final Class<?> type) {
if (type != Double.class && type != Integer.class && type != Long.class) {
- final int length = (int) length();
- return new ObjectArrayData(NumberArrayData.toObjectArray(array, length), length);
+ final int len = (int)length;
+ return new ObjectArrayData(toObjectArray(false), len);
}
return this;
}
@@ -99,7 +103,7 @@ final class NumberArrayData extends ContinuousArrayData {
@Override
public ArrayData shiftRight(final int by) {
- final ArrayData newData = ensure(by + length() - 1);
+ final ArrayData newData = ensure(by + length - 1);
if (newData != this) {
newData.shiftRight(by);
return newData;
@@ -144,29 +148,24 @@ final class NumberArrayData extends ContinuousArrayData {
@Override
public ArrayData set(final int index, final int value, final boolean strict) {
array[index] = value;
- setLength(Math.max(index + 1, length()));
+ setLength(Math.max(index + 1, length));
return this;
}
@Override
public ArrayData set(final int index, final long value, final boolean strict) {
array[index] = value;
- setLength(Math.max(index + 1, length()));
+ setLength(Math.max(index + 1, length));
return this;
}
@Override
public ArrayData set(final int index, final double value, final boolean strict) {
array[index] = value;
- setLength(Math.max(index + 1, length()));
+ setLength(Math.max(index + 1, length));
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();
@@ -227,7 +226,7 @@ final class NumberArrayData extends ContinuousArrayData {
@Override
public boolean has(final int index) {
- return 0 <= index && index < length();
+ return 0 <= index && index < length;
}
@Override
@@ -242,11 +241,11 @@ final class NumberArrayData extends ContinuousArrayData {
@Override
public Object pop() {
- if (length() == 0) {
+ if (length == 0) {
return UNDEFINED;
}
- final int newLength = (int) (length() - 1);
+ final int newLength = (int)length - 1;
final double elem = array[newLength];
array[newLength] = 0;
setLength(newLength);
@@ -255,25 +254,25 @@ final class NumberArrayData extends ContinuousArrayData {
@Override
public ArrayData slice(final long from, final long to) {
- final long start = from < 0 ? from + length() : from;
+ final long start = from < 0 ? from + length : from;
final long newLength = to - start;
return new NumberArrayData(Arrays.copyOfRange(array, (int)from, (int)to), (int)newLength);
}
@Override
public final ArrayData push(final boolean strict, final double item) {
- final long length = length();
- final ArrayData newData = ensure(length);
+ final long len = length;
+ final ArrayData newData = ensure(len);
if (newData == this) {
- array[(int)length] = item;
+ array[(int)len] = item;
return this;
}
- return newData.set((int)length, item, strict);
+ return newData.set((int)len, item, strict);
}
@Override
public ArrayData fastSplice(final int start, final int removed, final int added) throws UnsupportedOperationException {
- final long oldLength = length();
+ final long oldLength = length;
final long newLength = oldLength - removed + added;
if (newLength > SparseArrayData.MAX_DENSE_LENGTH && newLength > array.length) {
throw new UnsupportedOperationException();
@@ -298,4 +297,41 @@ final class NumberArrayData extends ContinuousArrayData {
return returnValue;
}
+
+ @Override
+ public long fastPush(final int arg) {
+ return fastPush((double)arg);
+ }
+
+ @Override
+ public long fastPush(final long arg) {
+ return fastPush((double)arg);
+ }
+
+ @Override
+ public long fastPush(final double arg) {
+ final int len = (int)length;
+ if (len == array.length) {
+ //note that fastpush never creates spares arrays, there is nothing to gain by that - it will just use even more memory
+ array = Arrays.copyOf(array, nextSize(len));
+ }
+ array[len] = arg;
+ return ++length;
+ }
+
+ @Override
+ public double fastPopDouble() {
+ if (length == 0) {
+ throw new ClassCastException();
+ }
+ final int newLength = (int)--length;
+ final double elem = array[newLength];
+ array[newLength] = 0;
+ return elem;
+ }
+
+ @Override
+ public Object fastPopObject() {
+ return fastPopDouble();
+ }
}
diff --git a/src/jdk/nashorn/internal/runtime/arrays/NumericElements.java b/src/jdk/nashorn/internal/runtime/arrays/NumericElements.java
new file mode 100644
index 00000000..ad940e2a
--- /dev/null
+++ b/src/jdk/nashorn/internal/runtime/arrays/NumericElements.java
@@ -0,0 +1,35 @@
+/*
+ * 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.arrays;
+
+/**
+ * Marker interface for any ContinuousArray with numeric elements
+ * (int, long or double)
+ * Used for type checks that throw ClassCastExceptions and force relinks
+ * for fast NativeArray specializations of builtin methods
+ */
+public interface NumericElements {
+ //empty
+}
diff --git a/src/jdk/nashorn/internal/runtime/arrays/ObjectArrayData.java b/src/jdk/nashorn/internal/runtime/arrays/ObjectArrayData.java
index 636c8c25..379ba6e4 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/ObjectArrayData.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/ObjectArrayData.java
@@ -30,7 +30,6 @@ 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;
@@ -57,20 +56,25 @@ final class ObjectArrayData extends ContinuousArrayData {
}
@Override
+ public Class<?> getElementType() {
+ return Object.class;
+ }
+
+ @Override
public ArrayData copy() {
- return new ObjectArrayData(array.clone(), (int) length());
+ return new ObjectArrayData(array.clone(), (int)length);
}
@Override
public Object[] asObjectArray() {
- return array.length == length() ? array.clone() : asObjectArrayCopy();
+ return array.length == length ? array.clone() : asObjectArrayCopy();
}
private Object[] asObjectArrayCopy() {
- final long l = length();
- assert l <= Integer.MAX_VALUE;
- final Object[] copy = new Object[(int)l];
- System.arraycopy(array, 0, copy, 0, (int)l);
+ final long len = length;
+ assert len <= Integer.MAX_VALUE;
+ final Object[] copy = new Object[(int)len];
+ System.arraycopy(array, 0, copy, 0, (int)len);
return copy;
}
@@ -86,7 +90,7 @@ final class ObjectArrayData extends ContinuousArrayData {
@Override
public ArrayData shiftRight(final int by) {
- final ArrayData newData = ensure(by + length() - 1);
+ final ArrayData newData = ensure(by + length - 1);
if (newData != this) {
newData.shiftRight(by);
return newData;
@@ -118,28 +122,28 @@ final class ObjectArrayData extends ContinuousArrayData {
@Override
public ArrayData set(final int index, final Object value, final boolean strict) {
array[index] = value;
- setLength(Math.max(index + 1, length()));
+ setLength(Math.max(index + 1, length));
return this;
}
@Override
public ArrayData set(final int index, final int value, final boolean strict) {
array[index] = value;
- setLength(Math.max(index + 1, length()));
+ setLength(Math.max(index + 1, length));
return this;
}
@Override
public ArrayData set(final int index, final long value, final boolean strict) {
array[index] = value;
- setLength(Math.max(index + 1, length()));
+ setLength(Math.max(index + 1, length));
return this;
}
@Override
public ArrayData set(final int index, final double value, final boolean strict) {
array[index] = value;
- setLength(Math.max(index + 1, length()));
+ setLength(Math.max(index + 1, length));
return this;
}
@@ -151,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();
@@ -216,7 +216,7 @@ final class ObjectArrayData extends ContinuousArrayData {
@Override
public boolean has(final int index) {
- return 0 <= index && index < length();
+ return 0 <= index && index < length;
}
@Override
@@ -232,12 +232,48 @@ final class ObjectArrayData extends ContinuousArrayData {
}
@Override
+ public long fastPush(final int arg) {
+ return fastPush((Object)arg);
+ }
+
+ @Override
+ public long fastPush(final long arg) {
+ return fastPush((Object)arg);
+ }
+
+ @Override
+ public long fastPush(final double arg) {
+ return fastPush((Object)arg);
+ }
+
+ @Override
+ public long fastPush(final Object arg) {
+ final int len = (int)length;
+ if (len == array.length) {
+ array = Arrays.copyOf(array, nextSize(len));
+ }
+ array[len] = arg;
+ return ++length;
+ }
+
+ @Override
+ public Object fastPopObject() {
+ if (length == 0) {
+ return ScriptRuntime.UNDEFINED;
+ }
+ final int newLength = (int)--length;
+ final Object elem = array[newLength];
+ array[newLength] = ScriptRuntime.EMPTY;
+ return elem;
+ }
+
+ @Override
public Object pop() {
- if (length() == 0) {
+ if (length == 0) {
return ScriptRuntime.UNDEFINED;
}
- final int newLength = (int) (length() - 1);
+ final int newLength = (int)length - 1;
final Object elem = array[newLength];
setEmpty(newLength);
setLength(newLength);
@@ -246,25 +282,25 @@ final class ObjectArrayData extends ContinuousArrayData {
@Override
public ArrayData slice(final long from, final long to) {
- final long start = from < 0 ? from + length() : from;
+ final long start = from < 0 ? from + length : from;
final long newLength = to - start;
return new ObjectArrayData(Arrays.copyOfRange(array, (int)from, (int)to), (int)newLength);
}
@Override
public ArrayData push(final boolean strict, final Object item) {
- final long length = length();
- final ArrayData newData = ensure(length);
+ final long len = length;
+ final ArrayData newData = ensure(len);
if (newData == this) {
- array[(int)length] = item;
+ array[(int)len] = item;
return this;
}
- return newData.set((int)length, item, strict);
+ return newData.set((int)len, item, strict);
}
@Override
public ArrayData fastSplice(final int start, final int removed, final int added) throws UnsupportedOperationException {
- final long oldLength = length();
+ final long oldLength = length;
final long newLength = oldLength - removed + added;
if (newLength > SparseArrayData.MAX_DENSE_LENGTH && newLength > array.length) {
throw new UnsupportedOperationException();
diff --git a/src/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java b/src/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java
index 623e3159..ef473451 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java
@@ -53,32 +53,32 @@ class SparseArrayData extends ArrayData {
SparseArrayData(final ArrayData underlying, final long length, final TreeMap<Long, Object> sparseMap) {
super(length);
- assert underlying.length() <= length;
+ assert underlying.length <= length;
this.underlying = underlying;
- this.maxDenseLength = Math.max(MAX_DENSE_LENGTH, underlying.length());
+ this.maxDenseLength = Math.max(MAX_DENSE_LENGTH, underlying.length);
this.sparseMap = sparseMap;
}
@Override
public ArrayData copy() {
- return new SparseArrayData(underlying.copy(), length(), new TreeMap<>(sparseMap));
+ return new SparseArrayData(underlying.copy(), length, new TreeMap<>(sparseMap));
}
@Override
public Object[] asObjectArray() {
- final int length = (int) Math.min(length(), Integer.MAX_VALUE);
- final int underlyingLength = (int) Math.min(length, underlying.length());
- final Object[] objArray = new Object[length];
+ final int len = (int)Math.min(length, Integer.MAX_VALUE);
+ final int underlyingLength = (int)Math.min(len, underlying.length);
+ final Object[] objArray = new Object[len];
for (int i = 0; i < underlyingLength; i++) {
objArray[i] = underlying.getObject(i);
}
- Arrays.fill(objArray, underlyingLength, length, ScriptRuntime.UNDEFINED);
+ Arrays.fill(objArray, underlyingLength, len, ScriptRuntime.UNDEFINED);
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
@@ -104,14 +104,14 @@ class SparseArrayData extends ArrayData {
}
sparseMap = newSparseMap;
- setLength(Math.max(length() - by, 0));
+ setLength(Math.max(length - by, 0));
}
@Override
public ArrayData shiftRight(final int by) {
final TreeMap<Long, Object> newSparseMap = new TreeMap<>();
- if (underlying.length() + by > maxDenseLength) {
- for (long i = maxDenseLength - by; i < underlying.length(); i++) {
+ if (underlying.length + by > maxDenseLength) {
+ for (long i = maxDenseLength - by; i < underlying.length; i++) {
if (underlying.has((int) i)) {
newSparseMap.put(Long.valueOf(i + by), underlying.getObject((int) i));
}
@@ -127,23 +127,23 @@ class SparseArrayData extends ArrayData {
}
sparseMap = newSparseMap;
- setLength(length() + by);
+ setLength(length + by);
return this;
}
@Override
public ArrayData ensure(final long safeIndex) {
- if (safeIndex < maxDenseLength && underlying.length() <= safeIndex) {
+ if (safeIndex < maxDenseLength && underlying.length <= safeIndex) {
underlying = underlying.ensure(safeIndex);
}
- setLength(Math.max(safeIndex + 1, length()));
+ setLength(Math.max(safeIndex + 1, length));
return this;
}
@Override
public ArrayData shrink(final long newLength) {
- if (newLength < underlying.length()) {
+ if (newLength < underlying.length) {
underlying = underlying.shrink(newLength);
underlying.setLength(newLength);
sparseMap.clear();
@@ -160,11 +160,11 @@ class SparseArrayData extends ArrayData {
if (index >= 0 && index < maxDenseLength) {
ensure(index);
underlying = underlying.set(index, value, strict);
- setLength(Math.max(underlying.length(), length()));
+ setLength(Math.max(underlying.length, length));
} else {
final Long longIndex = indexToKey(index);
sparseMap.put(longIndex, value);
- setLength(Math.max(longIndex + 1, length()));
+ setLength(Math.max(longIndex + 1, length));
}
return this;
@@ -175,11 +175,11 @@ class SparseArrayData extends ArrayData {
if (index >= 0 && index < maxDenseLength) {
ensure(index);
underlying = underlying.set(index, value, strict);
- setLength(Math.max(underlying.length(), length()));
+ setLength(Math.max(underlying.length, length));
} else {
final Long longIndex = indexToKey(index);
sparseMap.put(longIndex, value);
- setLength(Math.max(longIndex + 1, length()));
+ setLength(Math.max(longIndex + 1, length));
}
return this;
}
@@ -189,11 +189,11 @@ class SparseArrayData extends ArrayData {
if (index >= 0 && index < maxDenseLength) {
ensure(index);
underlying = underlying.set(index, value, strict);
- setLength(Math.max(underlying.length(), length()));
+ setLength(Math.max(underlying.length, length));
} else {
final Long longIndex = indexToKey(index);
sparseMap.put(longIndex, value);
- setLength(Math.max(longIndex + 1, length()));
+ setLength(Math.max(longIndex + 1, length));
}
return this;
}
@@ -203,11 +203,11 @@ class SparseArrayData extends ArrayData {
if (index >= 0 && index < maxDenseLength) {
ensure(index);
underlying = underlying.set(index, value, strict);
- setLength(Math.max(underlying.length(), length()));
+ setLength(Math.max(underlying.length, length));
} else {
final Long longIndex = indexToKey(index);
sparseMap.put(longIndex, value);
- setLength(Math.max(longIndex + 1, length()));
+ setLength(Math.max(longIndex + 1, length));
}
return this;
}
@@ -294,7 +294,7 @@ class SparseArrayData extends ArrayData {
@Override
public boolean has(final int index) {
if (index >= 0 && index < maxDenseLength) {
- return index < underlying.length() && underlying.has(index);
+ return index < underlying.length && underlying.has(index);
}
return sparseMap.containsKey(indexToKey(index));
@@ -303,7 +303,7 @@ class SparseArrayData extends ArrayData {
@Override
public ArrayData delete(final int index) {
if (index >= 0 && index < maxDenseLength) {
- if (index < underlying.length()) {
+ if (index < underlying.length) {
underlying = underlying.delete(index);
}
} else {
@@ -315,8 +315,8 @@ class SparseArrayData extends ArrayData {
@Override
public ArrayData delete(final long fromIndex, final long toIndex) {
- if (fromIndex < maxDenseLength && fromIndex < underlying.length()) {
- underlying = underlying.delete(fromIndex, Math.min(toIndex, underlying.length() - 1));
+ if (fromIndex < maxDenseLength && fromIndex < underlying.length) {
+ underlying = underlying.delete(fromIndex, Math.min(toIndex, underlying.length - 1));
}
if (toIndex >= maxDenseLength) {
sparseMap.subMap(fromIndex, true, toIndex, true).clear();
@@ -329,37 +329,37 @@ class SparseArrayData extends ArrayData {
}
@Override
- protected ArrayData convert(final Class<?> type) {
+ public ArrayData convert(final Class<?> type) {
underlying = underlying.convert(type);
return this;
}
@Override
public Object pop() {
- if (length() == 0) {
+ if (length == 0) {
return ScriptRuntime.UNDEFINED;
}
- if (length() == underlying.length()) {
+ if (length == underlying.length) {
final Object result = underlying.pop();
- setLength(underlying.length());
+ setLength(underlying.length);
return result;
}
- setLength(length() - 1);
- final Long key = Long.valueOf(length());
+ setLength(length - 1);
+ final Long key = Long.valueOf(length);
return sparseMap.containsKey(key) ? sparseMap.remove(key) : ScriptRuntime.UNDEFINED;
}
@Override
public ArrayData slice(final long from, final long to) {
- assert to <= length();
- final long start = from < 0 ? (from + length()) : from;
+ assert to <= length;
+ final long start = from < 0 ? (from + length) : from;
final long newLength = to - start;
if (start >= 0 && to <= maxDenseLength) {
- if (newLength <= underlying.length()) {
+ if (newLength <= underlying.length) {
return underlying.slice(from, to);
}
- return underlying.slice(from, to).ensure(newLength - 1).delete(underlying.length(), newLength);
+ return underlying.slice(from, to).ensure(newLength - 1).delete(underlying.length, newLength);
}
ArrayData sliced = EMPTY_ARRAY;
@@ -369,13 +369,13 @@ class SparseArrayData extends ArrayData {
sliced = sliced.set((int)(i - start), getObject((int)i), false);
}
}
- assert sliced.length() == newLength;
+ assert sliced.length == newLength;
return sliced;
}
@Override
public long nextIndex(final long index) {
- if (index < underlying.length() - 1) {
+ if (index < underlying.length - 1) {
return underlying.nextIndex(index);
}
@@ -383,6 +383,6 @@ class SparseArrayData extends ArrayData {
if (nextKey != null) {
return nextKey;
}
- return length();
+ return length;
}
}
diff --git a/src/jdk/nashorn/internal/runtime/arrays/TypedArrayData.java b/src/jdk/nashorn/internal/runtime/arrays/TypedArrayData.java
index 291e59e0..428678d0 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/TypedArrayData.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/TypedArrayData.java
@@ -26,7 +26,6 @@
package jdk.nashorn.internal.runtime.arrays;
import static jdk.nashorn.internal.lookup.Lookup.MH;
-
import java.lang.invoke.MethodHandle;
import java.nio.Buffer;
import jdk.internal.dynalink.CallSiteDescriptor;
@@ -55,11 +54,11 @@ public abstract class TypedArrayData<T extends Buffer> extends ContinuousArrayDa
}
/**
- * Length in elements. Accessed from {@code ArrayBufferView}
+ * Length in number of elements. Accessed from {@code ArrayBufferView}
* @return element length
*/
public final int getElementLength() {
- return (int)length();
+ return (int)length;
}
/**
@@ -120,7 +119,7 @@ public abstract class TypedArrayData<T extends Buffer> extends ContinuousArrayDa
@Override
public final boolean has(final int index) {
- return 0 <= index && index < length();
+ return 0 <= index && index < length;
}
@Override
@@ -134,7 +133,7 @@ public abstract class TypedArrayData<T extends Buffer> extends ContinuousArrayDa
}
@Override
- protected ArrayData convert(final Class<?> type) {
+ public ArrayData convert(final Class<?> type) {
throw new UnsupportedOperationException();
}
diff --git a/src/jdk/nashorn/internal/runtime/arrays/UndefinedArrayFilter.java b/src/jdk/nashorn/internal/runtime/arrays/UndefinedArrayFilter.java
index e2488d34..f744aacd 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/UndefinedArrayFilter.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/UndefinedArrayFilter.java
@@ -26,7 +26,6 @@
package jdk.nashorn.internal.runtime.arrays;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
-
import java.lang.reflect.Array;
import jdk.nashorn.internal.runtime.BitVector;
import jdk.nashorn.internal.runtime.UnwarrantedOptimismException;
@@ -41,7 +40,7 @@ final class UndefinedArrayFilter extends ArrayFilter {
UndefinedArrayFilter(final ArrayData underlying) {
super(underlying);
- this.undefined = new BitVector(underlying.length());
+ this.undefined = new BitVector(underlying.length);
}
@Override
@@ -81,25 +80,25 @@ final class UndefinedArrayFilter extends ArrayFilter {
@Override
public void shiftLeft(final int by) {
super.shiftLeft(by);
- undefined.shiftLeft(by, length());
+ undefined.shiftLeft(by, length);
}
@Override
public ArrayData shiftRight(final int by) {
super.shiftRight(by);
- undefined.shiftRight(by, length());
+ undefined.shiftRight(by, length);
return this;
}
@Override
public ArrayData ensure(final long safeIndex) {
- if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH && safeIndex >= length()) {
+ if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH && safeIndex >= length) {
return new SparseArrayData(this, safeIndex + 1);
}
super.ensure(safeIndex);
- undefined.resize(length());
+ undefined.resize(length);
return this;
}
@@ -107,7 +106,7 @@ final class UndefinedArrayFilter extends ArrayFilter {
@Override
public ArrayData shrink(final long newLength) {
super.shrink(newLength);
- undefined.resize(length());
+ undefined.resize(length);
return this;
}
@@ -217,7 +216,7 @@ final class UndefinedArrayFilter extends ArrayFilter {
@Override
public Object pop() {
- final long index = length() - 1;
+ final long index = length - 1;
if (super.has((int)index)) {
final boolean isUndefined = undefined.isSet(index);
@@ -234,7 +233,7 @@ final class UndefinedArrayFilter extends ArrayFilter {
final ArrayData newArray = underlying.slice(from, to);
final UndefinedArrayFilter newFilter = new UndefinedArrayFilter(newArray);
newFilter.getUndefined().copy(undefined);
- newFilter.getUndefined().shiftLeft(from, newFilter.length());
+ newFilter.getUndefined().shiftLeft(from, newFilter.length);
return newFilter;
}
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/linker/PrimitiveLookup.java b/src/jdk/nashorn/internal/runtime/linker/PrimitiveLookup.java
index f377c9d8..2a536bbf 100644
--- a/src/jdk/nashorn/internal/runtime/linker/PrimitiveLookup.java
+++ b/src/jdk/nashorn/internal/runtime/linker/PrimitiveLookup.java
@@ -26,14 +26,16 @@
package jdk.nashorn.internal.runtime.linker;
import static jdk.nashorn.internal.lookup.Lookup.MH;
-
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
+import java.lang.invoke.SwitchPoint;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
import jdk.internal.dynalink.support.Guards;
+import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.FindProperty;
+import jdk.nashorn.internal.runtime.GlobalConstants;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.UserAccessorProperty;
@@ -86,27 +88,41 @@ public final class PrimitiveLookup {
final MethodHandle protoFilter) {
final CallSiteDescriptor desc = request.getCallSiteDescriptor();
- if(desc.getNameTokenCount() > 2) {
+ //checks whether the property name is hard-coded in the call-site (i.e. a getProp vs a getElem, or setProp vs setElem)
+ //if it is we can make assumptions on the property: that if it is not defined on primitive wrapper itself it never will be.
+ //so in that case we can skip creation of primitive wrapper and start our search with the prototype.
+ if (desc.getNameTokenCount() > 2) {
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
final FindProperty find = wrappedReceiver.findProperty(name, true);
- if(find == null) {
+
+ if (find == null) {
// Give up early, give chance to BeanLinker and NashornBottomLinker to deal with it.
return null;
- } else if (find.isInherited() && !(find.getProperty() instanceof UserAccessorProperty)) {
+ }
+
+ final SwitchPoint sp = find.getProperty().getBuiltinSwitchPoint(); //can use this instead of proto filter
+ if (sp instanceof Context.BuiltinSwitchPoint && !sp.hasBeenInvalidated()) {
+ return new GuardedInvocation(GlobalConstants.staticConstantGetter(find.getObjectValue()), guard, sp, null);
+ }
+
+ if (find.isInherited() && !(find.getProperty() instanceof UserAccessorProperty)) {
// If property is found in the prototype object bind the method handle directly to
// the proto filter instead of going through wrapper instantiation below.
final ScriptObject proto = wrappedReceiver.getProto();
final GuardedInvocation link = proto.lookup(desc, request);
if (link != null) {
- final MethodHandle invocation = link.getInvocation();
+ final MethodHandle invocation = link.getInvocation(); //this contains the builtin switchpoint
+
final MethodHandle adaptedInvocation = MH.asType(invocation, invocation.type().changeParameterType(0, Object.class));
final MethodHandle method = MH.filterArguments(adaptedInvocation, 0, protoFilter);
final MethodHandle protoGuard = MH.filterArguments(link.getGuard(), 0, protoFilter);
+
return new GuardedInvocation(method, NashornGuards.combineGuards(guard, protoGuard));
}
}
}
+
final GuardedInvocation link = wrappedReceiver.lookup(desc, request);
if (link != null) {
MethodHandle method = link.getInvocation();
@@ -116,8 +132,10 @@ public final class PrimitiveLookup {
assert receiverType.isAssignableFrom(wrapType.returnType());
method = MH.filterArguments(method, 0, MH.asType(wrapFilter, wrapType.changeReturnType(receiverType)));
}
+
return new GuardedInvocation(method, guard, link.getSwitchPoints(), null);
}
+
return null;
}
}
diff --git a/src/jdk/nashorn/internal/runtime/regexp/RegExpFactory.java b/src/jdk/nashorn/internal/runtime/regexp/RegExpFactory.java
index 08168b63..77b1b3dc 100644
--- a/src/jdk/nashorn/internal/runtime/regexp/RegExpFactory.java
+++ b/src/jdk/nashorn/internal/runtime/regexp/RegExpFactory.java
@@ -25,6 +25,9 @@
package jdk.nashorn.internal.runtime.regexp;
+import java.util.Collections;
+import java.util.Set;
+import java.util.WeakHashMap;
import jdk.nashorn.internal.runtime.ParserException;
import jdk.nashorn.internal.runtime.options.Options;
@@ -39,6 +42,15 @@ public class RegExpFactory {
private final static String JDK = "jdk";
private final static String JONI = "joni";
+ /** Weak cache of already validated regexps - when reparsing, we don't, for example
+ * need to recompile (reverify) all regexps that have previously been parsed by this
+ * RegExpFactory in a previous compilation. This saves significant time in e.g. avatar
+ * startup */
+ private static final Set<String> VALID_CACHE_SET =
+ Collections.newSetFromMap(
+ Collections.synchronizedMap(
+ new WeakHashMap<String, Boolean>()));
+
static {
final String impl = Options.getStringProperty("nashorn.regexp.impl", JONI);
switch (impl) {
@@ -88,7 +100,9 @@ public class RegExpFactory {
*/
// @SuppressWarnings({"unused"})
public static void validate(final String pattern, final String flags) throws ParserException {
- instance.compile(pattern, flags);
+ if (VALID_CACHE_SET.add(pattern + flags)) {
+ instance.compile(pattern, flags);
+ }
}
/**
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/Options.properties b/src/jdk/nashorn/internal/runtime/resources/Options.properties
index a5e8cd5a..57943e01 100644
--- a/src/jdk/nashorn/internal/runtime/resources/Options.properties
+++ b/src/jdk/nashorn/internal/runtime/resources/Options.properties
@@ -203,9 +203,9 @@ nashorn.option.lazy.compilation = {
nashorn.option.optimistic.types = { \
name="--optimistic-types", \
- is_undocumented=true, \
- desc="Use optimistic type assumptions with deoptimizing recompilation.", \
- default=true \
+ short_name="-ot", \
+ desc="Use optimistic type assumptions with deoptimizing recompilation. This makes the compiler try, for any program symbol whose type cannot be proven at compile time, to type it as narrow and primitive as possible. If the runtime encounters an error because symbol type is too narrow, a wider method will be generated until steady stage is reached. While this produces as optimal Java Bytecode as possible, erroneous type guesses will lead to longer warmup. Optimistic typing is currently disabled by default, but can be enabled for significantly better peak performance.", \
+ default=false \
}
nashorn.option.loader.per.compile = { \
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/examples/charcodeat-benchmark.js b/test/examples/charcodeat-benchmark.js
new file mode 100644
index 00000000..308e2a07
--- /dev/null
+++ b/test/examples/charcodeat-benchmark.js
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * Simple benchmark to measure charCodeAt specialized method performance
+ */
+
+var str = "sghjkdsfkjghsdfjkfkjdfkjdfjkdfjkfdjkfdkfldjfhdfpkjdhafgksdjfgldfgjldfkjgdlfjgldkfjgkldfj";
+var RESULT1 = 9187;
+var RESULT2 = 1496;
+
+function f() {
+ var len = str.length;
+ var c = 0;
+ for (var i = 0; i < len; i++) {
+ c += str.charCodeAt(i);
+ }
+ return c;
+}
+
+function bench(res) {
+ var d = new Date;
+ var sum = 0;
+ for (var i = 0; i < 1e6; i++) {
+ sum |= f();
+ }
+ if (sum == res) {
+ print("Verified OK");
+ } else {
+ print("Verification failed " + sum + " should be " + res);
+ }
+ print((new Date - d) + " ms");
+}
+
+bench(RESULT1);
+
+print("Replacing charCodeAt... ");
+
+String.prototype.charCodeAt = function() { return 17; }
+
+bench(RESULT2);
+
+print("Done");
diff --git a/test/examples/push-pop-benchmark.js b/test/examples/push-pop-benchmark.js
new file mode 100644
index 00000000..16f4c940
--- /dev/null
+++ b/test/examples/push-pop-benchmark.js
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * Simple benchmark to measure push/pop specialized method performance
+ */
+
+var a = [];
+
+var RESULT = 15;
+
+function bench() {
+ var sum = 0;
+ for (var i=0;i<10;i++) {
+ a.push(i);
+ }
+ for (var i=0;i<10;i++) {
+ sum |= a.pop();
+ }
+ return sum;
+}
+
+function runbench() {
+ var sum = 0;
+ for (var iters = 0; iters<1e8; iters++) {
+ sum |= bench();
+ }
+ return sum;
+}
+
+var d = new Date;
+var res = runbench();
+print((new Date - d) + " ms");
+print();
+if (res != RESULT) {
+ print("ERROR: Wrong result - should be " + RESULT);
+} else {
+ print("Verified OK - result is correct");
+}
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/apply_to_call/apply_to_call5.js b/test/script/basic/apply_to_call/apply_to_call5.js
new file mode 100644
index 00000000..adb9401e
--- /dev/null
+++ b/test/script/basic/apply_to_call/apply_to_call5.js
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * apply_to_call5.js - do one apply to call specialization, then override, apply and make sure it reverts (i.e. stops
+ * calling call)
+ *
+ * @test
+ * @run
+ */
+
+print("start");
+
+var x = {
+ a : 0,
+ b : 0,
+ c : 0,
+ initialize : function(x,y,z) {
+ this.a = x;
+ this.b = y;
+ this.c = z;
+ }
+};
+
+function test() {
+ x.initialize.apply(x, arguments);
+}
+
+test(4711,23,17);
+print(x.a);
+print(x.b);
+print(x.c);
+
+print("Overwriting apply now");
+x.initialize.apply = function() { print("New function for apply - not a property"); }
+
+test(4712);
+print(x.a);
+
+
+var x2 = {
+ a : 0,
+ b : 0,
+ c : 0,
+ initialize : function(x,y,z) {
+ this.a = x;
+ this.b = y;
+ this.c = z;
+ }
+};
+
+function test2() {
+ x2.initialize.apply(x2, arguments);
+}
+
+test2(4711,23,17);
+print(x2.a);
+print(x2.b);
+print(x2.c);
+
+print("Overwriting apply now");
+x2.initialize['apply'] = function() { print("New function for apply - not a property"); }
+
+test(4712);
+print(x2.a);
+
+var x3 = {
+ a : 0,
+ b : 0,
+ c : 0,
+ initialize : function(x,y,z) {
+ this.a = x;
+ this.b = y;
+ this.c = z;
+ }
+};
+
+function test3() {
+ x3.initialize.apply(x3, arguments);
+}
+
+test3(4711,23,17);
+print(x3.a);
+print(x3.b);
+print(x3.c);
+
+print("Overwriting apply now");
+eval("x3.initialize['apply'] = function() { print('New function for apply - not a property'); }");
+
+test(4712);
+print(x3.a);
diff --git a/test/script/basic/apply_to_call/apply_to_call5.js.EXPECTED b/test/script/basic/apply_to_call/apply_to_call5.js.EXPECTED
new file mode 100644
index 00000000..4d678e90
--- /dev/null
+++ b/test/script/basic/apply_to_call/apply_to_call5.js.EXPECTED
@@ -0,0 +1,19 @@
+start
+4711
+23
+17
+Overwriting apply now
+New function for apply - not a property
+4711
+4711
+23
+17
+Overwriting apply now
+New function for apply - not a property
+4711
+4711
+23
+17
+Overwriting apply now
+New function for apply - not a property
+4711
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/basic/fastpushpop.js b/test/script/basic/fastpushpop.js
new file mode 100644
index 00000000..fffd3f23
--- /dev/null
+++ b/test/script/basic/fastpushpop.js
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * fastpushpop.js: make sure guards work for fast push implementation
+ * and normal one
+ *
+ * @test
+ * @run
+ */
+
+var a = [1,2,3];
+a.push(4);
+a.push(5);
+a.push(6);
+print(a);
+
+var a2 = Object.defineProperty(a,"length", { writable: false });
+try {
+ a2.push(7);
+} catch (e) {
+ print("first: " + (e instanceof TypeError));
+}
+
+print(a2);
+
+var b = [1,2,3,,,,4711.17,"dingo!"];
+b.push(4);
+b.push(5);
+b.push(6);
+print(b);
+
+var b2 = Object.defineProperty(b,"length", { writable: false });
+try {
+ b2.push(7);
+} catch (e) {
+ print("second: " + (e instanceof TypeError));
+}
+
+print(b2);
+
diff --git a/test/script/basic/fastpushpop.js.EXPECTED b/test/script/basic/fastpushpop.js.EXPECTED
new file mode 100644
index 00000000..0120e1d9
--- /dev/null
+++ b/test/script/basic/fastpushpop.js.EXPECTED
@@ -0,0 +1,6 @@
+1,2,3,4,5,6
+first: true
+1,2,3,4,5,6,7
+1,2,3,,,,4711.17,dingo!,4,5,6
+second: true
+1,2,3,,,,4711.17,dingo!,4,5,6,7
diff --git a/test/script/basic/octane-payload.js b/test/script/basic/octane-payload.js
index 36c780d7..f2be6b12 100644
--- a/test/script/basic/octane-payload.js
+++ b/test/script/basic/octane-payload.js
@@ -40,7 +40,7 @@ var tests = [
{name:"gbemu", files:["gbemu-part1.js", "gbemu-part2.js"], suite:"GameboyBenchmark"},
{name:"mandreel", files:["mandreel.js"], suite:"MandreelBenchmark"},
{name:"navier-stokes", files:["navier-stokes.js"], suite:"NavierStokes"},
- {name:"pdfjs", files:["pdfjs.js"], suite:"PdfJS"},
+ {name:"pdfjs", files:["pdfjs.js"], suite:"PdfJS", cleanUpIteration: function() { canvas_logs = []; }},
{name:"raytrace", files:["raytrace.js"], suite:"RayTrace"},
{name:"regexp", files:["regexp.js"], suite:"RegExpSuite"},
{name:"richards", files:["richards.js"], suite:"Richards"},
diff --git a/test/script/basic/run-octane.js b/test/script/basic/run-octane.js
index 0c666f2f..a0a9bdc8 100644
--- a/test/script/basic/run-octane.js
+++ b/test/script/basic/run-octane.js
@@ -24,8 +24,8 @@
/**
* @subtest
*/
-var payload = __DIR__ + "octane-payload.js";
-load(payload);
+var dir = typeof(__DIR__) == 'undefined' ? "test/script/basic/" : __DIR__;
+load(dir + "octane-payload.js");
var runtime = undefined;
var verbose = false;
@@ -43,27 +43,27 @@ function should_compile_only(name) {
function load_bench(arg) {
for (var idx = 0; idx < arg.files.length; idx++) {
- var f = arg.files[idx];
- var file = f.split('/');
- var file_name = path + file[file.length - 1];
-
- var compile_and_return = should_compile_only(file_name);
- if (compile_and_return) {
- if (typeof compile_only === 'undefined') { //for a run, skip compile onlies, don't even compile them
- return true;
+ var f = arg.files[idx];
+ var file = f.split('/');
+ var file_name = path + file[file.length - 1];
+
+ var compile_and_return = should_compile_only(file_name);
+ if (compile_and_return) {
+ if (typeof compile_only === 'undefined') { //for a run, skip compile onlies, don't even compile them
+ return true;
+ }
}
- }
- print_verbose(arg, "loading '" + arg.name + "' [" + f + "]...");
- load(file_name);
+ print_verbose(arg, "loading '" + arg.name + "' [" + f + "]... " + file_name);
+ load(file_name);
}
if (typeof arg.before !== 'undefined') {
- arg.before();
+ arg.before();
}
if (compile_and_return) {
- print_always(arg, "Compiled OK");
+ print_always(arg, "Compiled OK");
}
return !compile_and_return;
@@ -73,16 +73,16 @@ function load_bench(arg) {
function run_one_benchmark(arg, iters) {
if (!load_bench(arg)) {
- return;
+ return;
}
var success = true;
var current_name;
if (iters == undefined) {
- iters = numberOfIterations;
+ iters = numberOfIterations;
} else {
- numberOfIterations = iters;
+ numberOfIterations = iters;
}
var benchmarks = eval(arg.suite + ".benchmarks");
@@ -91,64 +91,69 @@ function run_one_benchmark(arg, iters) {
var mean_score = 0;
try {
- for (var x = 0; x < benchmarks.length ; x++) {
- //do warmup run
- //reset random number generator needed as of octane 9 before each run
+ for (var x = 0; x < benchmarks.length ; x++) {
+ //do warmup run
+ //reset random number generator needed as of octane 9 before each run
+ BenchmarkSuite.ResetRNG();
+ benchmarks[x].Setup();
+ }
BenchmarkSuite.ResetRNG();
- benchmarks[x].Setup();
- }
- BenchmarkSuite.ResetRNG();
- print_verbose(arg, "running '" + arg.name + "' for " + iters + " iterations of no less than " + min_time + " seconds");
-
- var scores = [];
-
- var min_time_ms = min_time * 1000;
- var len = benchmarks.length;
-
- for (var it = 0; it < iters + 1; it++) {
- //every iteration must take a minimum of 10 secs
- var ops = 0;
- var elapsed = 0;
- var start = new Date;
- do {
- for (var i = 0; i < len; i++) {
- benchmarks[i].run();
- //important - no timing here like elapsed = new Date() - start, as in the
- //original harness. This will make timing very non-deterministic.
- //NOTHING else must live in this loop
+ print_verbose(arg, "running '" + arg.name + "' for " + iters + " iterations of no less than " + min_time + " seconds");
+
+ var scores = [];
+
+ var min_time_ms = min_time * 1000;
+ var len = benchmarks.length;
+
+ for (var it = 0; it < iters + 1; it++) {
+ //every iteration must take a minimum of 10 secs
+ var ops = 0;
+ var elapsed = 0;
+ var start = new Date;
+ do {
+ for (var i = 0; i < len; i++) {
+ benchmarks[i].run();
+ //important - no timing here like elapsed = new Date() - start, as in the
+ //original harness. This will make timing very non-deterministic.
+ //NOTHING else must live in this loop
+ }
+ ops += len;
+ elapsed = new Date - start;
+ } while (elapsed < min_time * 1000);
+
+ var score = ops / elapsed * 1000 * 60;
+ scores.push(score);
+ var name = it == 0 ? "warmup" : "iteration " + it;
+ print_verbose(arg, name + " finished " + score.toFixed(0) + " ops/minute");
+
+ // optional per-iteration cleanup hook
+ if (typeof arg.cleanUpIteration == "function") {
+ arg.cleanUpIteration();
+ }
}
- ops += len;
- elapsed = new Date - start;
- } while (elapsed < min_time * 1000);
-
- var score = ops / elapsed * 1000 * 60;
- scores.push(score);
- var name = it == 0 ? "warmup" : "iteration " + it;
- print_verbose(arg, name + " finished " + score.toFixed(0) + " ops/minute");
- }
- for (var x = 0; x < benchmarks.length ; x++) {
- benchmarks[x].TearDown();
- }
+ for (var x = 0; x < benchmarks.length ; x++) {
+ benchmarks[x].TearDown();
+ }
- for (var x = 1; x < iters + 1 ; x++) {
- mean_score += scores[x];
- min_score = Math.min(min_score, scores[x]);
- max_score = Math.max(max_score, scores[x]);
- }
- mean_score /= iters;
+ for (var x = 1; x < iters + 1 ; x++) {
+ mean_score += scores[x];
+ min_score = Math.min(min_score, scores[x]);
+ max_score = Math.max(max_score, scores[x]);
+ }
+ mean_score /= iters;
} catch (e) {
- print_always(arg, "*** Aborted and setting score to zero. Reason: " + e);
- if (e instanceof java.lang.Throwable) {
- e.printStackTrace();
- }
- mean_score = min_score = max_score = 0;
- scores = [0];
+ print_always(arg, "*** Aborted and setting score to zero. Reason: " + e);
+ if (is_this_nashorn() && e instanceof java.lang.Throwable) {
+ e.printStackTrace();
+ }
+ mean_score = min_score = max_score = 0;
+ scores = [0];
}
var res = mean_score.toFixed(0);
if (verbose) {
- res += " ops/minute (" + min_score.toFixed(0) + "-" + max_score.toFixed(0) + "), warmup=" + scores[0].toFixed(0);
+ res += " ops/minute (" + min_score.toFixed(0) + "-" + max_score.toFixed(0) + "), warmup=" + scores[0].toFixed(0);
}
print_always(arg, res);
}
@@ -163,13 +168,13 @@ function print_always(arg, x) {
function print_verbose(arg, x) {
if (verbose) {
- print_always(arg, x)
+ print_always(arg, x)
}
}
function run_suite(tests, iters) {
for (var idx = 0; idx < tests.length; idx++) {
- run_one_benchmark(tests[idx], iters);
+ run_one_benchmark(tests[idx], iters);
}
}
@@ -184,13 +189,13 @@ if (typeof $ARGS !== 'undefined') {
var new_args = [];
for (i in args) {
if (args[i].toString().indexOf(' ') != -1) {
- args[i] = args[i].replace(/\/$/, '');
- var s = args[i].split(' ');
- for (j in s) {
- new_args.push(s[j]);
- }
+ args[i] = args[i].replace(/\/$/, '');
+ var s = args[i].split(' ');
+ for (j in s) {
+ new_args.push(s[j]);
+ }
} else {
- new_args.push(args[i]);
+ new_args.push(args[i]);
}
}
@@ -205,46 +210,46 @@ var min_time = 5;
for (var i = 0; i < args.length; i++) {
arg = args[i];
if (arg == "--iterations") {
- iters = +args[++i];
- if (isNaN(iters)) {
- throw "'--iterations' must be followed by integer";
- }
+ iters = +args[++i];
+ if (isNaN(iters)) {
+ throw "'--iterations' must be followed by integer";
+ }
} else if (arg == "--runtime") {
- runtime = args[++i];
+ runtime = args[++i];
} else if (arg == "--verbose") {
- verbose = true;
+ verbose = true;
} else if (arg == "--min-time") {
- min_time = +args[++i];
- if (isNaN(iters)) {
- throw "'--min-time' must be followed by integer";
- }
+ min_time = +args[++i];
+ if (isNaN(iters)) {
+ throw "'--min-time' must be followed by integer";
+ }
} else if (arg == "") {
- continue; //skip
+ continue; //skip
} else {
- var found = false;
- for (j in tests) {
- if (tests[j].name === arg) {
- tests_found.push(tests[j]);
- found = true;
- break;
- }
- }
- if (!found) {
- var str = "unknown test name: '" + arg + "' -- valid names are: ";
+ var found = false;
for (j in tests) {
- if (j != 0) {
- str += ", ";
+ if (tests[j].name === arg) {
+ tests_found.push(tests[j]);
+ found = true;
+ break;
+ }
}
- str += "'" + tests[j].name + "'";
+ if (!found) {
+ var str = "unknown test name: '" + arg + "' -- valid names are: ";
+ for (j in tests) {
+ if (j != 0) {
+ str += ", ";
+ }
+ str += "'" + tests[j].name + "'";
+ }
+ throw str;
}
- throw str;
- }
}
}
if (tests_found.length == 0) {
for (i in tests) {
- tests_found.push(tests[i]);
+ tests_found.push(tests[i]);
}
}
@@ -255,21 +260,21 @@ function is_this_nashorn() {
if (is_this_nashorn()) {
try {
- read = readFully;
+ read = readFully;
} catch (e) {
- print("ABORTING: Cannot find 'readFully'. You must have scripting enabled to use this test harness. (-scripting)");
- throw e;
+ print("ABORTING: Cannot find 'readFully'. You must have scripting enabled to use this test harness. (-scripting)");
+ throw e;
}
}
// run tests in alphabetical order by name
tests_found.sort(function(a, b) {
if (a.name < b.name) {
- return -1;
+ return -1;
} else if (a.name > b.name) {
- return 1;
+ return 1;
} else {
- return 0;
+ return 0;
}
});
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/ClassFilterTest.java b/test/src/jdk/nashorn/internal/runtime/ClassFilterTest.java
index 558833eb..7471731d 100644
--- a/test/src/jdk/nashorn/internal/runtime/ClassFilterTest.java
+++ b/test/src/jdk/nashorn/internal/runtime/ClassFilterTest.java
@@ -25,10 +25,10 @@
package jdk.nashorn.internal.runtime;
-
import jdk.nashorn.api.scripting.ClassFilter;
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
import jdk.nashorn.api.scripting.URLReader;
+import jdk.nashorn.internal.test.framework.TestFinder;
import org.testng.annotations.Test;
import javax.script.ScriptEngine;
@@ -126,9 +126,9 @@ public class ClassFilterTest {
private void persistentCacheTestImpl() {
NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
ScriptEngine engine = factory.getScriptEngine(
- new String[]{"--persistent-code-cache"},
- getClass().getClassLoader(),
- getClassFilter()
+ TestFinder.addExplicitOptimisticTypes(new String[]{"--persistent-code-cache", "--optimistic-types=true"}),
+ getClass().getClassLoader(),
+ getClassFilter()
);
String testScript = "var a = Java.type('java.lang.String');" + generateCodeForPersistentStore();
try {
@@ -137,7 +137,7 @@ public class ClassFilterTest {
fail(exc.getMessage());
}
ScriptEngine engineSafe = factory.getScriptEngine(
- new String[]{"--persistent-code-cache"},
+ TestFinder.addExplicitOptimisticTypes(new String[]{"--persistent-code-cache", "--optimistic-types=true"}),
getClass().getClassLoader(),
new ClassFilter() {
@Override
@@ -151,7 +151,7 @@ public class ClassFilterTest {
fail("ClassNotFoundException should have been thrown");
} catch (final Exception exc) {
if (!(exc.getCause() instanceof ClassNotFoundException)) {
- fail("ClassNotFoundException expected");
+ fail("ClassNotFoundException expected, got " + exc.getClass());
}
}
}
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<>();
diff --git a/test/src/jdk/nashorn/internal/test/framework/ScriptRunnable.java b/test/src/jdk/nashorn/internal/test/framework/ScriptRunnable.java
index f8aef77c..ee26061e 100644
--- a/test/src/jdk/nashorn/internal/test/framework/ScriptRunnable.java
+++ b/test/src/jdk/nashorn/internal/test/framework/ScriptRunnable.java
@@ -179,7 +179,7 @@ public final class ScriptRunnable extends AbstractScriptRunnable implements ITes
for (final String str : forkJVMOptions) {
if(!str.isEmpty()) {
cmd.add(str);
- }
+ }
}
cmd.add(Shell.class.getName());
// now add the rest of the "in process" runtime arguments
diff --git a/test/src/jdk/nashorn/internal/test/framework/TestFinder.java b/test/src/jdk/nashorn/internal/test/framework/TestFinder.java
index 4a96a4ae..018f4c7c 100644
--- a/test/src/jdk/nashorn/internal/test/framework/TestFinder.java
+++ b/test/src/jdk/nashorn/internal/test/framework/TestFinder.java
@@ -56,6 +56,7 @@ import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
@@ -75,7 +76,7 @@ import org.xml.sax.InputSource;
* Utility class to find/parse script test files and to create 'test' instances.
* Actual 'test' object type is decided by clients of this class.
*/
-final class TestFinder {
+public final class TestFinder {
private TestFinder() {}
interface TestFactory<T> {
@@ -215,6 +216,8 @@ final class TestFinder {
final List<String> scriptArguments = new ArrayList<>();
boolean inComment = false;
+ boolean explicitOptimistic = false;
+
try (Scanner scanner = new Scanner(testFile)) {
while (scanner.hasNext()) {
// TODO: Scan for /ref=file qualifiers, etc, to determine run
@@ -287,7 +290,11 @@ final class TestFinder {
scriptArguments.add(scanner.next());
break;
case "@option":
- engineOptions.add(scanner.next());
+ final String next = scanner.next();
+ engineOptions.add(next);
+ if (next.startsWith("--optimistic-types")) {
+ explicitOptimistic = true;
+ }
break;
case "@fork":
fork = true;
@@ -336,12 +343,61 @@ final class TestFinder {
testOptions.put(OPTIONS_FORK, "true");
}
+ //if there are explicit optimistic type settings, use those - do not override
+ //the test might only work with optimistic types on or off.
+ if (!explicitOptimistic) {
+ addExplicitOptimisticTypes(engineOptions);
+ }
+
tests.add(factory.createTest(framework, testFile.toFile(), engineOptions, testOptions, scriptArguments));
} else if (!isNotTest) {
orphans.add(name);
}
}
+ //the reverse of the default setting for optimistic types, if enabled, false, otherwise true
+ //thus, true for 8u40, false for 9
+ private static final boolean OPTIMISTIC_OVERRIDE = true;
+
+ /**
+ * Check if there is an optimistic override, that disables the default
+ * false optimistic types and sets them to true, for testing purposes
+ *
+ * @return true if optimistic type override has been set by test suite
+ */
+ public static boolean hasOptimisticOverride() {
+ return Boolean.valueOf(OPTIMISTIC_OVERRIDE).toString().equals(System.getProperty("optimistic.override"));
+ }
+
+ /**
+ * Add an optimistic-types=true option to an argument list if this
+ * is set to override the default false. Add an optimistic-types=true
+ * options to an argument list if this is set to override the default
+ * true
+ *
+ * @args new argument list array
+ */
+ public static String[] addExplicitOptimisticTypes(String[] args) {
+ if (hasOptimisticOverride()) {
+ final List<String> newList = new ArrayList<>(Arrays.asList(args));
+ newList.add("--optimistic-types=" + Boolean.valueOf(OPTIMISTIC_OVERRIDE));
+ return newList.toArray(new String[0]);
+ }
+ return args;
+ }
+
+ /**
+ * Add an optimistic-types=true option to an argument list if this
+ * is set to override the default false
+ *
+ * @args argument list
+ */
+ public static void addExplicitOptimisticTypes(List<String> args) {
+ if (hasOptimisticOverride()) {
+ args.add("--optimistic-types=" + Boolean.valueOf(OPTIMISTIC_OVERRIDE));
+ }
+ }
+
private static boolean strictModeEnabled() {
return Boolean.getBoolean(TEST_JS_ENABLE_STRICT_MODE);
}