aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEdward Nevill edward.nevill@linaro.org <Edward Nevill edward.nevill@linaro.org>2013-09-27 15:42:22 +0100
committerEdward Nevill edward.nevill@linaro.org <Edward Nevill edward.nevill@linaro.org>2013-09-27 15:42:22 +0100
commite93af65681ff4fb09c8b852c9a2a61e443be5f92 (patch)
tree53676a488a0ebe7e78c492066a4425d98aa382b6
parent7a1be74c8dd1f0d9d3ca59049cad188d2a06241d (diff)
parent127e24f2037a33b97b35584fb3cff868aebe51c0 (diff)
Merge up to jdk8-b90preview_rc1
-rw-r--r--.hgtags5
-rw-r--r--bin/verbose_octane.sh2
-rw-r--r--buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ConstructorGenerator.java5
-rw-r--r--buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/StringConstants.java3
-rw-r--r--docs/JavaScriptingProgrammersGuide.html24
-rw-r--r--make/build-nasgen.xml1
-rw-r--r--make/build.xml27
-rw-r--r--make/project.properties8
-rw-r--r--src/jdk/internal/dynalink/beans/ClassString.java8
-rw-r--r--src/jdk/internal/dynalink/beans/OverloadedMethod.java2
-rw-r--r--src/jdk/internal/dynalink/beans/StaticClassIntrospector.java6
-rw-r--r--src/jdk/internal/dynalink/beans/StaticClassLinker.java2
-rw-r--r--src/jdk/nashorn/api/scripting/Formatter.java2
-rw-r--r--src/jdk/nashorn/api/scripting/NashornScriptEngine.java5
-rw-r--r--src/jdk/nashorn/api/scripting/NashornScriptEngineFactory.java40
-rw-r--r--src/jdk/nashorn/internal/codegen/Attr.java881
-rw-r--r--src/jdk/nashorn/internal/codegen/ClassEmitter.java39
-rw-r--r--src/jdk/nashorn/internal/codegen/CodeGenerator.java1369
-rw-r--r--src/jdk/nashorn/internal/codegen/CompilationPhase.java260
-rw-r--r--src/jdk/nashorn/internal/codegen/Compiler.java62
-rw-r--r--src/jdk/nashorn/internal/codegen/CompilerConstants.java73
-rw-r--r--src/jdk/nashorn/internal/codegen/FieldObjectCreator.java5
-rw-r--r--src/jdk/nashorn/internal/codegen/FinalizeTypes.java212
-rw-r--r--src/jdk/nashorn/internal/codegen/FoldConstants.java16
-rw-r--r--src/jdk/nashorn/internal/codegen/Frame.java196
-rw-r--r--src/jdk/nashorn/internal/codegen/Lower.java935
-rw-r--r--src/jdk/nashorn/internal/codegen/MethodEmitter.java217
-rw-r--r--src/jdk/nashorn/internal/codegen/Namespace.java8
-rw-r--r--src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java32
-rw-r--r--src/jdk/nashorn/internal/codegen/ObjectCreator.java3
-rw-r--r--src/jdk/nashorn/internal/codegen/SplitMethodEmitter.java100
-rw-r--r--src/jdk/nashorn/internal/codegen/Splitter.java264
-rw-r--r--src/jdk/nashorn/internal/codegen/WeighNodes.java39
-rw-r--r--src/jdk/nashorn/internal/codegen/types/BooleanType.java6
-rw-r--r--src/jdk/nashorn/internal/codegen/types/IntType.java6
-rw-r--r--src/jdk/nashorn/internal/codegen/types/LongType.java6
-rw-r--r--src/jdk/nashorn/internal/codegen/types/NumberType.java6
-rw-r--r--src/jdk/nashorn/internal/codegen/types/Type.java13
-rw-r--r--src/jdk/nashorn/internal/ir/AccessNode.java99
-rw-r--r--src/jdk/nashorn/internal/ir/BaseNode.java90
-rw-r--r--src/jdk/nashorn/internal/ir/BinaryNode.java82
-rw-r--r--src/jdk/nashorn/internal/ir/Block.java263
-rw-r--r--src/jdk/nashorn/internal/ir/BlockLexicalContext.java120
-rw-r--r--src/jdk/nashorn/internal/ir/BreakNode.java53
-rw-r--r--src/jdk/nashorn/internal/ir/BreakableNode.java42
-rw-r--r--src/jdk/nashorn/internal/ir/CallNode.java165
-rw-r--r--src/jdk/nashorn/internal/ir/CaseNode.java49
-rw-r--r--src/jdk/nashorn/internal/ir/CatchNode.java87
-rw-r--r--src/jdk/nashorn/internal/ir/ContinueNode.java47
-rw-r--r--src/jdk/nashorn/internal/ir/EmptyNode.java7
-rw-r--r--src/jdk/nashorn/internal/ir/ExecuteNode.java46
-rw-r--r--src/jdk/nashorn/internal/ir/Flags.java69
-rw-r--r--src/jdk/nashorn/internal/ir/ForNode.java165
-rw-r--r--src/jdk/nashorn/internal/ir/FunctionNode.java974
-rw-r--r--src/jdk/nashorn/internal/ir/IdentNode.java85
-rw-r--r--src/jdk/nashorn/internal/ir/IfNode.java62
-rw-r--r--src/jdk/nashorn/internal/ir/IndexNode.java91
-rw-r--r--src/jdk/nashorn/internal/ir/LabelNode.java92
-rw-r--r--src/jdk/nashorn/internal/ir/LabeledNode.java123
-rw-r--r--src/jdk/nashorn/internal/ir/LexicalContext.java600
-rw-r--r--src/jdk/nashorn/internal/ir/LexicalContextNode.java (renamed from src/jdk/nashorn/internal/ir/DoWhileNode.java)57
-rw-r--r--src/jdk/nashorn/internal/ir/LineNumberNode.java16
-rw-r--r--src/jdk/nashorn/internal/ir/LiteralNode.java72
-rw-r--r--src/jdk/nashorn/internal/ir/Location.java20
-rw-r--r--src/jdk/nashorn/internal/ir/LoopNode.java176
-rw-r--r--src/jdk/nashorn/internal/ir/Node.java229
-rw-r--r--src/jdk/nashorn/internal/ir/ObjectNode.java38
-rw-r--r--src/jdk/nashorn/internal/ir/PropertyNode.java97
-rw-r--r--src/jdk/nashorn/internal/ir/ReturnNode.java67
-rw-r--r--src/jdk/nashorn/internal/ir/RuntimeNode.java75
-rw-r--r--src/jdk/nashorn/internal/ir/SplitNode.java175
-rw-r--r--src/jdk/nashorn/internal/ir/SwitchNode.java140
-rw-r--r--src/jdk/nashorn/internal/ir/Symbol.java94
-rw-r--r--src/jdk/nashorn/internal/ir/TernaryNode.java95
-rw-r--r--src/jdk/nashorn/internal/ir/ThrowNode.java48
-rw-r--r--src/jdk/nashorn/internal/ir/TryNode.java167
-rw-r--r--src/jdk/nashorn/internal/ir/UnaryNode.java61
-rw-r--r--src/jdk/nashorn/internal/ir/VarNode.java131
-rw-r--r--src/jdk/nashorn/internal/ir/WhileNode.java164
-rw-r--r--src/jdk/nashorn/internal/ir/WithNode.java63
-rw-r--r--src/jdk/nashorn/internal/ir/annotations/Immutable.java34
-rw-r--r--src/jdk/nashorn/internal/ir/debug/ASTWriter.java10
-rw-r--r--src/jdk/nashorn/internal/ir/debug/JSONWriter.java238
-rw-r--r--src/jdk/nashorn/internal/ir/debug/PrintVisitor.java171
-rw-r--r--src/jdk/nashorn/internal/ir/visitor/NodeOperatorVisitor.java219
-rw-r--r--src/jdk/nashorn/internal/ir/visitor/NodeVisitor.java260
-rw-r--r--src/jdk/nashorn/internal/lookup/MethodHandleFactory.java12
-rw-r--r--src/jdk/nashorn/internal/objects/BoundScriptFunctionImpl.java2
-rw-r--r--src/jdk/nashorn/internal/objects/NativeArray.java65
-rw-r--r--src/jdk/nashorn/internal/objects/NativeDate.java110
-rw-r--r--src/jdk/nashorn/internal/objects/NativeDebug.java1
-rw-r--r--src/jdk/nashorn/internal/objects/NativeFunction.java14
-rw-r--r--src/jdk/nashorn/internal/objects/NativeJSAdapter.java6
-rw-r--r--src/jdk/nashorn/internal/objects/NativeJSON.java4
-rw-r--r--src/jdk/nashorn/internal/objects/NativeJava.java69
-rw-r--r--src/jdk/nashorn/internal/objects/NativeRegExp.java74
-rw-r--r--src/jdk/nashorn/internal/objects/NativeRegExpExecResult.java1
-rw-r--r--src/jdk/nashorn/internal/objects/NativeString.java16
-rw-r--r--src/jdk/nashorn/internal/objects/NativeUint32Array.java7
-rw-r--r--src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java40
-rw-r--r--src/jdk/nashorn/internal/parser/AbstractParser.java38
-rw-r--r--src/jdk/nashorn/internal/parser/JSONParser.java19
-rw-r--r--src/jdk/nashorn/internal/parser/Lexer.java37
-rw-r--r--src/jdk/nashorn/internal/parser/Parser.java944
-rw-r--r--src/jdk/nashorn/internal/parser/TokenType.java5
-rw-r--r--src/jdk/nashorn/internal/runtime/AccessorProperty.java45
-rw-r--r--src/jdk/nashorn/internal/runtime/Context.java83
-rw-r--r--src/jdk/nashorn/internal/runtime/DebugLogger.java102
-rw-r--r--src/jdk/nashorn/internal/runtime/FindProperty.java16
-rw-r--r--src/jdk/nashorn/internal/runtime/JSONFunctions.java38
-rw-r--r--src/jdk/nashorn/internal/runtime/JSType.java28
-rw-r--r--src/jdk/nashorn/internal/runtime/NativeJavaPackage.java2
-rw-r--r--src/jdk/nashorn/internal/runtime/PropertyHashMap.java28
-rw-r--r--src/jdk/nashorn/internal/runtime/PropertyListenerManager.java78
-rw-r--r--src/jdk/nashorn/internal/runtime/PropertyMap.java10
-rw-r--r--src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java20
-rw-r--r--src/jdk/nashorn/internal/runtime/ScriptEnvironment.java38
-rw-r--r--src/jdk/nashorn/internal/runtime/ScriptFunction.java16
-rw-r--r--src/jdk/nashorn/internal/runtime/ScriptObject.java417
-rw-r--r--src/jdk/nashorn/internal/runtime/StructureLoader.java4
-rw-r--r--src/jdk/nashorn/internal/runtime/WithObject.java33
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/ArrayIndex.java4
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/MapIterator.java3
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java2
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/AdaptationException.java39
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/AdaptationResult.java74
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/ClassAndLoader.java188
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java882
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/JavaAdapterClassLoader.java225
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java1256
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/JavaAdapterGeneratorBase.java55
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/JavaAdapterServices.java112
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/NashornCallSiteDescriptor.java5
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/NashornLinker.java15
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/NashornStaticClassLinker.java4
-rw-r--r--src/jdk/nashorn/internal/runtime/options/Options.java8
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/RegExpFactory.java2
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/RegExpScanner.java182
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/Analyser.java3
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/Parser.java5
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/Regex.java21
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/ast/QuantifierNode.java4
-rw-r--r--src/jdk/nashorn/internal/runtime/resources/Messages.properties1
-rw-r--r--src/jdk/nashorn/internal/runtime/resources/Options.properties35
-rw-r--r--src/jdk/nashorn/internal/runtime/resources/fx/base.js226
-rw-r--r--src/jdk/nashorn/internal/runtime/resources/fx/bootstrap.js74
-rw-r--r--src/jdk/nashorn/internal/runtime/resources/fx/controls.js264
-rw-r--r--src/jdk/nashorn/internal/runtime/resources/fx/fxml.js30
-rw-r--r--src/jdk/nashorn/internal/runtime/resources/fx/graphics.js431
-rw-r--r--src/jdk/nashorn/internal/runtime/resources/fx/media.js45
-rw-r--r--src/jdk/nashorn/internal/runtime/resources/fx/swing.js29
-rw-r--r--src/jdk/nashorn/internal/runtime/resources/fx/swt.js29
-rw-r--r--src/jdk/nashorn/internal/runtime/resources/fx/web.js36
-rw-r--r--src/jdk/nashorn/internal/runtime/resources/mozilla_compat.js11
-rw-r--r--src/jdk/nashorn/tools/Shell.java57
-rw-r--r--test/examples/int-micro.js107
-rw-r--r--test/script/basic/JDK-8008238.js34
-rw-r--r--test/script/basic/JDK-8008814-3.js40
-rw-r--r--test/script/basic/JDK-8008814-3.js.EXPECTED1
-rw-r--r--test/script/basic/JDK-8008814-4.js40
-rw-r--r--test/script/basic/JDK-8008814-4.js.EXPECTED1
-rw-r--r--test/script/basic/JDK-8009230.js93
-rw-r--r--test/script/basic/JDK-8009230.js.EXPECTED45
-rw-r--r--test/script/basic/JDK-8010710.js (renamed from test/script/basic/JDK-8017010.js)0
-rw-r--r--test/script/basic/JDK-8010710.js.EXPECTED (renamed from test/script/basic/JDK-8017010.js.EXPECTED)0
-rw-r--r--test/script/basic/JDK-8010924.js67
-rw-r--r--test/script/basic/JDK-8011209.js76
-rw-r--r--test/script/basic/JDK-8011237.js39
-rw-r--r--test/script/basic/JDK-8011274.js48
-rw-r--r--test/script/basic/JDK-8011357.js68
-rw-r--r--test/script/basic/JDK-8011362.js34
-rw-r--r--test/script/basic/JDK-8011362.js.EXPECTED2
-rw-r--r--test/script/basic/JDK-8011365.js72
-rw-r--r--test/script/basic/JDK-8011382.js115
-rw-r--r--test/script/basic/JDK-8011394.js45
-rw-r--r--test/script/basic/JDK-8011421.js51
-rw-r--r--test/script/basic/JDK-8011543.js42
-rw-r--r--test/script/basic/JDK-8011552.js37
-rw-r--r--test/script/basic/JDK-8011555.js42
-rw-r--r--test/script/basic/JDK-8011555.js.EXPECTED1
-rw-r--r--test/script/basic/JDK-8011578.js42
-rw-r--r--test/script/basic/JDK-8011578.js.EXPECTED22
-rw-r--r--test/script/basic/JDK-8011714.js68
-rw-r--r--test/script/basic/JDK-8011714.js.EXPECTED27
-rw-r--r--test/script/basic/JDK-8011749.js38
-rw-r--r--test/script/basic/JDK-8011749.js.EXPECTED5
-rw-r--r--test/script/basic/JDK-8011756.js59
-rw-r--r--test/script/basic/JDK-8011756.js.EXPECTED22
-rw-r--r--test/script/basic/JDK-8011960.js53
-rw-r--r--test/script/basic/JDK-8011974.js39
-rw-r--r--test/script/basic/JDK-8011980.js34
-rw-r--r--test/script/basic/JDK-8011980.js.EXPECTED4
-rw-r--r--test/script/basic/JDK-8012240.js47
-rw-r--r--test/script/basic/JDK-8012334.js80
-rw-r--r--test/script/basic/JDK-8012334.js.EXPECTED200
-rw-r--r--test/script/basic/JDK-8012457.js46
-rw-r--r--test/script/basic/JDK-8012460.js59
-rw-r--r--test/script/basic/JDK-8012460.js.EXPECTED18
-rw-r--r--test/script/basic/JDK-8012462.js47
-rw-r--r--test/script/basic/JDK-8012931.js38
-rw-r--r--test/script/basic/JDK-8012931.js.EXPECTED1
-rw-r--r--test/script/basic/JDK-8013131.js66
-rw-r--r--test/script/basic/JDK-8013131.js.EXPECTED14
-rw-r--r--test/script/basic/JDK-8013167.js32
-rw-r--r--test/script/basic/JDK-8013167.js.EXPECTED1
-rw-r--r--test/script/basic/JDK-8013325.js44
-rw-r--r--test/script/basic/JDK-8013325.js.EXPECTED2
-rw-r--r--test/script/basic/JDK-8013337.js71
-rw-r--r--test/script/basic/JDK-8013337.js.EXPECTED17
-rw-r--r--test/script/basic/JDK-8013444.js49
-rw-r--r--test/script/basic/JDK-8013444.js.EXPECTED3
-rw-r--r--test/script/basic/javaclassoverrides.js86
-rw-r--r--test/script/basic/javaclassoverrides.js.EXPECTED9
-rw-r--r--test/script/basic/try2.js49
-rw-r--r--test/script/basic/try2.js.EXPECTED8
-rw-r--r--test/script/error/JDK-8008814-1.js34
-rw-r--r--test/script/error/JDK-8008814-1.js.EXPECTED3
-rw-r--r--test/script/error/JDK-8008814-2.js34
-rw-r--r--test/script/error/JDK-8008814-2.js.EXPECTED3
-rw-r--r--test/script/trusted/logcoverage.js108
-rw-r--r--test/script/trusted/logcoverage.js.EXPECTED4
-rw-r--r--test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java24
-rw-r--r--test/src/jdk/nashorn/api/scripting/VariableArityTestInterface.java31
-rw-r--r--test/src/jdk/nashorn/internal/runtime/JSTypeTest.java85
-rw-r--r--test/src/jdk/nashorn/test/models/Jdk8011362TestSubject.java47
-rw-r--r--test/src/jdk/nashorn/test/models/VarArgConstructor.java44
-rw-r--r--tools/fxshell/jdk/nashorn/tools/FXShell.java194
227 files changed, 12600 insertions, 8506 deletions
diff --git a/.hgtags b/.hgtags
index 089f0f07..150d9deb 100644
--- a/.hgtags
+++ b/.hgtags
@@ -195,3 +195,8 @@ b8a1b238c77c7c00024daaa2cb7d10838e017b5f jdk8-b69
053d7c55dc8272b58b8bb870dc92a4acf896d52a jdk8-b83
999cc1bf55203f51b2985feae6378932667ecff2 jdk8-b84
e0378f0a50dafdcfb7b04f6401d320f89884baa1 aarch64-20130813
+e0378f0a50dafdcfb7b04f6401d320f89884baa1 jdk8-b85
+002ad9d6735f36d1204e133324c73058c8abb1b0 jdk8-b86
+774aeaa89bc15f4365e3c2fc36f6a3a0da70ba28 jdk8-b87
+40c107d1ae6f81a62e35dfe618b827897405e9b2 jdk8-b88
+45ce27fbe2720d80070095c0db7344ec41e2833d jdk8-b89
diff --git a/bin/verbose_octane.sh b/bin/verbose_octane.sh
index e70e9d48..1895afed 100644
--- a/bin/verbose_octane.sh
+++ b/bin/verbose_octane.sh
@@ -26,7 +26,7 @@ if [ -z $ITERS ]; then
ITERS=7
fi
NASHORN_JAR=dist/nashorn.jar
-JVM_FLAGS="-XX:+UnlockDiagnosticVMOptions -Dnashorn.unstable.relink.threshold=8 -Xms2G -Xmx2G -XX:+TieredCompilation -server -jar ${NASHORN_JAR}"
+JVM_FLAGS="-Djava.ext.dirs=`dirname $0`/../dist:$JAVA_HOME/jre/lib/ext -XX:+UnlockDiagnosticVMOptions -Dnashorn.unstable.relink.threshold=8 -Xms2G -Xmx2G -XX:+TieredCompilation -server -jar ${NASHORN_JAR}"
JVM_FLAGS7="-Xbootclasspath/p:${NASHORN_JAR} ${JVM_FLAGS}"
OCTANE_ARGS="--verbose --iterations ${ITERS}"
diff --git a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ConstructorGenerator.java b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ConstructorGenerator.java
index 836cb893..7c59f8c3 100644
--- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ConstructorGenerator.java
+++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ConstructorGenerator.java
@@ -38,7 +38,6 @@ import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_DUPLICATE_DE
import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_FIELD_NAME;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_TYPE;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_DESC;
-import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPE;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPEOBJECT_SETCONSTRUCTOR;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPEOBJECT_SETCONSTRUCTOR_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPEOBJECT_TYPE;
@@ -47,6 +46,8 @@ import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIM
import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_TYPE;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETARITY;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETARITY_DESC;
+import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETPROTOTYPE;
+import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETPROTOTYPE_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_TYPE;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTOBJECT_INIT_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTOBJECT_TYPE;
@@ -238,7 +239,7 @@ public class ConstructorGenerator extends ClassGenerator {
mi.loadThis();
mi.invokeStatic(PROTOTYPEOBJECT_TYPE, PROTOTYPEOBJECT_SETCONSTRUCTOR,
PROTOTYPEOBJECT_SETCONSTRUCTOR_DESC);
- mi.putField(SCRIPTFUNCTION_TYPE, PROTOTYPE, OBJECT_DESC);
+ mi.invokeVirtual(SCRIPTFUNCTION_TYPE, SCRIPTFUNCTION_SETPROTOTYPE, SCRIPTFUNCTION_SETPROTOTYPE_DESC);
}
}
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 8c2dcef0..5a5032f9 100644
--- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/StringConstants.java
+++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/StringConstants.java
@@ -55,7 +55,6 @@ public interface StringConstants {
static final Type TYPE_SCRIPTFUNCTIONIMPL = Type.getType(ScriptFunctionImpl.class);
static final Type TYPE_SCRIPTOBJECT = Type.getType(ScriptObject.class);
- static final String PROTOTYPE = "prototype";
static final String PROTOTYPE_SUFFIX = "$Prototype";
static final String CONSTRUCTOR_SUFFIX = "$Constructor";
// This field name is known to Nashorn runtime (Context).
@@ -88,6 +87,8 @@ public interface StringConstants {
Type.getMethodDescriptor(Type.VOID_TYPE, TYPE_STRING, TYPE_METHODHANDLE, TYPE_PROPERTYMAP, TYPE_METHODHANDLE_ARRAY);
static final String SCRIPTFUNCTION_SETARITY = "setArity";
static final String SCRIPTFUNCTION_SETARITY_DESC = Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE);
+ static final String SCRIPTFUNCTION_SETPROTOTYPE = "setPrototype";
+ static final String SCRIPTFUNCTION_SETPROTOTYPE_DESC = Type.getMethodDescriptor(Type.VOID_TYPE, TYPE_OBJECT);
static final String PROTOTYPEOBJECT_TYPE = TYPE_PROTOTYPEOBJECT.getInternalName();
static final String PROTOTYPEOBJECT_SETCONSTRUCTOR = "setConstructor";
static final String PROTOTYPEOBJECT_SETCONSTRUCTOR_DESC = Type.getMethodDescriptor(Type.VOID_TYPE, TYPE_OBJECT, TYPE_OBJECT);
diff --git a/docs/JavaScriptingProgrammersGuide.html b/docs/JavaScriptingProgrammersGuide.html
index dd243d3a..811b29c9 100644
--- a/docs/JavaScriptingProgrammersGuide.html
+++ b/docs/JavaScriptingProgrammersGuide.html
@@ -1,3 +1,27 @@
+<!--
+ 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.
+-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html class=" regenabled gecko radius jsenabled regloaded" xmlns="http://www.w3.org/1999/xhtml"><head>
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
diff --git a/make/build-nasgen.xml b/make/build-nasgen.xml
index 5797befa..a50d41e0 100644
--- a/make/build-nasgen.xml
+++ b/make/build-nasgen.xml
@@ -37,6 +37,7 @@
<pathelement location="${basedir}/buildtools/nasgen/dist/nasgen.jar"/>
<pathelement path="${basedir}/build/classes"/>
</classpath>
+ <jvmarg value="-Djava.ext.dirs="/>
<arg value="${basedir}/build/classes"/>
<arg value="jdk.nashorn.internal.objects"/>
<arg value="${basedir}/build/classes"/>
diff --git a/make/build.xml b/make/build.xml
index 945ccaa2..d884b2fe 100644
--- a/make/build.xml
+++ b/make/build.xml
@@ -56,7 +56,7 @@
<target name="init" depends="init-conditions, init-cc">
<!-- extends jvm args -->
- <property name="run.test.jvmargs">${run.test.jvmargs.main} ${run.test.cc.jvmargs}</property>
+ <property name="run.test.jvmargs" value="${run.test.jvmargs.main} ${run.test.cc.jvmargs}"/>
<property name="run.test.jvmargs.octane" value="${run.test.jvmargs.octane.main} ${run.test.cc.jvmargs}" />
<echo message="run.test.jvmargs=${run.test.jvmargs}"/>
@@ -139,6 +139,31 @@
</manifest>
</jar>
</target>
+
+ <target name="build-fxshell" depends="jar">
+ <description>Builds the javafx shell.</description>
+ <mkdir dir="${fxshell.classes.dir}"/>
+ <javac srcdir="${fxshell.dir}"
+ destdir="${fxshell.classes.dir}"
+ classpath="${dist.jar}:${javac.classpath}"
+ debug="${javac.debug}"
+ encoding="${javac.encoding}"
+ includeantruntime="false">
+ </javac>
+ <jar jarfile="${fxshell.jar}" manifest="${meta.inf.dir}/MANIFEST.MF" index="true" filesetmanifest="merge">
+ <fileset dir="${fxshell.classes.dir}"/>
+ <manifest>
+ <attribute name="Archiver-Version" value="n/a"/>
+ <attribute name="Build-Jdk" value="${java.runtime.version}"/>
+ <attribute name="Built-By" value="n/a"/>
+ <attribute name="Created-By" value="Ant jar task"/>
+ <section name="jdk/nashorn/">
+ <attribute name="Implementation-Title" value="Oracle Nashorn FXShell"/>
+ <attribute name="Implementation-Version" value="${nashorn.version}"/>
+ </section>
+ </manifest>
+ </jar>
+ </target>
<target name="javadoc" depends="prepare">
<javadoc destdir="${dist.javadoc.dir}" use="yes" overview="src/overview.html" windowtitle="${nashorn.product.name} ${nashorn.version}" additionalparam="-quiet" failonerror="true">
diff --git a/make/project.properties b/make/project.properties
index 58da977d..e2f39bb7 100644
--- a/make/project.properties
+++ b/make/project.properties
@@ -65,6 +65,12 @@ dist.dir=dist
dist.jar=${dist.dir}/nashorn.jar
dist.javadoc.dir=${dist.dir}/javadoc
+# nashorn javafx shell
+fxshell.tool = jdk.nashorn.tools.FXShell
+fxshell.classes.dir = ${build.dir}/fxshell/classes
+fxshell.dir = tools/fxshell
+fxshell.jar = ${dist.dir}/nashornfx.jar
+
# jars refererred
file.reference.testng.jar=test/lib/testng.jar
@@ -208,7 +214,7 @@ run.test.xms=2G
# -XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintNMethods
# add '-Dtest.js.outofprocess' to run each test in a new sub-process
-run.test.jvmargs.main=-server -Xmx${run.test.xmx} -XX:-TieredCompilation -esa -ea -Dnashorn.debug=true -Dfile.encoding=UTF-8
+run.test.jvmargs.main=-server -Xmx${run.test.xmx} -XX:-TieredCompilation -ea -Dnashorn.debug=true -Dfile.encoding=UTF-8
#-XX:+HeapDumpOnOutOfMemoryError -XX:-UseCompressedKlassPointers -XX:+PrintHeapAtGC -XX:ClassMetaspaceSize=300M
run.test.jvmargs.octane.main=-Xms${run.test.xms} ${run.test.jvmargs.main}
diff --git a/src/jdk/internal/dynalink/beans/ClassString.java b/src/jdk/internal/dynalink/beans/ClassString.java
index d6c6da08..dfcb3786 100644
--- a/src/jdk/internal/dynalink/beans/ClassString.java
+++ b/src/jdk/internal/dynalink/beans/ClassString.java
@@ -96,6 +96,11 @@ import jdk.internal.dynalink.support.TypeUtilities;
* @author Attila Szegedi
*/
final class ClassString {
+ /**
+ * An anonymous inner class used solely to represent the "type" of null values for method applicability checking.
+ */
+ static final Class<?> NULL_CLASS = (new Object() { /* Intentionally empty */ }).getClass();
+
private final Class<?>[] classes;
private int hashCode;
@@ -203,6 +208,9 @@ final class ClassString {
}
private static boolean canConvert(LinkerServices ls, Class<?> from, Class<?> to) {
+ if(from == NULL_CLASS) {
+ return !to.isPrimitive();
+ }
return ls == null ? TypeUtilities.isMethodInvocationConvertible(from, to) : ls.canConvert(from, to);
}
}
diff --git a/src/jdk/internal/dynalink/beans/OverloadedMethod.java b/src/jdk/internal/dynalink/beans/OverloadedMethod.java
index d0015162..7093e757 100644
--- a/src/jdk/internal/dynalink/beans/OverloadedMethod.java
+++ b/src/jdk/internal/dynalink/beans/OverloadedMethod.java
@@ -152,7 +152,7 @@ class OverloadedMethod {
final Class<?>[] argTypes = new Class[args.length];
for(int i = 0; i < argTypes.length; ++i) {
final Object arg = args[i];
- argTypes[i] = arg == null ? callSiteType.parameterType(i) : arg.getClass();
+ argTypes[i] = arg == null ? ClassString.NULL_CLASS : arg.getClass();
}
final ClassString classString = new ClassString(argTypes);
MethodHandle method = argTypesToMethods.get(classString);
diff --git a/src/jdk/internal/dynalink/beans/StaticClassIntrospector.java b/src/jdk/internal/dynalink/beans/StaticClassIntrospector.java
index d4cedb7f..214152a4 100644
--- a/src/jdk/internal/dynalink/beans/StaticClassIntrospector.java
+++ b/src/jdk/internal/dynalink/beans/StaticClassIntrospector.java
@@ -106,7 +106,11 @@ class StaticClassIntrospector extends FacetIntrospector {
@Override
MethodHandle editMethodHandle(MethodHandle mh) {
- MethodHandle newHandle = MethodHandles.dropArguments(mh, 0, Object.class);
+ return dropReceiver(mh, Object.class);
+ }
+
+ static MethodHandle dropReceiver(final MethodHandle mh, final Class<?> receiverClass) {
+ MethodHandle newHandle = MethodHandles.dropArguments(mh, 0, receiverClass);
// NOTE: this is a workaround for the fact that dropArguments doesn't preserve vararg collector state.
if(mh.isVarargsCollector() && !newHandle.isVarargsCollector()) {
final MethodType type = mh.type();
diff --git a/src/jdk/internal/dynalink/beans/StaticClassLinker.java b/src/jdk/internal/dynalink/beans/StaticClassLinker.java
index c87d6667..d6096fe5 100644
--- a/src/jdk/internal/dynalink/beans/StaticClassLinker.java
+++ b/src/jdk/internal/dynalink/beans/StaticClassLinker.java
@@ -144,7 +144,7 @@ class StaticClassLinker implements TypeBasedGuardingDynamicLinker {
}
private static MethodHandle drop(MethodHandle mh) {
- return MethodHandles.dropArguments(mh, 0, StaticClass.class);
+ return StaticClassIntrospector.dropReceiver(mh, StaticClass.class);
}
@Override
diff --git a/src/jdk/nashorn/api/scripting/Formatter.java b/src/jdk/nashorn/api/scripting/Formatter.java
index 5cb19ed4..a14a83e4 100644
--- a/src/jdk/nashorn/api/scripting/Formatter.java
+++ b/src/jdk/nashorn/api/scripting/Formatter.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 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
diff --git a/src/jdk/nashorn/api/scripting/NashornScriptEngine.java b/src/jdk/nashorn/api/scripting/NashornScriptEngine.java
index 55967bb0..197a6da4 100644
--- a/src/jdk/nashorn/api/scripting/NashornScriptEngine.java
+++ b/src/jdk/nashorn/api/scripting/NashornScriptEngine.java
@@ -397,10 +397,7 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
}
setContextVariables(ctxt);
- final Object val = ctxt.getAttribute(ScriptEngine.FILENAME);
- final String fileName = (val != null) ? val.toString() : "<eval>";
- Object res = ScriptRuntime.apply(script, ctxtGlobal);
- return ScriptObjectMirror.translateUndefined(ScriptObjectMirror.wrap(res, ctxtGlobal));
+ return ScriptObjectMirror.translateUndefined(ScriptObjectMirror.wrap(ScriptRuntime.apply(script, ctxtGlobal), ctxtGlobal));
} catch (final Exception e) {
throwAsScriptException(e);
throw new AssertionError("should not reach here");
diff --git a/src/jdk/nashorn/api/scripting/NashornScriptEngineFactory.java b/src/jdk/nashorn/api/scripting/NashornScriptEngineFactory.java
index e38284da..1ca6dcdd 100644
--- a/src/jdk/nashorn/api/scripting/NashornScriptEngineFactory.java
+++ b/src/jdk/nashorn/api/scripting/NashornScriptEngineFactory.java
@@ -210,42 +210,10 @@ public final class NashornScriptEngineFactory implements ScriptEngineFactory {
}
private static ClassLoader getAppClassLoader() {
- if (System.getSecurityManager() == null) {
- return Thread.currentThread().getContextClassLoader();
- }
-
- // Try to determine the caller class loader. Use that if it can be
- // found. If not, use the class loader of nashorn itself as the
- // "application" class loader for scripts.
-
- // User could have called ScriptEngineFactory.getScriptEngine()
- //
- // <caller>
- // <factory.getScriptEngine()>
- // <factory.getAppClassLoader()>
- // <Reflection.getCallerClass()>
- //
- // or used one of the getEngineByABC methods of ScriptEngineManager.
- //
- // <caller>
- // <ScriptEngineManager.getEngineByName()>
- // <factory.getScriptEngine()>
- // <factory.getAppClassLoader()>
- // <Reflection.getCallerClass()>
-
- // So, stack depth is 3 or 4 (recall it is zero based). We try
- // stack depths 3, 4 and look for non-bootstrap caller.
- Class<?> caller = null;
- for (int depth = 3; depth < 5; depth++) {
- caller = Reflection.getCallerClass(depth);
- if (caller != null && caller.getClassLoader() != null) {
- // found a non-bootstrap caller
- break;
- }
- }
-
- final ClassLoader ccl = (caller == null)? null : caller.getClassLoader();
- // if caller loader is null, then use nashorn's own loader
+ // Revisit: script engine implementation needs the capability to
+ // find the class loader of the context in which the script engine
+ // is running so that classes will be found and loaded properly
+ ClassLoader ccl = Thread.currentThread().getContextClassLoader();
return (ccl == null)? NashornScriptEngineFactory.class.getClassLoader() : ccl;
}
}
diff --git a/src/jdk/nashorn/internal/codegen/Attr.java b/src/jdk/nashorn/internal/codegen/Attr.java
index 9ecf7c89..9b17c47f 100644
--- a/src/jdk/nashorn/internal/codegen/Attr.java
+++ b/src/jdk/nashorn/internal/codegen/Attr.java
@@ -25,14 +25,16 @@
package jdk.nashorn.internal.codegen;
+import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
import static jdk.nashorn.internal.codegen.CompilerConstants.EXCEPTION_PREFIX;
import static jdk.nashorn.internal.codegen.CompilerConstants.ITERATOR_PREFIX;
+import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
-import static jdk.nashorn.internal.codegen.CompilerConstants.SCRIPT_RETURN;
import static jdk.nashorn.internal.codegen.CompilerConstants.SWITCH_TAG_PREFIX;
import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
+import static jdk.nashorn.internal.ir.Symbol.IS_FUNCTION_SELF;
import static jdk.nashorn.internal.ir.Symbol.IS_GLOBAL;
import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL;
import static jdk.nashorn.internal.ir.Symbol.IS_LET;
@@ -42,18 +44,18 @@ import static jdk.nashorn.internal.ir.Symbol.IS_THIS;
import static jdk.nashorn.internal.ir.Symbol.IS_VAR;
import static jdk.nashorn.internal.ir.Symbol.KINDMASK;
+import java.util.ArrayDeque;
import java.util.ArrayList;
+import java.util.Deque;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
-import java.util.ListIterator;
import java.util.Set;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.AccessNode;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.CallNode;
-import jdk.nashorn.internal.ir.CallNode.EvalArgs;
import jdk.nashorn.internal.ir.CaseNode;
import jdk.nashorn.internal.ir.CatchNode;
import jdk.nashorn.internal.ir.ForNode;
@@ -62,6 +64,7 @@ import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.IndexNode;
import jdk.nashorn.internal.ir.LexicalContext;
+import jdk.nashorn.internal.ir.LexicalContextNode;
import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
import jdk.nashorn.internal.ir.Node;
@@ -76,6 +79,7 @@ import jdk.nashorn.internal.ir.TernaryNode;
import jdk.nashorn.internal.ir.TryNode;
import jdk.nashorn.internal.ir.UnaryNode;
import jdk.nashorn.internal.ir.VarNode;
+import jdk.nashorn.internal.ir.WithNode;
import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.TokenType;
@@ -105,21 +109,22 @@ import jdk.nashorn.internal.runtime.ScriptObject;
*/
final class Attr extends NodeOperatorVisitor {
+
/**
* Local definitions in current block (to discriminate from function
* declarations always defined in the function scope. This is for
* "can be undefined" analysis.
*/
- private Set<String> localDefs;
+ private final Deque<Set<String>> localDefs;
/**
* Local definitions in current block to guard against cases like
* NASHORN-467 when things can be undefined as they are used before
* their local var definition. *sigh* JavaScript...
*/
- private Set<String> localUses;
+ private final Deque<Set<String>> localUses;
- private final LexicalContext lexicalContext = new LexicalContext();
+ private final Deque<Type> returnTypes;
private static final DebugLogger LOG = new DebugLogger("attr");
private static final boolean DEBUG = LOG.isEnabled();
@@ -128,10 +133,13 @@ final class Attr extends NodeOperatorVisitor {
* Constructor.
*/
Attr() {
+ localDefs = new ArrayDeque<>();
+ localUses = new ArrayDeque<>();
+ returnTypes = new ArrayDeque<>();
}
@Override
- protected Node enterDefault(final Node node) {
+ protected boolean enterDefault(final Node node) {
return start(node);
}
@@ -142,76 +150,127 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveAccessNode(final AccessNode accessNode) {
- newTemporary(Type.OBJECT, accessNode); //While Object type is assigned here, Access Specialization in FinalizeTypes may narrow this
+ ensureSymbol(Type.OBJECT, accessNode); //While Object type is assigned here, Access Specialization in FinalizeTypes may narrow this
end(accessNode);
return accessNode;
}
- @Override
- public Node enterBlock(final Block block) {
- lexicalContext.push(block);
- start(block);
+ private void enterFunctionBody() {
- final Set<String> savedLocalDefs = localDefs;
- final Set<String> savedLocalUses = localUses;
+ final FunctionNode functionNode = getLexicalContext().getCurrentFunction();
+ final Block body = getLexicalContext().getCurrentBlock();
+ initCallee(body);
+ initThis(body);
+ if (functionNode.isVarArg()) {
+ initVarArg(body, functionNode.needsArguments());
+ }
- block.setFrame(getCurrentFunctionNode().pushFrame());
+ initParameters(functionNode, body);
+ initScope(body);
+ initReturn(body);
- try {
- // a block starts out by copying the local defs and local uses
- // from the outer level. But we need the copies, as when we
- // leave the block the def and use sets given upon entry must
- // be restored
- localDefs = new HashSet<>(savedLocalDefs);
- localUses = new HashSet<>(savedLocalUses);
-
- block.visitStatements(this);
- } finally {
- localDefs = savedLocalDefs;
- localUses = savedLocalUses;
+ if (functionNode.isProgram()) {
+ initFromPropertyMap(body);
+ } else if(!functionNode.isDeclared()) {
+ // It's neither declared nor program - it's a function expression then; assign it a self-symbol.
- getCurrentFunctionNode().popFrame();
+ if (functionNode.getSymbol() != null) {
+ // a temporary left over from an earlier pass when the function was lazy
+ assert functionNode.getSymbol().isTemp();
+ // remove it
+ functionNode.setSymbol(null);
+ }
+ final boolean anonymous = functionNode.isAnonymous();
+ final String name = anonymous ? null : functionNode.getIdent().getName();
+ if (anonymous || body.getExistingSymbol(name) != null) {
+ // The function is either anonymous, or another local identifier already trumps its name on entry:
+ // either it has the same name as one of its parameters, or is named "arguments" and also references the
+ // "arguments" identifier in its body.
+ ensureSymbol(functionNode, Type.typeFor(ScriptFunction.class), functionNode);
+ } else {
+ final Symbol selfSymbol = defineSymbol(body, name, IS_VAR | IS_FUNCTION_SELF, functionNode);
+ assert selfSymbol.isFunctionSelf();
+ newType(selfSymbol, Type.OBJECT);
+ }
}
- end(block);
+ /*
+ * This pushes all declarations (except for non-statements, i.e. for
+ * node temporaries) to the top of the function scope. This way we can
+ * get around problems like
+ *
+ * while (true) {
+ * break;
+ * if (true) {
+ * var s;
+ * }
+ * }
+ *
+ * to an arbitrary nesting depth.
+ *
+ * @see NASHORN-73
+ */
- lexicalContext.pop(block);
- return null;
- }
+ // This visitor will assign symbol to all declared variables, except function declarations (which are taken care
+ // in a separate step above) and "var" declarations in for loop initializers.
+ body.accept(new NodeOperatorVisitor() {
+ @Override
+ public boolean enterFunctionNode(final FunctionNode nestedFn) {
+ return false;
+ }
- @Override
- public Node enterCallNode(final CallNode callNode) {
- start(callNode);
+ @Override
+ public boolean enterVarNode(final VarNode varNode) {
- callNode.getFunction().accept(this);
+ // any declared symbols that aren't visited need to be typed as well, hence the list
- final List<Node> acceptedArgs = new ArrayList<>(callNode.getArgs().size());
- for (final Node arg : callNode.getArgs()) {
- LOG.info("Doing call arg " + arg);
- acceptedArgs.add(arg.accept(this));
- }
- callNode.setArgs(acceptedArgs);
+ if (varNode.isStatement()) {
- final EvalArgs evalArgs = callNode.getEvalArgs();
- if (evalArgs != null) {
- evalArgs.setCode(evalArgs.getCode().accept(this));
+ final IdentNode ident = varNode.getName();
+ final Symbol symbol = defineSymbol(body, ident.getName(), IS_VAR, new IdentNode(ident));
+ functionNode.addDeclaredSymbol(symbol);
+ if (varNode.isFunctionDeclaration()) {
+ newType(symbol, FunctionNode.FUNCTION_TYPE);
+ }
+ }
+ return false;
+ }
+ });
+ }
- final IdentNode thisNode = new IdentNode(getCurrentFunctionNode().getThisNode());
- assert thisNode.getSymbol() != null; //should copy attributed symbol and that's it
- evalArgs.setThis(thisNode);
+ @Override
+ public boolean enterBlock(final Block block) {
+ start(block);
+
+ if (getLexicalContext().isFunctionBody()) {
+ enterFunctionBody();
}
+ pushLocalsBlock();
- newTemporary(callNode.getType(), callNode); // access specialization in FinalizeTypes may narrow it further later
+ return true;
+ }
- end(callNode);
+ @Override
+ public Node leaveBlock(final Block block) {
+ popLocals();
+ return end(block);
+ }
- return null;
+ @Override
+ public Node leaveCallNode(final CallNode callNode) {
+ ensureSymbol(callNode.getType(), callNode);
+ return end(callNode);
}
@Override
- public Node enterCatchNode(final CatchNode catchNode) {
+ public boolean enterCallNode(final CallNode callNode) {
+ return start(callNode);
+ }
+
+ @Override
+ public boolean enterCatchNode(final CatchNode catchNode) {
final IdentNode exception = catchNode.getException();
- final Block block = getCurrentBlock();
+ final Block block = getLexicalContext().getCurrentBlock();
start(catchNode);
@@ -220,7 +279,7 @@ final class Attr extends NodeOperatorVisitor {
newType(def, Type.OBJECT);
addLocalDef(exception.getName());
- return catchNode;
+ return true;
}
/**
@@ -240,7 +299,7 @@ final class Attr extends NodeOperatorVisitor {
flags |= IS_SCOPE;
}
- final FunctionNode function = lexicalContext.getFunction(block);
+ final FunctionNode function = getLexicalContext().getFunction(block);
if (symbol != null) {
// Symbol was already defined. Check if it needs to be redefined.
if ((flags & KINDMASK) == IS_PARAM) {
@@ -254,7 +313,6 @@ final class Attr extends NodeOperatorVisitor {
}
} else if ((flags & KINDMASK) == IS_VAR) {
if ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET) {
- assert !((flags & IS_LET) == IS_LET && symbol.getBlock() == block) : "duplicate let variable in block";
// Always create a new definition.
symbol = null;
} else {
@@ -272,17 +330,16 @@ final class Attr extends NodeOperatorVisitor {
// Determine where to create it.
if ((flags & Symbol.KINDMASK) == IS_VAR && ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET)) {
- symbolBlock = block;
+ symbolBlock = block; //internal vars are always defined in the block closest to them
} else {
- symbolBlock = function;
+ symbolBlock = getLexicalContext().getFunctionBody(function);
}
// Create and add to appropriate block.
- symbol = new Symbol(name, flags, node, symbolBlock);
+ symbol = new Symbol(name, flags);
symbolBlock.putSymbol(name, symbol);
if ((flags & Symbol.KINDMASK) != IS_GLOBAL) {
- symbolBlock.getFrame().addSymbol(symbol);
symbol.setNeedsSlot(true);
}
} else if (symbol.less(flags)) {
@@ -297,149 +354,100 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public Node enterFunctionNode(final FunctionNode functionNode) {
+ public boolean enterFunctionNode(final FunctionNode functionNode) {
start(functionNode, false);
- if (functionNode.isLazy()) {
- LOG.info("LAZY: " + functionNode.getName() + " => Promoting to OBJECT");
- newTemporary(lexicalContext.getCurrentFunction(), Type.OBJECT, functionNode);
- functionNode.setReturnType(Type.OBJECT);
- end(functionNode);
- return null;
- }
-
- lexicalContext.push(functionNode);
-
- clearLocalDefs();
- clearLocalUses();
-
- functionNode.setFrame(functionNode.pushFrame());
-
- initCallee(functionNode);
- initThis(functionNode);
- if (functionNode.isVarArg()) {
- initVarArg(functionNode);
- }
- initParameters(functionNode);
- initScope(functionNode);
- initReturn(functionNode);
-
- // Add all nested declared functions as symbols in this function
- for (final FunctionNode nestedFunction : functionNode.getDeclaredFunctions()) {
- final IdentNode ident = nestedFunction.getIdent();
- if (ident != null) {
- assert nestedFunction.isDeclared();
- final Symbol functionSymbol = defineSymbol(functionNode, ident.getName(), IS_VAR, nestedFunction);
- newType(functionSymbol, Type.typeFor(ScriptFunction.class));
+ if (functionNode.isDeclared()) {
+ final Iterator<Block> blocks = getLexicalContext().getBlocks();
+ if (blocks.hasNext()) {
+ defineSymbol(
+ blocks.next(),
+ functionNode.getIdent().getName(),
+ IS_VAR,
+ functionNode);
+ } else {
+ // Q: What's an outermost function in a lexical context that is not a program?
+ // A: It's a function being compiled lazily!
+ assert getLexicalContext().getOutermostFunction() == functionNode && !functionNode.isProgram();
}
}
- if (functionNode.isProgram()) {
- initFromPropertyMap(functionNode);
- }
-
- // Add function name as local symbol
- if (!functionNode.isDeclared() && !functionNode.isProgram()) {
- if(functionNode.getSymbol() != null) {
- // a temporary left over from an earlier pass when the function was lazy
- assert functionNode.getSymbol().isTemp();
- // remove it
- functionNode.setSymbol(null);
- }
- final Symbol selfSymbol;
- if(functionNode.isAnonymous()) {
- selfSymbol = newTemporary(functionNode, Type.OBJECT, functionNode);
- } else {
- selfSymbol = defineSymbol(functionNode, functionNode.getIdent().getName(), IS_VAR, functionNode);
- newType(selfSymbol, Type.OBJECT);
- selfSymbol.setNode(functionNode);
- }
+ if (functionNode.isLazy()) {
+ LOG.info("LAZY: ", functionNode.getName(), " => Promoting to OBJECT");
+ ensureSymbol(getLexicalContext().getCurrentFunction(), Type.OBJECT, functionNode);
+ end(functionNode);
+ return false;
}
- /*
- * This pushes all declarations (except for non-statements, i.e. for
- * node temporaries) to the top of the function scope. This way we can
- * get around problems like
- *
- * while (true) {
- * break;
- * if (true) {
- * var s;
- * }
- * }
- *
- * to an arbitrary nesting depth.
- *
- * @see NASHORN-73
- */
+ returnTypes.push(functionNode.getReturnType());
+ pushLocalsFunction();
+ return true;
+ }
- final List<Symbol> declaredSymbols = new ArrayList<>();
- // This visitor will assign symbol to all declared variables, except function declarations (which are taken care
- // in a separate step above) and "var" declarations in for loop initializers.
- functionNode.accept(new NodeOperatorVisitor() {
- @Override
- public Node enterFunctionNode(FunctionNode nestedFn) {
- // Don't descend into nested functions
- return nestedFn == functionNode ? nestedFn : null;
- }
- @Override
- public Node enterVarNode(VarNode varNode) {
- if(varNode.isStatement() && !varNode.isFunctionDeclaration()) {
- final IdentNode ident = varNode.getName();
- // any declared symbols that aren't visited need to be typed as well, hence the list
- declaredSymbols.add(defineSymbol(functionNode, ident.getName(), IS_VAR, new IdentNode(ident)));
- }
- return null;
- }
- });
+ @Override
+ public Node leaveFunctionNode(final FunctionNode functionNode) {
+ FunctionNode newFunctionNode = functionNode;
- visitFunctionStatements(functionNode);
+ final LexicalContext lc = getLexicalContext();
//unknown parameters are promoted to object type.
- finalizeParameters(functionNode);
- finalizeTypes(functionNode);
- for (final Symbol symbol : declaredSymbols) {
+ finalizeParameters(newFunctionNode);
+ finalizeTypes(newFunctionNode);
+ for (final Symbol symbol : newFunctionNode.getDeclaredSymbols()) {
if (symbol.getSymbolType().isUnknown()) {
symbol.setType(Type.OBJECT);
symbol.setCanBeUndefined();
}
}
- if (functionNode.getReturnType().isUnknown()) {
- LOG.info("Unknown return type promoted to object");
- functionNode.setReturnType(Type.OBJECT);
- }
+ final Block body = newFunctionNode.getBody();
- if (functionNode.getSelfSymbolInit() != null) {
- LOG.info("Accepting self symbol init " + functionNode.getSelfSymbolInit() + " for " + functionNode.getName());
- final Node init = functionNode.getSelfSymbolInit();
- final List<Node> newStatements = new ArrayList<>();
- newStatements.add(init);
- newStatements.addAll(functionNode.getStatements());
- functionNode.setStatements(newStatements);
- functionNode.setNeedsSelfSymbol(functionNode.getSelfSymbolInit().accept(this));
+ if (newFunctionNode.hasLazyChildren()) {
+ //the final body has already been assigned as we have left the function node block body by now
+ objectifySymbols(body);
}
- if (functionNode.hasLazyChildren()) {
- objectifySymbols(functionNode);
- }
+ if (body.getFlag(Block.NEEDS_SELF_SYMBOL)) {
+ final IdentNode callee = compilerConstant(CALLEE);
+ final VarNode selfInit =
+ new VarNode(
+ newFunctionNode.getSource(),
+ newFunctionNode.getToken(),
+ newFunctionNode.getFinish(),
+ newFunctionNode.getIdent(),
+ callee);
- functionNode.popFrame();
+ LOG.info("Accepting self symbol init ", selfInit, " for ", newFunctionNode.getName());
- functionNode.setState(CompilationState.ATTR);
+ final List<Node> newStatements = new ArrayList<>();
+ newStatements.add(selfInit);
+ assert callee.getSymbol() != null && callee.getSymbol().hasSlot();
- end(functionNode, false);
- lexicalContext.pop(functionNode);
+ final IdentNode name = selfInit.getName();
+ final Symbol nameSymbol = body.getExistingSymbol(name.getName());
- return null;
- }
+ assert nameSymbol != null;
+
+ name.setSymbol(nameSymbol);
+ selfInit.setSymbol(nameSymbol);
- private void visitFunctionStatements(final FunctionNode functionNode) {
- final List<Node> newStatements = new ArrayList<>(functionNode.getStatements());
- for(ListIterator<Node> stmts = newStatements.listIterator(); stmts.hasNext();) {
- stmts.set(stmts.next().accept(this));
+ newStatements.addAll(body.getStatements());
+ newFunctionNode = newFunctionNode.setBody(lc, body.setStatements(lc, newStatements));
+ }
+
+ if (returnTypes.peek().isUnknown()) {
+ LOG.info("Unknown return type promoted to object");
+ newFunctionNode = newFunctionNode.setReturnType(lc, Type.OBJECT);
}
- functionNode.setStatements(newStatements);
+ final Type returnType = returnTypes.pop();
+ newFunctionNode = newFunctionNode.setReturnType(lc, returnType.isUnknown() ? Type.OBJECT : returnType);
+ newFunctionNode = newFunctionNode.setState(lc, CompilationState.ATTR);
+
+ popLocals();
+
+ end(newFunctionNode, false);
+
+ return newFunctionNode; //.setFlag(lc, lc.getFlags(functionNode));
}
@Override
@@ -450,7 +458,7 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public Node enterIdentNode(final IdentNode identNode) {
+ public boolean enterIdentNode(final IdentNode identNode) {
final String name = identNode.getName();
start(identNode);
@@ -458,31 +466,28 @@ final class Attr extends NodeOperatorVisitor {
if (identNode.isPropertyName()) {
// assign a pseudo symbol to property name
final Symbol pseudoSymbol = pseudoSymbol(name);
- LOG.info("IdentNode is property name -> assigning pseudo symbol " + pseudoSymbol);
+ LOG.info("IdentNode is property name -> assigning pseudo symbol ", pseudoSymbol);
LOG.unindent();
identNode.setSymbol(pseudoSymbol);
- return null;
+ return false;
}
- final Block block = getCurrentBlock();
- final Symbol oldSymbol = identNode.getSymbol();
+ final LexicalContext lc = getLexicalContext();
+ final Block block = lc.getCurrentBlock();
+ final Symbol oldSymbol = identNode.getSymbol();
Symbol symbol = findSymbol(block, name);
//If an existing symbol with the name is found, use that otherwise, declare a new one
if (symbol != null) {
- LOG.info("Existing symbol = " + symbol);
- if (isFunctionExpressionSelfReference(symbol)) {
- final FunctionNode functionNode = (FunctionNode)symbol.getNode();
- assert functionNode.getCalleeNode() != null;
-
- final VarNode var = new VarNode(functionNode.getSource(), functionNode.getToken(), functionNode.getFinish(), functionNode.getIdent(), functionNode.getCalleeNode());
- //newTemporary(Type.OBJECT, var); //ScriptFunction? TODO
-
- functionNode.setNeedsSelfSymbol(var);
- }
-
- if (!identNode.isInitializedHere()) { // NASHORN-448
+ LOG.info("Existing symbol = ", symbol);
+ if (symbol.isFunctionSelf()) {
+ final FunctionNode functionNode = lc.getDefiningFunction(symbol);
+ assert functionNode != null;
+ assert lc.getFunctionBody(functionNode).getExistingSymbol(CALLEE.symbolName()) != null;
+ lc.setFlag(functionNode.getBody(), Block.NEEDS_SELF_SYMBOL);
+ newType(symbol, FunctionNode.FUNCTION_TYPE);
+ } else if (!identNode.isInitializedHere()) { // NASHORN-448
// here is a use outside the local def scope
if (!isLocalDef(name)) {
newType(symbol, Type.OBJECT);
@@ -491,26 +496,18 @@ final class Attr extends NodeOperatorVisitor {
}
identNode.setSymbol(symbol);
- // non-local: we need to put symbol in scope (if it isn't already)
- if (!isLocal(getCurrentFunctionNode(), symbol) && !symbol.isScope()) {
- symbol.setIsScope();
- }
+ // if symbol is non-local or we're in a with block, we need to put symbol in scope (if it isn't already)
+ maybeForceScope(symbol);
} else {
- LOG.info("No symbol exists. Declare undefined: " + symbol);
- symbol = useSymbol(block, name, identNode);
+ LOG.info("No symbol exists. Declare undefined: ", symbol);
+ symbol = defineSymbol(block, name, IS_GLOBAL, identNode);
// we have never seen this before, it can be undefined
newType(symbol, Type.OBJECT); // TODO unknown -we have explicit casts anyway?
symbol.setCanBeUndefined();
- symbol.setIsScope();
+ Symbol.setSymbolIsScope(lc, symbol);
}
- assert symbol != null;
- if(symbol.isGlobal()) {
- setUsesGlobalSymbol();
- } else if(symbol.isScope()) {
- final Iterator<Block> blocks = lexicalContext.getBlocks();
- blocks.next().setUsesScopeSymbol(symbol, blocks);
- }
+ setBlockScope(name, symbol);
if (symbol != oldSymbol && !identNode.isInitializedHere()) {
symbol.increaseUseCount();
@@ -519,42 +516,93 @@ final class Attr extends NodeOperatorVisitor {
end(identNode);
- return null;
+ return false;
}
/**
- * Marks the current function as one using any global symbol. The function and all its parent functions will all be
- * marked as needing parent scope.
- * @see #needsParentScope()
+ * If the symbol isn't already a scope symbol, and it is either not local to the current function, or it is being
+ * referenced from within a with block, we force it to be a scope symbol.
+ * @param symbol the symbol that might be scoped
*/
- private void setUsesGlobalSymbol() {
- for(final Iterator<FunctionNode> fns = lexicalContext.getFunctions(); fns.hasNext();) {
- fns.next().setUsesAncestorScope();
+ private void maybeForceScope(final Symbol symbol) {
+ if(!symbol.isScope() && symbolNeedsToBeScope(symbol)) {
+ Symbol.setSymbolIsScope(getLexicalContext(), symbol);
}
}
- /**
- * Declare the use of a symbol in a block.
- *
- * @param block block in which the symbol is used
- * @param name Name of symbol.
- * @param node Using node
- *
- * @return Symbol for given name.
- */
- private Symbol useSymbol(final Block block, final String name, final Node node) {
- Symbol symbol = findSymbol(block, name);
+ private boolean symbolNeedsToBeScope(Symbol symbol) {
+ if(symbol.isThis() || symbol.isInternal()) {
+ return false;
+ }
+ boolean previousWasBlock = false;
+ for(final Iterator<LexicalContextNode> it = getLexicalContext().getAllNodes(); it.hasNext();) {
+ final LexicalContextNode node = it.next();
+ if(node instanceof FunctionNode) {
+ // We reached the function boundary without seeing a definition for the symbol - it needs to be in
+ // scope.
+ return true;
+ } else if(node instanceof WithNode) {
+ if(previousWasBlock) {
+ // We reached a WithNode; the symbol must be scoped. Note that if the WithNode was not immediately
+ // preceded by a block, this means we're currently processing its expression, not its body,
+ // therefore it doesn't count.
+ return true;
+ }
+ previousWasBlock = false;
+ } else if(node instanceof Block) {
+ if(((Block)node).getExistingSymbol(symbol.getName()) == symbol) {
+ // We reached the block that defines the symbol without reaching either the function boundary, or a
+ // WithNode. The symbol need not be scoped.
+ return false;
+ }
+ previousWasBlock = true;
+ } else {
+ previousWasBlock = false;
+ }
+ }
+ throw new AssertionError();
+ }
- if (symbol == null) {
- // If not found, declare as a free var.
- symbol = defineSymbol(block, name, IS_GLOBAL, node);
- } else {
- node.setSymbol(symbol);
+ private void setBlockScope(final String name, final Symbol symbol) {
+ assert symbol != null;
+ if (symbol.isGlobal()) {
+ setUsesGlobalSymbol();
+ return;
}
- return symbol;
+ if (symbol.isScope()) {
+ final LexicalContext lc = getLexicalContext();
+
+ Block scopeBlock = null;
+ for (final Iterator<LexicalContextNode> contextNodeIter = getLexicalContext().getAllNodes(); contextNodeIter.hasNext(); ) {
+ final LexicalContextNode node = contextNodeIter.next();
+ if (node instanceof Block) {
+ if (((Block)node).getExistingSymbol(name) != null) {
+ scopeBlock = (Block)node;
+ break;
+ }
+ } else if (node instanceof FunctionNode) {
+ lc.setFlag(node, FunctionNode.USES_ANCESTOR_SCOPE);
+ }
+ }
+
+ if (scopeBlock != null) {
+ assert getLexicalContext().contains(scopeBlock);
+ lc.setFlag(scopeBlock, Block.NEEDS_SCOPE);
+ }
+ }
}
+ /**
+ * Marks the current function as one using any global symbol. The function and all its parent functions will all be
+ * marked as needing parent scope.
+ * @see #needsParentScope()
+ */
+ private void setUsesGlobalSymbol() {
+ for (final Iterator<FunctionNode> fns = getLexicalContext().getFunctions(); fns.hasNext();) {
+ getLexicalContext().setFlag(fns.next(), FunctionNode.USES_ANCESTOR_SCOPE);
+ }
+ }
/**
* Search for symbol in the lexical context starting from the given block.
@@ -564,7 +612,7 @@ final class Attr extends NodeOperatorVisitor {
private Symbol findSymbol(final Block block, final String name) {
// Search up block chain to locate symbol.
- for(final Iterator<Block> blocks = lexicalContext.getBlocks(block); blocks.hasNext();) {
+ for(final Iterator<Block> blocks = getLexicalContext().getBlocks(block); blocks.hasNext();) {
// Find name.
final Symbol symbol = blocks.next().getExistingSymbol(name);
// If found then we are good.
@@ -577,13 +625,13 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveIndexNode(final IndexNode indexNode) {
- newTemporary(Type.OBJECT, indexNode); //TODO
+ ensureSymbol(Type.OBJECT, indexNode); //TODO
return indexNode;
}
@SuppressWarnings("rawtypes")
@Override
- public Node enterLiteralNode(final LiteralNode literalNode) {
+ public boolean enterLiteralNode(final LiteralNode literalNode) {
try {
start(literalNode);
assert !literalNode.isTokenType(TokenType.THIS) : "tokentype for " + literalNode + " is this"; //guard against old dead code case. literal nodes should never inherit tokens
@@ -604,26 +652,33 @@ final class Attr extends NodeOperatorVisitor {
assert !(literalNode.getValue() instanceof Node) : "literals with Node values not supported";
}
- getCurrentFunctionNode().newLiteral(literalNode);
+ getLexicalContext().getCurrentFunction().newLiteral(literalNode);
} finally {
end(literalNode);
}
- return null;
+
+ return false;
+ }
+
+ @Override
+ public boolean enterObjectNode(final ObjectNode objectNode) {
+ return start(objectNode);
}
@Override
public Node leaveObjectNode(final ObjectNode objectNode) {
- newTemporary(Type.OBJECT, objectNode);
- end(objectNode);
- return objectNode;
+ ensureSymbol(Type.OBJECT, objectNode);
+ return end(objectNode);
}
+ //TODO is this correct why not leave?
@Override
- public Node enterPropertyNode(final PropertyNode propertyNode) {
+ public boolean enterPropertyNode(final PropertyNode propertyNode) {
// assign a pseudo symbol to property name, see NASHORN-710
+ start(propertyNode);
propertyNode.setSymbol(new Symbol(propertyNode.getKeyName(), 0, Type.OBJECT));
end(propertyNode);
- return propertyNode;
+ return true;
}
@Override
@@ -636,8 +691,10 @@ final class Attr extends NodeOperatorVisitor {
if (expr.getType().isUnknown() && symbol.isParam()) {
symbol.setType(Type.OBJECT);
}
- getCurrentFunctionNode().setReturnType(Type.widest(getCurrentFunctionNode().getReturnType(), symbol.getSymbolType()));
- LOG.info("Returntype is now " + getCurrentFunctionNode().getReturnType());
+
+ final Type returnType = Type.widest(returnTypes.pop(), symbol.getSymbolType());
+ returnTypes.push(returnType);
+ LOG.info("Returntype is now ", returnType);
}
end(returnNode);
@@ -649,25 +706,29 @@ final class Attr extends NodeOperatorVisitor {
public Node leaveSwitchNode(final SwitchNode switchNode) {
Type type = Type.UNKNOWN;
+ final List<CaseNode> newCases = new ArrayList<>();
for (final CaseNode caseNode : switchNode.getCases()) {
final Node test = caseNode.getTest();
+
+ CaseNode newCaseNode = caseNode;
if (test != null) {
if (test instanceof LiteralNode) {
//go down to integers if we can
final LiteralNode<?> lit = (LiteralNode<?>)test;
if (lit.isNumeric() && !(lit.getValue() instanceof Integer)) {
if (JSType.isRepresentableAsInt(lit.getNumber())) {
- caseNode.setTest(LiteralNode.newInstance(lit, lit.getInt32()).accept(this));
+ newCaseNode = caseNode.setTest(LiteralNode.newInstance(lit, lit.getInt32()).accept(this));
}
}
} else {
// the "all integer" case that CodeGenerator optimizes for currently assumes literals only
type = Type.OBJECT;
- break;
}
- type = Type.widest(type, caseNode.getTest().getType());
+ type = Type.widest(type, newCaseNode.getTest().getType());
}
+
+ newCases.add(newCaseNode);
}
//only optimize for all integers
@@ -675,11 +736,11 @@ final class Attr extends NodeOperatorVisitor {
type = Type.OBJECT;
}
- switchNode.setTag(newInternal(getCurrentFunctionNode().uniqueName(SWITCH_TAG_PREFIX.tag()), type));
+ switchNode.setTag(newInternal(getLexicalContext().getCurrentFunction().uniqueName(SWITCH_TAG_PREFIX.symbolName()), type));
end(switchNode);
- return switchNode;
+ return switchNode.setCases(getLexicalContext(), newCases);
}
@Override
@@ -696,25 +757,25 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public Node enterVarNode(final VarNode varNode) {
+ public boolean enterVarNode(final VarNode varNode) {
start(varNode);
final IdentNode ident = varNode.getName();
final String name = ident.getName();
- final Symbol symbol = defineSymbol(getCurrentBlock(), name, IS_VAR, ident);
+ final Symbol symbol = defineSymbol(getLexicalContext().getCurrentBlock(), name, IS_VAR, ident);
assert symbol != null;
- LOG.info("VarNode " + varNode + " set symbol " + symbol);
+ LOG.info("VarNode ", varNode, " set symbol ", symbol);
varNode.setSymbol(symbol);
// NASHORN-467 - use before definition of vars - conservative
- if (localUses.contains(ident.getName())) {
+ if (isLocalUse(ident.getName())) {
newType(symbol, Type.OBJECT);
symbol.setCanBeUndefined();
}
- return varNode;
+ return true;
}
@Override
@@ -734,7 +795,7 @@ final class Attr extends NodeOperatorVisitor {
addLocalDef(name);
final Symbol symbol = varNode.getSymbol();
- final boolean isScript = lexicalContext.getFunction(symbol.getBlock()).isProgram(); //see NASHORN-56
+ final boolean isScript = getLexicalContext().getDefiningFunction(symbol).isProgram(); //see NASHORN-56
if ((init.getType().isNumeric() || init.getType().isBoolean()) && !isScript) {
// Forbid integers as local vars for now as we have no way to treat them as undefined
newType(symbol, init.getType());
@@ -751,14 +812,14 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveADD(final UnaryNode unaryNode) {
- newTemporary(arithType(), unaryNode);
+ ensureSymbol(arithType(), unaryNode);
end(unaryNode);
return unaryNode;
}
@Override
public Node leaveBIT_NOT(final UnaryNode unaryNode) {
- newTemporary(Type.INT, unaryNode);
+ ensureSymbol(Type.INT, unaryNode);
end(unaryNode);
return unaryNode;
}
@@ -766,30 +827,29 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveDECINC(final UnaryNode unaryNode) {
// @see assignOffset
- ensureAssignmentSlots(getCurrentFunctionNode(), unaryNode.rhs());
+ ensureAssignmentSlots(getLexicalContext().getCurrentFunction(), unaryNode.rhs());
final Type type = arithType();
newType(unaryNode.rhs().getSymbol(), type);
- newTemporary(type, unaryNode);
+ ensureSymbol(type, unaryNode);
end(unaryNode);
return unaryNode;
}
@Override
public Node leaveDELETE(final UnaryNode unaryNode) {
- final FunctionNode currentFunctionNode = getCurrentFunctionNode();
- final boolean strictMode = currentFunctionNode.isStrictMode();
+ final FunctionNode currentFunctionNode = getLexicalContext().getCurrentFunction();
+ final boolean strictMode = currentFunctionNode.isStrict();
final Node rhs = unaryNode.rhs();
final Node strictFlagNode = LiteralNode.newInstance(unaryNode, strictMode).accept(this);
Request request = Request.DELETE;
- final RuntimeNode runtimeNode;
final List<Node> args = new ArrayList<>();
if (rhs instanceof IdentNode) {
// If this is a declared variable or a function parameter, delete always fails (except for globals).
final String name = ((IdentNode)rhs).getName();
- final boolean failDelete = strictMode || rhs.getSymbol().isParam() || (rhs.getSymbol().isVar() && !rhs.getSymbol().isTopLevel());
+ final boolean failDelete = strictMode || rhs.getSymbol().isParam() || (rhs.getSymbol().isVar() && !isProgramLevelSymbol(name));
if (failDelete && rhs.getSymbol().isThis()) {
return LiteralNode.newInstance(unaryNode, true).accept(this);
@@ -797,7 +857,7 @@ final class Attr extends NodeOperatorVisitor {
final Node literalNode = LiteralNode.newInstance(unaryNode, name).accept(this);
if (!failDelete) {
- args.add(currentFunctionNode.getScopeNode());
+ args.add(compilerConstant(SCOPE));
}
args.add(literalNode);
args.add(strictFlagNode);
@@ -825,42 +885,62 @@ final class Attr extends NodeOperatorVisitor {
return LiteralNode.newInstance(unaryNode, true).accept(this);
}
- runtimeNode = new RuntimeNode(unaryNode, request, args);
- assert runtimeNode.getSymbol() == unaryNode.getSymbol(); //clone constructor should do this
+ final RuntimeNode runtimeNode = new RuntimeNode(unaryNode, request, args);
+ assert runtimeNode.getSymbol() == unaryNode.getSymbol(); //unary parent constructor should do this
return leaveRuntimeNode(runtimeNode);
}
+ /**
+ * Is the symbol denoted by the specified name in the current lexical context defined in the program level
+ * @param name the name of the symbol
+ * @return true if the symbol denoted by the specified name in the current lexical context defined in the program level.
+ */
+ private boolean isProgramLevelSymbol(final String name) {
+ for(final Iterator<Block> it = getLexicalContext().getBlocks(); it.hasNext();) {
+ final Block next = it.next();
+ if(next.getExistingSymbol(name) != null) {
+ return next == getLexicalContext().getFunctionBody(getLexicalContext().getOutermostFunction());
+ }
+ }
+ throw new AssertionError("Couldn't find symbol " + name + " in the context");
+ }
+
@Override
public Node leaveNEW(final UnaryNode unaryNode) {
- newTemporary(Type.OBJECT, unaryNode);
+ ensureSymbol(Type.OBJECT, unaryNode);
end(unaryNode);
return unaryNode;
}
@Override
public Node leaveNOT(final UnaryNode unaryNode) {
- newTemporary(Type.BOOLEAN, unaryNode);
+ ensureSymbol(Type.BOOLEAN, unaryNode);
end(unaryNode);
return unaryNode;
}
+ private IdentNode compilerConstant(CompilerConstants cc) {
+ final FunctionNode functionNode = getLexicalContext().getCurrentFunction();
+ final IdentNode node = new IdentNode(functionNode.getSource(), functionNode.getToken(), functionNode.getFinish(), cc.symbolName());
+ node.setSymbol(functionNode.compilerConstant(cc));
+ return node;
+ }
+
@Override
public Node leaveTYPEOF(final UnaryNode unaryNode) {
- final Node rhs = unaryNode.rhs();
-
- RuntimeNode runtimeNode;
+ final Node rhs = unaryNode.rhs();
List<Node> args = new ArrayList<>();
if (rhs instanceof IdentNode && !rhs.getSymbol().isParam() && !rhs.getSymbol().isVar()) {
- args.add(getCurrentFunctionNode().getScopeNode());
+ args.add(compilerConstant(SCOPE));
args.add(LiteralNode.newInstance(rhs, ((IdentNode)rhs).getName()).accept(this)); //null
} else {
args.add(rhs);
args.add(LiteralNode.newInstance(unaryNode).accept(this)); //null, do not reuse token of identifier rhs, it can be e.g. 'this'
}
- runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args);
+ RuntimeNode runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args);
assert runtimeNode.getSymbol() == unaryNode.getSymbol();
runtimeNode = (RuntimeNode)leaveRuntimeNode(runtimeNode);
@@ -872,21 +952,20 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveRuntimeNode(final RuntimeNode runtimeNode) {
- newTemporary(runtimeNode.getRequest().getReturnType(), runtimeNode);
+ ensureSymbol(runtimeNode.getRequest().getReturnType(), runtimeNode);
return runtimeNode;
}
@Override
public Node leaveSUB(final UnaryNode unaryNode) {
- newTemporary(arithType(), unaryNode);
+ ensureSymbol(arithType(), unaryNode);
end(unaryNode);
return unaryNode;
}
@Override
public Node leaveVOID(final UnaryNode unaryNode) {
- final RuntimeNode runtimeNode = new RuntimeNode(unaryNode, Request.VOID);
- runtimeNode.accept(this);
+ final RuntimeNode runtimeNode = (RuntimeNode)new RuntimeNode(unaryNode, Request.VOID).accept(this);
assert runtimeNode.getSymbol().getSymbolType().isObject();
end(unaryNode);
return runtimeNode;
@@ -903,7 +982,7 @@ final class Attr extends NodeOperatorVisitor {
ensureTypeNotUnknown(lhs);
ensureTypeNotUnknown(rhs);
- newTemporary(Type.widest(lhs.getType(), rhs.getType()), binaryNode);
+ ensureSymbol(Type.widest(lhs.getType(), rhs.getType()), binaryNode);
end(binaryNode);
@@ -912,7 +991,7 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveAND(final BinaryNode binaryNode) {
- newTemporary(Type.OBJECT, binaryNode);
+ ensureSymbol(Type.OBJECT, binaryNode);
end(binaryNode);
return binaryNode;
}
@@ -921,39 +1000,39 @@ final class Attr extends NodeOperatorVisitor {
* This is a helper called before an assignment.
* @param binaryNode assignment node
*/
- private Node enterAssignmentNode(final BinaryNode binaryNode) {
+ private boolean enterAssignmentNode(final BinaryNode binaryNode) {
start(binaryNode);
final Node lhs = binaryNode.lhs();
if (lhs instanceof IdentNode) {
- final Block block = getCurrentBlock();
+ final Block block = getLexicalContext().getCurrentBlock();
final IdentNode ident = (IdentNode)lhs;
final String name = ident.getName();
- Symbol symbol = findSymbol(getCurrentBlock(), name);
+ Symbol symbol = findSymbol(block, name);
if (symbol == null) {
symbol = defineSymbol(block, name, IS_GLOBAL, ident);
binaryNode.setSymbol(symbol);
- } else if (!isLocal(getCurrentFunctionNode(), symbol)) {
- symbol.setIsScope();
+ } else {
+ maybeForceScope(symbol);
}
addLocalDef(name);
}
- return binaryNode;
+ return true;
}
private boolean isLocal(FunctionNode function, Symbol symbol) {
- final Block block = symbol.getBlock();
- // some temp symbols have no block, so can be assumed local
- return block == null || lexicalContext.getFunction(block) == function;
+ final FunctionNode definingFn = getLexicalContext().getDefiningFunction(symbol);
+ // Temp symbols are not assigned to a block, so their defining fn is null; those can be assumed local
+ return definingFn == null || definingFn == function;
}
@Override
- public Node enterASSIGN(final BinaryNode binaryNode) {
+ public boolean enterASSIGN(final BinaryNode binaryNode) {
return enterAssignmentNode(binaryNode);
}
@@ -963,7 +1042,7 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public Node enterASSIGN_ADD(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_ADD(final BinaryNode binaryNode) {
return enterAssignmentNode(binaryNode);
}
@@ -978,7 +1057,7 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public Node enterASSIGN_BIT_AND(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_BIT_AND(final BinaryNode binaryNode) {
return enterAssignmentNode(binaryNode);
}
@@ -988,7 +1067,7 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public Node enterASSIGN_BIT_OR(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_BIT_OR(final BinaryNode binaryNode) {
return enterAssignmentNode(binaryNode);
}
@@ -998,7 +1077,7 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public Node enterASSIGN_BIT_XOR(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_BIT_XOR(final BinaryNode binaryNode) {
return enterAssignmentNode(binaryNode);
}
@@ -1008,7 +1087,7 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public Node enterASSIGN_DIV(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_DIV(final BinaryNode binaryNode) {
return enterAssignmentNode(binaryNode);
}
@@ -1018,7 +1097,7 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public Node enterASSIGN_MOD(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_MOD(final BinaryNode binaryNode) {
return enterAssignmentNode(binaryNode);
}
@@ -1028,7 +1107,7 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public Node enterASSIGN_MUL(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_MUL(final BinaryNode binaryNode) {
return enterAssignmentNode(binaryNode);
}
@@ -1038,7 +1117,7 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public Node enterASSIGN_SAR(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_SAR(final BinaryNode binaryNode) {
return enterAssignmentNode(binaryNode);
}
@@ -1048,7 +1127,7 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public Node enterASSIGN_SHL(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_SHL(final BinaryNode binaryNode) {
return enterAssignmentNode(binaryNode);
}
@@ -1058,7 +1137,7 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public Node enterASSIGN_SHR(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_SHR(final BinaryNode binaryNode) {
return enterAssignmentNode(binaryNode);
}
@@ -1068,7 +1147,7 @@ final class Attr extends NodeOperatorVisitor {
}
@Override
- public Node enterASSIGN_SUB(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_SUB(final BinaryNode binaryNode) {
return enterAssignmentNode(binaryNode);
}
@@ -1094,13 +1173,13 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveCOMMARIGHT(final BinaryNode binaryNode) {
- newTemporary(binaryNode.rhs().getType(), binaryNode);
+ ensureSymbol(binaryNode.rhs().getType(), binaryNode);
return binaryNode;
}
@Override
public Node leaveCOMMALEFT(final BinaryNode binaryNode) {
- newTemporary(binaryNode.lhs().getType(), binaryNode);
+ ensureSymbol(binaryNode.lhs().getType(), binaryNode);
return binaryNode;
}
@@ -1113,7 +1192,7 @@ final class Attr extends NodeOperatorVisitor {
final Node lhs = binaryNode.lhs();
final Node rhs = binaryNode.rhs();
- newTemporary(Type.BOOLEAN, binaryNode);
+ ensureSymbol(Type.BOOLEAN, binaryNode);
ensureTypeNotUnknown(lhs);
ensureTypeNotUnknown(rhs);
@@ -1131,7 +1210,7 @@ final class Attr extends NodeOperatorVisitor {
//newType(binaryNode.lhs().getSymbol(), operandType);
//newType(binaryNode.rhs().getSymbol(), operandType);
- newTemporary(destType, binaryNode);
+ ensureSymbol(destType, binaryNode);
return binaryNode;
}
@@ -1216,7 +1295,7 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveOR(final BinaryNode binaryNode) {
- newTemporary(Type.OBJECT, binaryNode);
+ ensureSymbol(Type.OBJECT, binaryNode);
end(binaryNode);
return binaryNode;
}
@@ -1244,7 +1323,7 @@ final class Attr extends NodeOperatorVisitor {
@Override
public Node leaveForNode(final ForNode forNode) {
if (forNode.isForIn()) {
- forNode.setIterator(newInternal(getCurrentFunctionNode().uniqueName(ITERATOR_PREFIX.tag()), Type.OBJECT)); //NASHORN-73
+ forNode.setIterator(newInternal(getLexicalContext().getCurrentFunction().uniqueName(ITERATOR_PREFIX.symbolName()), Type.OBJECT)); //NASHORN-73
/*
* Iterators return objects, so we need to widen the scope of the
* init variable if it, for example, has been assigned double type
@@ -1267,65 +1346,50 @@ final class Attr extends NodeOperatorVisitor {
ensureTypeNotUnknown(rhs);
final Type type = Type.widest(lhs.getType(), rhs.getType());
- newTemporary(type, ternaryNode);
+ ensureSymbol(type, ternaryNode);
end(ternaryNode);
+ assert ternaryNode.getSymbol() != null;
return ternaryNode;
}
- private void initThis(final FunctionNode functionNode) {
- final Symbol thisSymbol = defineSymbol(functionNode, THIS.tag(), IS_PARAM | IS_THIS, null);
+ private void initThis(final Block block) {
+ final Symbol thisSymbol = defineSymbol(block, THIS.symbolName(), IS_PARAM | IS_THIS, null);
newType(thisSymbol, Type.OBJECT);
thisSymbol.setNeedsSlot(true);
- functionNode.getThisNode().setSymbol(thisSymbol);
- LOG.info("Initialized scope symbol: " + thisSymbol);
}
- private void initScope(final FunctionNode functionNode) {
- final Symbol scopeSymbol = defineSymbol(functionNode, SCOPE.tag(), IS_VAR | IS_INTERNAL, null);
+ private void initScope(final Block block) {
+ final Symbol scopeSymbol = defineSymbol(block, SCOPE.symbolName(), IS_VAR | IS_INTERNAL, null);
newType(scopeSymbol, Type.typeFor(ScriptObject.class));
scopeSymbol.setNeedsSlot(true);
- functionNode.getScopeNode().setSymbol(scopeSymbol);
- LOG.info("Initialized scope symbol: " + scopeSymbol);
}
- private void initReturn(final FunctionNode functionNode) {
- final Symbol returnSymbol = defineSymbol(functionNode, SCRIPT_RETURN.tag(), IS_VAR | IS_INTERNAL, null);
+ private void initReturn(final Block block) {
+ final Symbol returnSymbol = defineSymbol(block, RETURN.symbolName(), IS_VAR | IS_INTERNAL, null);
newType(returnSymbol, Type.OBJECT);
returnSymbol.setNeedsSlot(true);
- functionNode.getResultNode().setSymbol(returnSymbol);
- LOG.info("Initialized return symbol: " + returnSymbol);
//return symbol is always object as it's the __return__ thing. What returnType is is another matter though
}
- private void initVarArg(final FunctionNode functionNode) {
- if (functionNode.isVarArg()) {
- final Symbol varArgsSymbol = defineSymbol(functionNode, VARARGS.tag(), IS_PARAM | IS_INTERNAL, null);
- varArgsSymbol.setTypeOverride(Type.OBJECT_ARRAY);
- varArgsSymbol.setNeedsSlot(true);
- functionNode.getVarArgsNode().setSymbol(varArgsSymbol);
- LOG.info("Initialized varargs symbol: " + varArgsSymbol);
-
- if (functionNode.needsArguments()) {
- final String argumentsName = functionNode.getArgumentsNode().getName();
- final Symbol argumentsSymbol = defineSymbol(functionNode, argumentsName, IS_VAR | IS_INTERNAL, null);
- newType(argumentsSymbol, Type.typeFor(ScriptObject.class));
- argumentsSymbol.setNeedsSlot(true);
- functionNode.getArgumentsNode().setSymbol(argumentsSymbol);
- addLocalDef(argumentsName);
- LOG.info("Initialized vararg varArgsSymbol=" + varArgsSymbol + " argumentsSymbol=" + argumentsSymbol);
- }
+ private void initVarArg(final Block block, final boolean needsArguments) {
+ final Symbol varArgsSymbol = defineSymbol(block, VARARGS.symbolName(), IS_PARAM | IS_INTERNAL, null);
+ varArgsSymbol.setTypeOverride(Type.OBJECT_ARRAY);
+ varArgsSymbol.setNeedsSlot(true);
+
+ if (needsArguments) {
+ final Symbol argumentsSymbol = defineSymbol(block, ARGUMENTS.symbolName(), IS_VAR | IS_INTERNAL, null);
+ newType(argumentsSymbol, Type.typeFor(ScriptObject.class));
+ argumentsSymbol.setNeedsSlot(true);
+ addLocalDef(ARGUMENTS.symbolName());
}
}
- private void initCallee(final FunctionNode functionNode) {
- assert functionNode.getCalleeNode() != null : functionNode + " has no callee";
- final Symbol calleeSymbol = defineSymbol(functionNode, CALLEE.tag(), IS_PARAM | IS_INTERNAL, null);
- newType(calleeSymbol, Type.typeFor(ScriptFunction.class));
+ private void initCallee(final Block block) {
+ final Symbol calleeSymbol = defineSymbol(block, CALLEE.symbolName(), IS_PARAM | IS_INTERNAL, null);
+ newType(calleeSymbol, FunctionNode.FUNCTION_TYPE);
calleeSymbol.setNeedsSlot(true);
- functionNode.getCalleeNode().setSymbol(calleeSymbol);
- LOG.info("Initialized callee symbol " + calleeSymbol);
}
/**
@@ -1334,25 +1398,19 @@ final class Attr extends NodeOperatorVisitor {
*
* @param functionNode the function node
*/
- private void initParameters(final FunctionNode functionNode) {
- //If a function is specialized, we don't need to tag either it return
- // type or its parameters with the widest (OBJECT) type for safety.
- functionNode.setReturnType(Type.UNKNOWN);
-
+ private void initParameters(final FunctionNode functionNode, final Block body) {
for (final IdentNode param : functionNode.getParameters()) {
addLocalDef(param.getName());
- final Symbol paramSymbol = defineSymbol(functionNode, param.getName(), IS_PARAM, param);
+ final Symbol paramSymbol = defineSymbol(body, param.getName(), IS_PARAM, param);
if (paramSymbol != null) {
final Type callSiteParamType = functionNode.getSpecializedType(param);
if (callSiteParamType != null) {
- LOG.info("Param " + paramSymbol + " has a callsite type " + callSiteParamType + ". Using that.");
-
- System.err.println("Param " + param + " has a callsite type " + callSiteParamType + ". Using that.");
+ LOG.info("Param ", paramSymbol, " has a callsite type ", callSiteParamType, ". Using that.");
}
newType(paramSymbol, callSiteParamType == null ? Type.UNKNOWN : callSiteParamType);
}
- LOG.info("Initialized param " + paramSymbol);
+ LOG.info("Initialized param ", paramSymbol);
}
}
@@ -1378,7 +1436,7 @@ final class Attr extends NodeOperatorVisitor {
// this function, we can tell the runtime system that no matter what the
// call site is, use this information. TODO
if (!paramSymbol.getSymbolType().isObject()) {
- LOG.finest("Parameter " + ident + " could profit from specialization to " + paramSymbol.getSymbolType());
+ LOG.finest("Parameter ", ident, " could profit from specialization to ", paramSymbol.getSymbolType());
}
newType(paramSymbol, Type.widest(type, paramSymbol.getSymbolType()));
@@ -1392,19 +1450,18 @@ final class Attr extends NodeOperatorVisitor {
/**
* Move any properties from a global map into the scope of this method
- * @param functionNode the function node for which to init scope vars
+ * @param block the function node body for which to init scope vars
*/
- private void initFromPropertyMap(final FunctionNode functionNode) {
+ private void initFromPropertyMap(final Block block) {
// For a script, add scope symbols as defined in the property map
- assert functionNode.isProgram();
final PropertyMap map = Context.getGlobalMap();
for (final Property property : map.getProperties()) {
final String key = property.getKey();
- final Symbol symbol = defineSymbol(functionNode, key, IS_GLOBAL, null);
+ final Symbol symbol = defineSymbol(block, key, IS_GLOBAL, null);
newType(symbol, Type.OBJECT);
- LOG.info("Added global symbol from property map " + symbol);
+ LOG.info("Added global symbol from property map ", symbol);
}
}
@@ -1412,7 +1469,7 @@ final class Attr extends NodeOperatorVisitor {
final Symbol symbol = node.getSymbol();
- LOG.info("Ensure type not unknown for: " + symbol);
+ LOG.info("Ensure type not unknown for: ", symbol);
/*
* Note that not just unknowns, but params need to be blown
@@ -1452,7 +1509,7 @@ final class Attr extends NodeOperatorVisitor {
}
private Symbol exceptionSymbol() {
- return newInternal(getCurrentFunctionNode().uniqueName(EXCEPTION_PREFIX.tag()), Type.typeFor(ECMAException.class));
+ return newInternal(getLexicalContext().getCurrentFunction().uniqueName(EXCEPTION_PREFIX.symbolName()), Type.typeFor(ECMAException.class));
}
/**
@@ -1512,15 +1569,15 @@ final class Attr extends NodeOperatorVisitor {
}
Type from = node.getType();
if (!Type.areEquivalent(from, to) && Type.widest(from, to) == to) {
- LOG.fine("Had to post pass widen '" + node + "' " + Debug.id(node) + " from " + node.getType() + " to " + to);
+ LOG.fine("Had to post pass widen '", node, "' " + Debug.id(node), " from ", node.getType(), " to ", to);
newType(node.getSymbol(), to);
changed.add(node);
}
}
@Override
- public Node enterFunctionNode(final FunctionNode node) {
- return node.isLazy() ? null : node;
+ public boolean enterFunctionNode(final FunctionNode node) {
+ return !node.isLazy();
}
/**
@@ -1574,7 +1631,7 @@ final class Attr extends NodeOperatorVisitor {
} else {
type = Type.OBJECT; //force lhs to be an object if not numeric assignment, e.g. strings too.
}
- newTemporary(type, binaryNode);
+ ensureSymbol(type, binaryNode);
newType(lhs.getSymbol(), type);
end(binaryNode);
return binaryNode;
@@ -1589,32 +1646,25 @@ final class Attr extends NodeOperatorVisitor {
final Node lhs = binaryNode.lhs();
newType(lhs.getSymbol(), destType); //may not narrow if dest is already wider than destType
- newTemporary(destType, binaryNode); //for OP= nodes, the node can carry a narrower types than its lhs rhs. This is perfectly fine
+ ensureSymbol(destType, binaryNode); //for OP= nodes, the node can carry a narrower types than its lhs rhs. This is perfectly fine
- ensureAssignmentSlots(getCurrentFunctionNode(), binaryNode);
+ ensureAssignmentSlots(getLexicalContext().getCurrentFunction(), binaryNode);
end(binaryNode);
return binaryNode;
}
- private static boolean isFunctionExpressionSelfReference(final Symbol symbol) {
- if (symbol.isVar() && symbol.getNode() == symbol.getBlock() && symbol.getNode() instanceof FunctionNode) {
- return ((FunctionNode)symbol.getNode()).getIdent().getName().equals(symbol.getName());
- }
- return false;
- }
-
- private static Symbol newTemporary(final FunctionNode functionNode, final Type type, final Node node) {
- LOG.info("New TEMPORARY added to " + functionNode.getName() + " type=" + type);
- return functionNode.newTemporary(type, node);
+ private Symbol ensureSymbol(final FunctionNode functionNode, final Type type, final Node node) {
+ LOG.info("New TEMPORARY added to ", functionNode.getName(), " type=", type);
+ return functionNode.ensureSymbol(getLexicalContext().getCurrentBlock(), type, node);
}
- private Symbol newTemporary(final Type type, final Node node) {
- return newTemporary(getCurrentFunctionNode(), type, node);
+ private Symbol ensureSymbol(final Type type, final Node node) {
+ return ensureSymbol(getLexicalContext().getCurrentFunction(), type, node);
}
private Symbol newInternal(final String name, final Type type) {
- final Symbol iter = defineSymbol(getCurrentFunctionNode(), name, IS_VAR | IS_INTERNAL, null);
+ final Symbol iter = defineSymbol(getLexicalContext().getCurrentBlock(), name, IS_VAR | IS_INTERNAL, null);
iter.setType(type); // NASHORN-73
return iter;
}
@@ -1624,40 +1674,51 @@ final class Attr extends NodeOperatorVisitor {
symbol.setType(type);
if (symbol.getSymbolType() != oldType) {
- LOG.info("New TYPE " + type + " for " + symbol + " (was " + oldType + ")");
+ LOG.info("New TYPE ", type, " for ", symbol," (was ", oldType, ")");
}
if (symbol.isParam()) {
symbol.setType(type);
- LOG.info("Param type change " + symbol);
+ LOG.info("Param type change ", symbol);
}
}
- private void clearLocalDefs() {
- localDefs = new HashSet<>();
+ private void pushLocalsFunction() {
+ localDefs.push(new HashSet<String>());
+ localUses.push(new HashSet<String>());
+ }
+
+ private void pushLocalsBlock() {
+ localDefs.push(localDefs.isEmpty() ? new HashSet<String>() : new HashSet<>(localDefs.peek()));
+ localUses.push(localUses.isEmpty() ? new HashSet<String>() : new HashSet<>(localUses.peek()));
+ }
+
+ private void popLocals() {
+ localDefs.pop();
+ localUses.pop();
}
private boolean isLocalDef(final String name) {
- return localDefs.contains(name);
+ return localDefs.peek().contains(name);
}
private void addLocalDef(final String name) {
- LOG.info("Adding local def of symbol: '" + name + "'");
- localDefs.add(name);
+ LOG.info("Adding local def of symbol: '", name, "'");
+ localDefs.peek().add(name);
}
private void removeLocalDef(final String name) {
- LOG.info("Removing local def of symbol: '" + name + "'");
- localDefs.remove(name);
+ LOG.info("Removing local def of symbol: '", name, "'");
+ localDefs.peek().remove(name);
}
- private void clearLocalUses() {
- localUses = new HashSet<>();
+ private boolean isLocalUse(final String name) {
+ return localUses.peek().contains(name);
}
private void addLocalUse(final String name) {
- LOG.info("Adding local use of symbol: '" + name + "'");
- localUses.add(name);
+ LOG.info("Adding local use of symbol: '", name, "'");
+ localUses.peek().add(name);
}
/**
@@ -1665,30 +1726,28 @@ final class Attr extends NodeOperatorVisitor {
* This is done when the function contains unevaluated black boxes such as
* lazy sub-function nodes that have not been compiled.
*
- * @param functionNode function node in whose scope symbols should conservatively be made objects
+ * @param body body for the function node we are leaving
*/
- private static void objectifySymbols(final FunctionNode functionNode) {
- functionNode.accept(new NodeVisitor() {
+ private static void objectifySymbols(final Block body) {
+ body.accept(new NodeVisitor() {
private void toObject(final Block block) {
for (final Iterator<Symbol> iter = block.symbolIterator(); iter.hasNext();) {
final Symbol symbol = iter.next();
- newType(symbol, Type.OBJECT);
+ if (!symbol.isTemp()) {
+ newType(symbol, Type.OBJECT);
+ }
}
}
@Override
- public Node enterBlock(final Block block) {
+ public boolean enterBlock(final Block block) {
toObject(block);
- return block;
+ return true;
}
@Override
- public Node enterFunctionNode(final FunctionNode node) {
- toObject(node);
- if (node.isLazy()) {
- return null;
- }
- return node;
+ public boolean enterFunctionNode(final FunctionNode node) {
+ return false;
}
});
}
@@ -1702,11 +1761,11 @@ final class Attr extends NodeOperatorVisitor {
return cn.substring(lastDot + 1);
}
- private Node start(final Node node) {
+ private boolean start(final Node node) {
return start(node, true);
}
- private Node start(final Node node, final boolean printNode) {
+ private boolean start(final Node node, final boolean printNode) {
if (DEBUG) {
final StringBuilder sb = new StringBuilder();
@@ -1715,13 +1774,13 @@ final class Attr extends NodeOperatorVisitor {
append("] ").
append(printNode ? node.toString() : "").
append(" in '").
- append(getCurrentFunctionNode().getName()).
+ append(getLexicalContext().getCurrentFunction().getName()).
append("'");
- LOG.info(sb.toString());
+ LOG.info(sb);
LOG.indent();
}
- return node;
+ return true;
}
private Node end(final Node node) {
@@ -1737,7 +1796,7 @@ final class Attr extends NodeOperatorVisitor {
append("] ").
append(printNode ? node.toString() : "").
append(" in '").
- append(getCurrentFunctionNode().getName());
+ append(getLexicalContext().getCurrentFunction().getName());
if (node.getSymbol() == null) {
sb.append(" <NO SYMBOL>");
@@ -1746,7 +1805,7 @@ final class Attr extends NodeOperatorVisitor {
}
LOG.unindent();
- LOG.info(sb.toString());
+ LOG.info(sb);
}
return node;
diff --git a/src/jdk/nashorn/internal/codegen/ClassEmitter.java b/src/jdk/nashorn/internal/codegen/ClassEmitter.java
index 7ca7f994..2d379183 100644
--- a/src/jdk/nashorn/internal/codegen/ClassEmitter.java
+++ b/src/jdk/nashorn/internal/codegen/ClassEmitter.java
@@ -58,12 +58,14 @@ import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;
+
import jdk.internal.org.objectweb.asm.ClassReader;
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.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptEnvironment;
import jdk.nashorn.internal.runtime.ScriptObject;
@@ -219,14 +221,14 @@ public class ClassEmitter implements Emitter {
private void defineCommonStatics(final boolean strictMode) {
// source - used to store the source data (text) for this script. Shared across
// compile units. Set externally by the compiler.
- field(EnumSet.of(Flag.PRIVATE, Flag.STATIC), SOURCE.tag(), Source.class);
+ field(EnumSet.of(Flag.PRIVATE, Flag.STATIC), SOURCE.symbolName(), Source.class);
// constants - used to the constants array for this script. Shared across
// compile units. Set externally by the compiler.
- field(EnumSet.of(Flag.PRIVATE, Flag.STATIC), CONSTANTS.tag(), Object[].class);
+ field(EnumSet.of(Flag.PRIVATE, Flag.STATIC), CONSTANTS.symbolName(), Object[].class);
// strictMode - was this script compiled in strict mode. Set externally by the compiler.
- field(EnumSet.of(Flag.PUBLIC, Flag.STATIC, Flag.FINAL), STRICT_MODE.tag(), boolean.class, strictMode);
+ field(EnumSet.of(Flag.PUBLIC, Flag.STATIC, Flag.FINAL), STRICT_MODE.symbolName(), boolean.class, strictMode);
}
/**
@@ -238,9 +240,9 @@ public class ClassEmitter implements Emitter {
if (constantMethodNeeded.contains(String.class)) {
// $getString - get the ith entry from the constants table and cast to String.
- final MethodEmitter getStringMethod = method(EnumSet.of(Flag.PRIVATE, Flag.STATIC), GET_STRING.tag(), String.class, int.class);
+ final MethodEmitter getStringMethod = method(EnumSet.of(Flag.PRIVATE, Flag.STATIC), GET_STRING.symbolName(), String.class, int.class);
getStringMethod.begin();
- getStringMethod.getStatic(unitClassName, CONSTANTS.tag(), CONSTANTS.descriptor())
+ getStringMethod.getStatic(unitClassName, CONSTANTS.symbolName(), CONSTANTS.descriptor())
.load(Type.INT, 0)
.arrayload()
.checkcast(String.class)
@@ -250,7 +252,7 @@ public class ClassEmitter implements Emitter {
if (constantMethodNeeded.contains(PropertyMap.class)) {
// $getMap - get the ith entry from the constants table and cast to PropertyMap.
- final MethodEmitter getMapMethod = method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), GET_MAP.tag(), PropertyMap.class, int.class);
+ final MethodEmitter getMapMethod = method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), GET_MAP.symbolName(), PropertyMap.class, int.class);
getMapMethod.begin();
getMapMethod.loadConstants()
.load(Type.INT, 0)
@@ -260,7 +262,7 @@ public class ClassEmitter implements Emitter {
getMapMethod.end();
// $setMap - overwrite an existing map.
- final MethodEmitter setMapMethod = method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), SET_MAP.tag(), void.class, int.class, PropertyMap.class);
+ final MethodEmitter setMapMethod = method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), SET_MAP.symbolName(), void.class, int.class, PropertyMap.class);
setMapMethod.begin();
setMapMethod.loadConstants()
.load(Type.INT, 0)
@@ -289,7 +291,7 @@ public class ClassEmitter implements Emitter {
final MethodEmitter getArrayMethod = method(EnumSet.of(Flag.PRIVATE, Flag.STATIC), methodName, cls, int.class);
getArrayMethod.begin();
- getArrayMethod.getStatic(unitClassName, CONSTANTS.tag(), CONSTANTS.descriptor())
+ getArrayMethod.getStatic(unitClassName, CONSTANTS.symbolName(), CONSTANTS.descriptor())
.load(Type.INT, 0)
.arrayload()
.checkcast(cls)
@@ -307,7 +309,7 @@ public class ClassEmitter implements Emitter {
*/
static String getArrayMethodName(final Class<?> cls) {
assert cls.isArray();
- return GET_ARRAY_PREFIX.tag() + cls.getComponentType().getSimpleName() + GET_ARRAY_SUFFIX.tag();
+ return GET_ARRAY_PREFIX.symbolName() + cls.getComponentType().getSimpleName() + GET_ARRAY_SUFFIX.symbolName();
}
/**
@@ -409,6 +411,10 @@ public class ClassEmitter implements Emitter {
methodsStarted.remove(method);
}
+ SplitMethodEmitter method(final SplitNode splitNode, final String methodName, final Class<?> rtype, final Class<?>... ptypes) {
+ 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
*
@@ -433,7 +439,7 @@ public class ClassEmitter implements Emitter {
* @return method emitter to use for weaving this method
*/
MethodEmitter method(final EnumSet<Flag> methodFlags, final String methodName, final Class<?> rtype, final Class<?>... ptypes) {
- return new MethodEmitter(this, cw.visitMethod(Flag.getValue(methodFlags), methodName, methodDescriptor(rtype, ptypes), null, null));
+ return new MethodEmitter(this, methodVisitor(methodFlags, methodName, rtype, ptypes));
}
/**
@@ -484,7 +490,7 @@ public class ClassEmitter implements Emitter {
* @return method emitter to use for weaving <clinit>
*/
MethodEmitter clinit() {
- return method(EnumSet.of(Flag.STATIC), CLINIT.tag(), void.class);
+ return method(EnumSet.of(Flag.STATIC), CLINIT.symbolName(), void.class);
}
/**
@@ -493,7 +499,7 @@ public class ClassEmitter implements Emitter {
* @return method emitter to use for weaving <init>()V
*/
MethodEmitter init() {
- return method(INIT.tag(), void.class);
+ return method(INIT.symbolName(), void.class);
}
/**
@@ -503,7 +509,7 @@ public class ClassEmitter implements Emitter {
* @return method emitter to use for weaving <init>()V
*/
MethodEmitter init(final Class<?>... ptypes) {
- return method(INIT.tag(), void.class, ptypes);
+ return method(INIT.symbolName(), void.class, ptypes);
}
/**
@@ -515,7 +521,7 @@ public class ClassEmitter implements Emitter {
* @return method emitter to use for weaving <init>(...)V
*/
MethodEmitter init(final EnumSet<Flag> flags, final Class<?>... ptypes) {
- return method(flags, INIT.tag(), void.class, ptypes);
+ return method(flags, INIT.symbolName(), void.class, ptypes);
}
/**
@@ -628,4 +634,9 @@ public class ClassEmitter implements Emitter {
return v;
}
}
+
+ private MethodVisitor methodVisitor(EnumSet<Flag> flags, final String methodName, final Class<?> rtype, final Class<?>... ptypes) {
+ return cw.visitMethod(Flag.getValue(flags), methodName, methodDescriptor(rtype, ptypes), null, null);
+ }
+
}
diff --git a/src/jdk/nashorn/internal/codegen/CodeGenerator.java b/src/jdk/nashorn/internal/codegen/CodeGenerator.java
index 1d09e9c9..5d4b8f98 100644
--- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java
+++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java
@@ -27,14 +27,18 @@ package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.codegen.ClassEmitter.Flag.PRIVATE;
import static jdk.nashorn.internal.codegen.ClassEmitter.Flag.STATIC;
+import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
+import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
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.LEAF;
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;
import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
import static jdk.nashorn.internal.codegen.CompilerConstants.interfaceCallNoLookup;
import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor;
@@ -48,8 +52,10 @@ import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALL
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_STRICT;
import java.io.PrintWriter;
+import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Deque;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Iterator;
@@ -67,11 +73,11 @@ import jdk.nashorn.internal.ir.BaseNode;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.BreakNode;
+import jdk.nashorn.internal.ir.BreakableNode;
import jdk.nashorn.internal.ir.CallNode;
import jdk.nashorn.internal.ir.CaseNode;
import jdk.nashorn.internal.ir.CatchNode;
import jdk.nashorn.internal.ir.ContinueNode;
-import jdk.nashorn.internal.ir.DoWhileNode;
import jdk.nashorn.internal.ir.EmptyNode;
import jdk.nashorn.internal.ir.ExecuteNode;
import jdk.nashorn.internal.ir.ForNode;
@@ -81,10 +87,12 @@ import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.IfNode;
import jdk.nashorn.internal.ir.IndexNode;
import jdk.nashorn.internal.ir.LexicalContext;
+import jdk.nashorn.internal.ir.LexicalContextNode;
import jdk.nashorn.internal.ir.LineNumberNode;
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.LoopNode;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.ObjectNode;
import jdk.nashorn.internal.ir.PropertyNode;
@@ -107,8 +115,10 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.Lexer.RegexToken;
import jdk.nashorn.internal.parser.TokenType;
import jdk.nashorn.internal.runtime.Context;
+import jdk.nashorn.internal.runtime.Debug;
import jdk.nashorn.internal.runtime.DebugLogger;
import jdk.nashorn.internal.runtime.ECMAException;
+import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.Property;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
@@ -157,14 +167,29 @@ final class CodeGenerator extends NodeOperatorVisitor {
/** How many regexp fields have been emitted */
private int regexFieldCount;
- /** Used for temporary signaling between enterCallNode and enterFunctionNode to handle the special case of calling
- * a just-defined anonymous function expression. */
- private boolean functionNodeIsCallee;
-
/** Map of shared scope call sites */
private final Map<SharedScopeCall, SharedScopeCall> scopeCalls = new HashMap<>();
- private final LexicalContext lexicalContext = new LexicalContext();
+ /** Compile unit stack - every time we start a sub method (e.g. a split) we push one */
+ private final Deque<CompileUnit> compileUnits = new ArrayDeque<>();
+
+ /** Method emitter stack - every time we start a sub method (e.g. a split) we push one */
+ private final Deque<MethodEmitter> methodEmitters = new ArrayDeque<>();
+
+ /** The discard stack - whenever we enter a discard node we keep track of its return value status -
+ * i.e. should we keep it or throw it away */
+ private final Deque<Node> discard = new ArrayDeque<>();
+
+ // A stack tracking the next free local variable slot in the blocks. There's one entry for every block
+ // currently on the lexical context stack.
+ private int[] nextFreeSlots = new int[16];
+ private int nextFreeSlotsSize = 0;
+
+ /** Current method emitter */
+ private MethodEmitter method;
+
+ /** Current compile unit */
+ private CompileUnit unit;
/** When should we stop caching regexp expressions in fields to limit bytecode size? */
private static final int MAX_REGEX_FIELDS = 2 * 1024;
@@ -177,6 +202,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
* @param compiler
*/
CodeGenerator(final Compiler compiler) {
+ super(new DynamicScopeTrackingLexicalContext());
this.compiler = compiler;
this.callSiteFlags = compiler.getEnv()._callsite_flags;
}
@@ -188,7 +214,37 @@ final class CodeGenerator extends NodeOperatorVisitor {
* @return the correct flags for a call site in the current function
*/
int getCallSiteFlags() {
- return getCurrentFunctionNode().isStrictMode() ? callSiteFlags | CALLSITE_STRICT : callSiteFlags;
+ return getLexicalContext().getCurrentFunction().isStrict() ? callSiteFlags | CALLSITE_STRICT : callSiteFlags;
+ }
+
+ private void pushMethodEmitter(final MethodEmitter newMethod) {
+ methodEmitters.push(newMethod);
+ this.method = newMethod;
+ }
+
+ private void popMethodEmitter(final MethodEmitter oldMethod) {
+ assert methodEmitters.peek() == oldMethod;
+ methodEmitters.pop();
+ if (!methodEmitters.isEmpty()) {
+ this.method = methodEmitters.peek();
+ } else {
+ this.method = null;
+ }
+ }
+
+ private void push(final CompileUnit newUnit) {
+ compileUnits.push(newUnit);
+ this.unit = newUnit;
+ }
+
+ private void pop(final CompileUnit oldUnit) {
+ assert compileUnits.peek() == oldUnit;
+ compileUnits.pop();
+ if (!compileUnits.isEmpty()) {
+ this.unit = compileUnits.peek();
+ } else {
+ this.unit = null;
+ }
}
/**
@@ -217,7 +273,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
assert identNode.getSymbol().isScope() : identNode + " is not in scope!";
final int flags = CALLSITE_SCOPE | getCallSiteFlags();
- method.loadScope();
+ method.loadCompilerConstant(SCOPE);
if (isFastScope(symbol)) {
// Only generate shared scope getter for fast-scope symbols so we know we can dial in correct scope.
@@ -231,27 +287,103 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
/**
+ * A lexical context that also tracks if we have any dynamic scopes in the context. Such scopes can have new
+ * variables introduced into them at run time - a with block or a function directly containing an eval call.
+ */
+ private static class DynamicScopeTrackingLexicalContext extends LexicalContext {
+ int dynamicScopeCount = 0;
+
+ @Override
+ public <T extends LexicalContextNode> T push(T node) {
+ if(isDynamicScopeBoundary(node)) {
+ ++dynamicScopeCount;
+ }
+ return super.push(node);
+ }
+
+ @Override
+ public <T extends LexicalContextNode> T pop(T node) {
+ final T popped = super.pop(node);
+ if(isDynamicScopeBoundary(popped)) {
+ --dynamicScopeCount;
+ }
+ return popped;
+ }
+
+ private boolean isDynamicScopeBoundary(LexicalContextNode node) {
+ if(node instanceof Block) {
+ // Block's immediate parent is a with node. Note we aren't testing for a WithNode, as that'd capture
+ // processing of WithNode.expression too, but it should be unaffected.
+ return !isEmpty() && peek() instanceof WithNode;
+ } else if(node instanceof FunctionNode) {
+ // Function has a direct eval in it (so a top-level "var ..." in the eval code can introduce a new
+ // variable into the function's scope), and it isn't strict (as evals in strict functions get an
+ // isolated scope).
+ return isFunctionDynamicScope((FunctionNode)node);
+ }
+ return false;
+ }
+ }
+
+ boolean inDynamicScope() {
+ return ((DynamicScopeTrackingLexicalContext)getLexicalContext()).dynamicScopeCount > 0;
+ }
+
+ static boolean isFunctionDynamicScope(FunctionNode fn) {
+ return fn.hasEval() && !fn.isStrict();
+ }
+
+ /**
* Check if this symbol can be accessed directly with a putfield or getfield or dynamic load
*
* @param function function to check for fast scope
* @return true if fast scope
*/
private boolean isFastScope(final Symbol symbol) {
- if (!symbol.isScope() || !symbol.getBlock().needsScope()) {
+ if(!symbol.isScope()) {
return false;
}
- // Allow fast scope access if no function contains with or eval
- for(final Iterator<FunctionNode> it = lexicalContext.getFunctions(getCurrentFunctionNode()); it.hasNext();) {
- final FunctionNode func = it.next();
- if (func.hasWith() || func.hasEval()) {
- return false;
+ final LexicalContext lc = getLexicalContext();
+ if(!inDynamicScope()) {
+ // If there's no with or eval in context, and the symbol is marked as scoped, it is fast scoped. Such a
+ // symbol must either be global, or its defining block must need scope.
+ assert symbol.isGlobal() || lc.getDefiningBlock(symbol).needsScope() : symbol.getName();
+ return true;
+ }
+ if(symbol.isGlobal()) {
+ // Shortcut: if there's a with or eval in context, globals can't be fast scoped
+ return false;
+ }
+ // Otherwise, check if there's a dynamic scope between use of the symbol and its definition
+ final String name = symbol.getName();
+ boolean previousWasBlock = false;
+ for (final Iterator<LexicalContextNode> it = lc.getAllNodes(); it.hasNext();) {
+ final LexicalContextNode node = it.next();
+ if(node instanceof Block) {
+ // If this block defines the symbol, then we can fast scope the symbol.
+ final Block block = (Block)node;
+ if(block.getExistingSymbol(name) == symbol) {
+ assert block.needsScope();
+ return true;
+ }
+ previousWasBlock = true;
+ } else {
+ if((node instanceof WithNode && previousWasBlock) || (node instanceof FunctionNode && isFunctionDynamicScope((FunctionNode)node))) {
+ // If we hit a scope that can have symbols introduced into it at run time before finding the defining
+ // block, the symbol can't be fast scoped. A WithNode only counts if we've immediately seen a block
+ // before - its block. Otherwise, we are currently processing the WithNode's expression, and that's
+ // obviously not subjected to introducing new symbols.
+ return false;
+ }
+ previousWasBlock = false;
}
}
- return true;
+ // Should've found the symbol defined in a block
+ throw new AssertionError();
}
private MethodEmitter loadSharedScopeVar(final Type valueType, final Symbol symbol, final int flags) {
- method.load(isFastScope(symbol) ? getScopeProtoDepth(getCurrentBlock(), symbol) : -1);
+ method.load(isFastScope(symbol) ? getScopeProtoDepth(getLexicalContext().getCurrentBlock(), symbol) : -1);
final SharedScopeCall scopeCall = getScopeGet(valueType, symbol, flags | CALLSITE_FAST_SCOPE);
scopeCall.generateInvoke(method);
return method;
@@ -271,10 +403,10 @@ final class CodeGenerator extends NodeOperatorVisitor {
private int getScopeProtoDepth(final Block startingBlock, final Symbol symbol) {
int depth = 0;
- final Block definingBlock = symbol.getBlock();
- for(final Iterator<Block> blocks = lexicalContext.getBlocks(startingBlock); blocks.hasNext();) {
+ final String name = symbol.getName();
+ for(final Iterator<Block> blocks = getLexicalContext().getBlocks(startingBlock); blocks.hasNext();) {
final Block currentBlock = blocks.next();
- if (currentBlock == definingBlock) {
+ if (currentBlock.getExistingSymbol(name) == symbol) {
return depth;
}
if (currentBlock.needsScope()) {
@@ -285,9 +417,9 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
private void loadFastScopeProto(final Symbol symbol, final boolean swap) {
- final int depth = getScopeProtoDepth(getCurrentBlock(), symbol);
+ final int depth = getScopeProtoDepth(getLexicalContext().getCurrentBlock(), symbol);
assert depth != -1;
- if(depth > 0) {
+ if (depth > 0) {
if (swap) {
method.swap();
}
@@ -328,46 +460,46 @@ final class CodeGenerator extends NodeOperatorVisitor {
*/
final CodeGenerator codegen = this;
- node.accept(new NodeVisitor(getCurrentCompileUnit(), method) {
+ node.accept(new NodeVisitor() {
@Override
- public Node enterIdentNode(final IdentNode identNode) {
+ public boolean enterIdentNode(final IdentNode identNode) {
loadIdent(identNode);
- return null;
+ return false;
}
@Override
- public Node enterAccessNode(final AccessNode accessNode) {
+ public boolean enterAccessNode(final AccessNode accessNode) {
if (!baseAlreadyOnStack) {
load(accessNode.getBase()).convert(Type.OBJECT);
}
assert method.peekType().isObject();
method.dynamicGet(node.getType(), accessNode.getProperty().getName(), getCallSiteFlags(), accessNode.isFunction());
- return null;
+ return false;
}
@Override
- public Node enterIndexNode(final IndexNode indexNode) {
+ public boolean enterIndexNode(final IndexNode indexNode) {
if (!baseAlreadyOnStack) {
load(indexNode.getBase()).convert(Type.OBJECT);
load(indexNode.getIndex());
}
method.dynamicGetIndex(node.getType(), getCallSiteFlags(), indexNode.isFunction());
- return null;
+ return false;
}
@Override
- public Node enterFunctionNode(FunctionNode functionNode) {
+ public boolean enterFunctionNode(FunctionNode functionNode) {
// function nodes will always leave a constructed function object on stack, no need to load the symbol
// separately as in enterDefault()
functionNode.accept(codegen);
- return null;
+ return false;
}
@Override
- public Node enterDefault(final Node otherNode) {
+ public boolean enterDefault(final Node otherNode) {
otherNode.accept(codegen); // generate code for whatever we are looking at.
method.load(symbol); // load the final symbol to the stack (or nop if no slot, then result is already there)
- return null;
+ return false;
}
});
@@ -375,14 +507,9 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
@Override
- public Node enterAccessNode(final AccessNode accessNode) {
- if (accessNode.testResolved()) {
- return null;
- }
-
+ public boolean enterAccessNode(final AccessNode accessNode) {
load(accessNode);
-
- return null;
+ return false;
}
/**
@@ -407,7 +534,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
final boolean isInternal = symbol.isParam() || symbol.isInternal() || symbol.isThis() || !symbol.canBeUndefined();
if (symbol.hasSlot() && !isInternal) {
- assert symbol.getSymbolType().isNumber() || symbol.getSymbolType().isObject() : "no potentially undefined narrower local vars than doubles are allowed: " + symbol + " in " + getCurrentFunctionNode();
+ assert symbol.getSymbolType().isNumber() || symbol.getSymbolType().isObject() : "no potentially undefined narrower local vars than doubles are allowed: " + symbol + " in " + getLexicalContext().getCurrentFunction();
if (symbol.getSymbolType().isNumber()) {
numbers.add(symbol);
} else if (symbol.getSymbolType().isObject()) {
@@ -441,22 +568,20 @@ final class CodeGenerator extends NodeOperatorVisitor {
* @param block block containing symbols.
*/
private void symbolInfo(final Block block) {
- for (final Symbol symbol : block.getFrame().getSymbols()) {
- method.localVariable(symbol, block.getEntryLabel(), block.getBreakLabel());
+ for (final Iterator<Symbol> iter = block.symbolIterator(); iter.hasNext(); ) {
+ final Symbol symbol = iter.next();
+ if (symbol.hasSlot()) {
+ method.localVariable(symbol, block.getEntryLabel(), block.getBreakLabel());
+ }
}
}
@Override
- public Node enterBlock(final Block block) {
- if (block.testResolved()) {
- return null;
- }
- lexicalContext.push(block);
-
+ public boolean enterBlock(final Block block) {
method.label(block.getEntryLabel());
initLocals(block);
- return block;
+ return true;
}
@Override
@@ -464,10 +589,10 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.label(block.getBreakLabel());
symbolInfo(block);
- if (block.needsScope()) {
+ if (block.needsScope() && !block.isTerminal()) {
popBlockScope(block);
}
- lexicalContext.pop(block);
+ --nextFreeSlotsSize;
return block;
}
@@ -477,34 +602,30 @@ final class CodeGenerator extends NodeOperatorVisitor {
final Label skipLabel = new Label("skip_catch");
/* pop scope a la try-finally */
- method.loadScope();
+ method.loadCompilerConstant(SCOPE);
method.invoke(ScriptObject.GET_PROTO);
- method.storeScope();
+ method.storeCompilerConstant(SCOPE);
method._goto(skipLabel);
method.label(exitLabel);
method._catch(recoveryLabel);
- method.loadScope();
+ method.loadCompilerConstant(SCOPE);
method.invoke(ScriptObject.GET_PROTO);
- method.storeScope();
+ method.storeCompilerConstant(SCOPE);
method.athrow();
method.label(skipLabel);
method._try(block.getEntryLabel(), exitLabel, recoveryLabel, Throwable.class);
}
@Override
- public Node enterBreakNode(final BreakNode breakNode) {
- if (breakNode.testResolved()) {
- return null;
- }
-
- for (int i = 0; i < breakNode.getScopeNestingLevel(); i++) {
+ public boolean enterBreakNode(final BreakNode breakNode) {
+ final BreakableNode breakFrom = getLexicalContext().getBreakable(breakNode.getLabel());
+ for (int i = 0; i < getLexicalContext().getScopeNestingLevelTo(breakFrom); i++) {
closeWith();
}
+ method.splitAwareGoto(getLexicalContext(), breakFrom.getBreakLabel());
- method.splitAwareGoto(breakNode.getTargetLabel());
-
- return null;
+ return false;
}
private int loadArgs(final List<Node> args) {
@@ -541,21 +662,17 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
@Override
- public Node enterCallNode(final CallNode callNode) {
- if (callNode.testResolved()) {
- return null;
- }
-
+ public boolean enterCallNode(final CallNode callNode) {
final List<Node> args = callNode.getArgs();
final Node function = callNode.getFunction();
- final Block currentBlock = getCurrentBlock();
+ final Block currentBlock = getLexicalContext().getCurrentBlock();
- function.accept(new NodeVisitor(getCurrentCompileUnit(), method) {
+ function.accept(new NodeVisitor() {
private void sharedScopeCall(final IdentNode identNode, final int flags) {
final Symbol symbol = identNode.getSymbol();
int scopeCallFlags = flags;
- method.loadScope();
+ method.loadCompilerConstant(SCOPE);
if (isFastScope(symbol)) {
method.load(getScopeProtoDepth(currentBlock, symbol));
scopeCallFlags |= CALLSITE_FAST_SCOPE;
@@ -591,7 +708,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
// We don't need ScriptFunction object for 'eval'
method.pop();
- method.loadScope(); // Load up self (scope).
+ method.loadCompilerConstant(SCOPE); // Load up self (scope).
final CallNode.EvalArgs evalArgs = callNode.getEvalArgs();
// load evaluated code
@@ -618,7 +735,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
@Override
- public Node enterIdentNode(final IdentNode node) {
+ public boolean enterIdentNode(final IdentNode node) {
final Symbol symbol = node.getSymbol();
if (symbol.isScope()) {
@@ -626,27 +743,27 @@ final class CodeGenerator extends NodeOperatorVisitor {
final int useCount = symbol.getUseCount();
// Threshold for generating shared scope callsite is lower for fast scope symbols because we know
- // we can dial in the correct scope. However, we als need to enable it for non-fast scopes to
+ // we can dial in the correct scope. However, we also need to enable it for non-fast scopes to
// support huge scripts like mandreel.js.
if (callNode.isEval()) {
evalCall(node, flags);
} else if (useCount <= SharedScopeCall.FAST_SCOPE_CALL_THRESHOLD
|| (!isFastScope(symbol) && useCount <= SharedScopeCall.SLOW_SCOPE_CALL_THRESHOLD)
- || callNode.inWithBlock()) {
+ || CodeGenerator.this.inDynamicScope()) {
scopeCall(node, flags);
} else {
sharedScopeCall(node, flags);
}
- assert method.peekType().equals(callNode.getType());
+ assert method.peekType().equals(callNode.getType()) : method.peekType() + "!=" + callNode.getType();
} else {
enterDefault(node);
}
- return null;
+ return false;
}
@Override
- public Node enterAccessNode(final AccessNode node) {
+ public boolean enterAccessNode(final AccessNode node) {
load(node.getBase());
method.convert(Type.OBJECT);
method.dup();
@@ -655,35 +772,34 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.dynamicCall(callNode.getType(), 2 + loadArgs(args), getCallSiteFlags());
assert method.peekType().equals(callNode.getType());
- return null;
+ return false;
}
@Override
- public Node enterFunctionNode(final FunctionNode callee) {
+ public boolean enterFunctionNode(final FunctionNode origCallee) {
+ // NOTE: visiting the callee will leave a constructed ScriptFunction object on the stack if
+ // callee.needsCallee() == true
+ final FunctionNode callee = (FunctionNode)origCallee.accept(CodeGenerator.this);
+
final boolean isVarArg = callee.isVarArg();
final int argCount = isVarArg ? -1 : callee.getParameters().size();
final String signature = new FunctionSignature(true, callee.needsCallee(), callee.getReturnType(), isVarArg ? null : callee.getParameters()).toString();
- if (callee.needsCallee()) {
- newFunctionObject(callee);
- }
-
- if (callee.isStrictMode()) { // self is undefined
+ if (callee.isStrict()) { // self is undefined
method.loadUndefined(Type.OBJECT);
} else { // get global from scope (which is the self)
globalInstance();
}
loadArgs(args, signature, isVarArg, argCount);
+ assert callee.getCompileUnit() != null : "no compile unit for " + callee.getName() + " " + Debug.id(callee) + " " + callNode;
method.invokestatic(callee.getCompileUnit().getUnitClassName(), callee.getName(), signature);
assert method.peekType().equals(callee.getReturnType()) : method.peekType() + " != " + callee.getReturnType();
- functionNodeIsCallee = true;
- callee.accept(CodeGenerator.this);
- return null;
+ return false;
}
@Override
- public Node enterIndexNode(final IndexNode node) {
+ public boolean enterIndexNode(final IndexNode node) {
load(node.getBase());
method.convert(Type.OBJECT);
method.dup();
@@ -697,11 +813,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.dynamicCall(callNode.getType(), 2 + loadArgs(args), getCallSiteFlags());
assert method.peekType().equals(callNode.getType());
- return null;
+ return false;
}
@Override
- protected Node enterDefault(final Node node) {
+ protected boolean enterDefault(final Node node) {
// Load up function.
load(function);
method.convert(Type.OBJECT); //TODO, e.g. booleans can be used as functions
@@ -709,58 +825,41 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.dynamicCall(callNode.getType(), 2 + loadArgs(args), getCallSiteFlags() | CALLSITE_SCOPE);
assert method.peekType().equals(callNode.getType());
- return null;
+ return false;
}
});
method.store(callNode.getSymbol());
- return null;
+ return false;
}
@Override
- public Node enterContinueNode(final ContinueNode continueNode) {
- if (continueNode.testResolved()) {
- return null;
- }
-
- for (int i = 0; i < continueNode.getScopeNestingLevel(); i++) {
+ public boolean enterContinueNode(final ContinueNode continueNode) {
+ final LoopNode continueTo = getLexicalContext().getContinueTo(continueNode.getLabel());
+ for (int i = 0; i < getLexicalContext().getScopeNestingLevelTo(continueTo); i++) {
closeWith();
}
+ method.splitAwareGoto(getLexicalContext(), continueTo.getContinueLabel());
- method.splitAwareGoto(continueNode.getTargetLabel());
-
- return null;
- }
-
- @Override
- public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
- return enterWhileNode(doWhileNode);
+ return false;
}
@Override
- public Node enterEmptyNode(final EmptyNode emptyNode) {
- return null;
+ public boolean enterEmptyNode(final EmptyNode emptyNode) {
+ return false;
}
@Override
- public Node enterExecuteNode(final ExecuteNode executeNode) {
- if (executeNode.testResolved()) {
- return null;
- }
-
+ public boolean enterExecuteNode(final ExecuteNode executeNode) {
final Node expression = executeNode.getExpression();
expression.accept(this);
- return null;
+ return false;
}
@Override
- public Node enterForNode(final ForNode forNode) {
- if (forNode.testResolved()) {
- return null;
- }
-
+ public boolean enterForNode(final ForNode forNode) {
final Node test = forNode.getTest();
final Block body = forNode.getBody();
final Node modify = forNode.getModify();
@@ -790,6 +889,10 @@ final class CodeGenerator extends NodeOperatorVisitor {
new Store<Node>(init) {
@Override
+ protected void storeNonDiscard() {
+ return;
+ }
+ @Override
protected void evaluate() {
method.load(iter);
method.invoke(interfaceCallNoLookup(Iterator.class, "next", Object.class));
@@ -829,7 +932,19 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.label(breakLabel);
}
- return null;
+ return false;
+ }
+
+ private static int assignSlots(final Block block, final int firstSlot) {
+ int nextSlot = firstSlot;
+ for (final Iterator<Symbol> iter = block.symbolIterator(); iter.hasNext(); ) {
+ final Symbol next = iter.next();
+ if (next.hasSlot()) {
+ next.setSlot(nextSlot);
+ nextSlot += next.slotCount();
+ }
+ }
+ return nextSlot;
}
/**
@@ -838,21 +953,26 @@ final class CodeGenerator extends NodeOperatorVisitor {
* @param block block with local vars.
*/
private void initLocals(final Block block) {
- final FunctionNode function = lexicalContext.getFunction(block);
- final boolean isFunctionNode = block == function;
-
- /*
- * Get the symbols from the frame and realign the frame so that all
- * slots get correct numbers. The slot numbering is not fixed until
- * after initLocals has been run
- */
- final Frame frame = block.getFrame();
- final List<Symbol> symbols = frame.getSymbols();
+ final boolean isFunctionBody = getLexicalContext().isFunctionBody();
- /* Fix the predefined slots so they have numbers >= 0, like varargs. */
- frame.realign();
+ final int nextFreeSlot;
+ if (isFunctionBody) {
+ // On entry to function, start with slot 0
+ nextFreeSlot = 0;
+ } else {
+ // Otherwise, continue from previous block's first free slot
+ nextFreeSlot = nextFreeSlots[nextFreeSlotsSize - 1];
+ }
+ if(nextFreeSlotsSize == nextFreeSlots.length) {
+ final int[] newNextFreeSlots = new int[nextFreeSlotsSize * 2];
+ System.arraycopy(nextFreeSlots, 0, newNextFreeSlots, 0, nextFreeSlotsSize);
+ nextFreeSlots = newNextFreeSlots;
+ }
+ nextFreeSlots[nextFreeSlotsSize++] = assignSlots(block, nextFreeSlot);
- if (isFunctionNode) {
+ final FunctionNode function = getLexicalContext().getCurrentFunction();
+ if (isFunctionBody) {
+ /* Fix the predefined slots so they have numbers >= 0, like varargs. */
if (function.needsParentScope()) {
initParentScope();
}
@@ -876,14 +996,18 @@ final class CodeGenerator extends NodeOperatorVisitor {
final List<String> nameList = new ArrayList<>();
final List<Symbol> locals = new ArrayList<>();
-
// Initalize symbols and values
final List<Symbol> newSymbols = new ArrayList<>();
final List<Symbol> values = new ArrayList<>();
final boolean hasArguments = function.needsArguments();
- for (final Symbol symbol : symbols) {
- if (symbol.isInternal() || symbol.isThis()) {
+
+ final Iterator<Symbol> symbols = block.symbolIterator();
+
+ while (symbols.hasNext()) {
+ final Symbol symbol = symbols.next();
+
+ if (symbol.isInternal() || symbol.isThis() || symbol.isTemp()) {
continue;
}
@@ -907,9 +1031,6 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}
- /* Correct slot numbering again */
- frame.realign();
-
// we may have locals that need to be initialized
initSymbols(locals);
@@ -931,7 +1052,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
@Override
protected void loadScope(MethodEmitter m) {
if(function.needsParentScope()) {
- m.loadScope();
+ m.loadCompilerConstant(SCOPE);
} else {
m.loadNull();
}
@@ -940,118 +1061,102 @@ final class CodeGenerator extends NodeOperatorVisitor {
foc.makeObject(method);
// runScript(): merge scope into global
- if (isFunctionNode && function.isProgram()) {
+ if (isFunctionBody && function.isProgram()) {
method.invoke(ScriptRuntime.MERGE_SCOPE);
}
- method.storeScope();
+ method.storeCompilerConstant(SCOPE);
} else {
// Since we don't have a scope, parameters didn't get assigned array indices by the FieldObjectCreator, so
// we need to assign them separately here.
int nextParam = 0;
- if (isFunctionNode && function.isVarArg()) {
+ if (isFunctionBody && function.isVarArg()) {
for (final IdentNode param : function.getParameters()) {
param.getSymbol().setFieldIndex(nextParam++);
}
}
+
+ final Iterator<Symbol> iter = block.symbolIterator();
+ final List<Symbol> symbols = new ArrayList<>();
+ while (iter.hasNext()) {
+ symbols.add(iter.next());
+ }
initSymbols(symbols);
}
// Debugging: print symbols? @see --print-symbols flag
- printSymbols(block, (isFunctionNode ? "Function " : "Block in ") + (function.getIdent() == null ? "<anonymous>" : function.getIdent().getName()));
+ printSymbols(block, (isFunctionBody ? "Function " : "Block in ") + (function.getIdent() == null ? "<anonymous>" : function.getIdent().getName()));
}
private void initArguments(final FunctionNode function) {
- method.loadVarArgs();
+ method.loadCompilerConstant(VARARGS);
if(function.needsCallee()) {
- method.loadCallee();
+ method.loadCompilerConstant(CALLEE);
} else {
// If function is strict mode, "arguments.callee" is not populated, so we don't necessarily need the
// caller.
- assert function.isStrictMode();
+ assert function.isStrict();
method.loadNull();
}
method.load(function.getParameters().size());
globalAllocateArguments();
- method.storeArguments();
+ method.storeCompilerConstant(ARGUMENTS);
}
private void initParentScope() {
- method.loadCallee();
+ method.loadCompilerConstant(CALLEE);
method.invoke(ScriptFunction.GET_SCOPE);
- method.storeScope();
+ method.storeCompilerConstant(SCOPE);
}
@Override
- public Node enterFunctionNode(final FunctionNode functionNode) {
- final boolean isCallee = functionNodeIsCallee;
- functionNodeIsCallee = false;
-
- if (functionNode.testResolved()) {
- return null;
- }
-
- if(!(isCallee || functionNode == compiler.getFunctionNode())) {
- newFunctionObject(functionNode);
- }
-
+ public boolean enterFunctionNode(final FunctionNode functionNode) {
if (functionNode.isLazy()) {
- return null;
+ // Must do it now; can't postpone it until leaveFunctionNode()
+ newFunctionObject(functionNode, functionNode);
+ return false;
}
- LOG.info("=== BEGIN " + functionNode.getName());
- lexicalContext.push(functionNode);
+ LOG.info("=== BEGIN ", functionNode.getName());
- setCurrentCompileUnit(functionNode.getCompileUnit());
- assert getCurrentCompileUnit() != null;
+ assert functionNode.getCompileUnit() != null : "no compile unit for " + functionNode.getName() + " " + Debug.id(functionNode);
+ push(functionNode.getCompileUnit());
+ assert !compileUnits.isEmpty();
- setCurrentMethodEmitter(getCurrentCompileUnit().getClassEmitter().method(functionNode));
- functionNode.setMethodEmitter(method);
+ pushMethodEmitter(unit.getClassEmitter().method(functionNode));
// Mark end for variable tables.
method.begin();
- method.label(functionNode.getEntryLabel());
- initLocals(functionNode);
- functionNode.setState(CompilationState.EMITTED);
-
- return functionNode;
+ return true;
}
@Override
public Node leaveFunctionNode(final FunctionNode functionNode) {
- // Mark end for variable tables.
- method.label(functionNode.getBreakLabel());
-
- if (!functionNode.needsScope()) {
- method.markerVariable(LEAF.tag(), functionNode.getEntryLabel(), functionNode.getBreakLabel());
- }
-
- symbolInfo(functionNode);
try {
method.end(); // wrap up this method
+ pop(functionNode.getCompileUnit());
+ popMethodEmitter(method);
+ LOG.info("=== END ", functionNode.getName());
+
+ final FunctionNode newFunctionNode = functionNode.setState(getLexicalContext(), CompilationState.EMITTED);
+
+ newFunctionObject(newFunctionNode, functionNode);
+ return newFunctionNode;
} catch (final Throwable t) {
Context.printStackTrace(t);
final VerifyError e = new VerifyError("Code generation bug in \"" + functionNode.getName() + "\": likely stack misaligned: " + t + " " + functionNode.getSource().getName());
e.initCause(t);
throw e;
}
-
- lexicalContext.pop(functionNode);
- LOG.info("=== END " + functionNode.getName());
- return functionNode;
}
@Override
- public Node enterIdentNode(final IdentNode identNode) {
- return null;
+ public boolean enterIdentNode(final IdentNode identNode) {
+ return false;
}
@Override
- public Node enterIfNode(final IfNode ifNode) {
- if (ifNode.testResolved()) {
- return null;
- }
-
+ public boolean enterIfNode(final IfNode ifNode) {
final Node test = ifNode.getTest();
final Block pass = ifNode.getPass();
final Block fail = ifNode.getFail();
@@ -1082,30 +1187,21 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.label(afterLabel);
}
- return null;
+ return false;
}
@Override
- public Node enterIndexNode(final IndexNode indexNode) {
- if (indexNode.testResolved()) {
- return null;
- }
-
+ public boolean enterIndexNode(final IndexNode indexNode) {
load(indexNode);
-
- return null;
+ return false;
}
@Override
- public Node enterLineNumberNode(final LineNumberNode lineNumberNode) {
- if (lineNumberNode.testResolved()) {
- return null;
- }
-
- final Label label = new Label("line:" + lineNumberNode.getLineNumber() + " (" + getCurrentFunctionNode().getName() + ")");
+ public boolean enterLineNumberNode(final LineNumberNode lineNumberNode) {
+ final Label label = new Label((String)null);
method.label(label);
method.lineNumber(lineNumberNode.getLineNumber(), label);
- return null;
+ return false;
}
/**
@@ -1131,43 +1227,43 @@ final class CodeGenerator extends NodeOperatorVisitor {
final Type elementType = arrayType.getElementType();
if (units != null) {
- final CompileUnit savedCompileUnit = getCurrentCompileUnit();
- final MethodEmitter savedMethod = getCurrentMethodEmitter();
+ final MethodEmitter savedMethod = method;
- try {
- for (final ArrayUnit unit : units) {
- setCurrentCompileUnit(unit.getCompileUnit());
+ for (final ArrayUnit arrayUnit : units) {
+ push(arrayUnit.getCompileUnit());
- final String className = getCurrentCompileUnit().getUnitClassName();
- final String name = getCurrentFunctionNode().uniqueName(SPLIT_PREFIX.tag());
- final String signature = methodDescriptor(type, Object.class, ScriptFunction.class, ScriptObject.class, type);
+ final String className = unit.getUnitClassName();
+ final String name = getLexicalContext().getCurrentFunction().uniqueName(SPLIT_PREFIX.symbolName());
+ final String signature = methodDescriptor(type, Object.class, ScriptFunction.class, ScriptObject.class, type);
- setCurrentMethodEmitter(getCurrentCompileUnit().getClassEmitter().method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), name, signature));
- method.setFunctionNode(getCurrentFunctionNode());
- method.begin();
+ final MethodEmitter me = unit.getClassEmitter().method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), name, signature);
+ pushMethodEmitter(me);
- fixScopeSlot();
+ method.setFunctionNode(getLexicalContext().getCurrentFunction());
+ method.begin();
- method.load(arrayType, SPLIT_ARRAY_ARG.slot());
+ fixScopeSlot();
- for (int i = unit.getLo(); i < unit.getHi(); i++) {
- storeElement(nodes, elementType, postsets[i]);
- }
-
- method._return();
- method.end();
+ method.load(arrayType, SPLIT_ARRAY_ARG.slot());
- savedMethod.loadThis();
- savedMethod.swap();
- savedMethod.loadCallee();
- savedMethod.swap();
- savedMethod.loadScope();
- savedMethod.swap();
- savedMethod.invokestatic(className, name, signature);
+ for (int i = arrayUnit.getLo(); i < arrayUnit.getHi(); i++) {
+ storeElement(nodes, elementType, postsets[i]);
}
- } finally {
- setCurrentCompileUnit(savedCompileUnit);
- setCurrentMethodEmitter(savedMethod);
+
+ method._return();
+ method.end();
+ popMethodEmitter(me);
+
+ assert method == savedMethod;
+ method.loadCompilerConstant(THIS);
+ method.swap();
+ method.loadCompilerConstant(CALLEE);
+ method.swap();
+ method.loadCompilerConstant(SCOPE);
+ method.swap();
+ method.invokestatic(className, name, signature);
+
+ pop(unit);
}
return method;
@@ -1217,12 +1313,12 @@ final class CodeGenerator extends NodeOperatorVisitor {
* @param string string to load
*/
void loadConstant(final String string) {
- final String unitClassName = getCurrentCompileUnit().getUnitClassName();
- final ClassEmitter classEmitter = getCurrentCompileUnit().getClassEmitter();
+ final String unitClassName = unit.getUnitClassName();
+ final ClassEmitter classEmitter = unit.getClassEmitter();
final int index = compiler.getConstantData().add(string);
method.load(index);
- method.invokestatic(unitClassName, GET_STRING.tag(), methodDescriptor(String.class, int.class));
+ method.invokestatic(unitClassName, GET_STRING.symbolName(), methodDescriptor(String.class, int.class));
classEmitter.needGetConstantMethod(String.class);
}
@@ -1233,14 +1329,14 @@ final class CodeGenerator extends NodeOperatorVisitor {
* @param object object to load
*/
void loadConstant(final Object object) {
- final String unitClassName = getCurrentCompileUnit().getUnitClassName();
- final ClassEmitter classEmitter = getCurrentCompileUnit().getClassEmitter();
+ final String unitClassName = unit.getUnitClassName();
+ final ClassEmitter classEmitter = unit.getClassEmitter();
final int index = compiler.getConstantData().add(object);
final Class<?> cls = object.getClass();
if (cls == PropertyMap.class) {
method.load(index);
- method.invokestatic(unitClassName, GET_MAP.tag(), methodDescriptor(PropertyMap.class, int.class));
+ method.invokestatic(unitClassName, GET_MAP.symbolName(), methodDescriptor(PropertyMap.class, int.class));
classEmitter.needGetConstantMethod(PropertyMap.class);
} else if (cls.isArray()) {
method.load(index);
@@ -1303,14 +1399,14 @@ final class CodeGenerator extends NodeOperatorVisitor {
return loadRegexToken(regexToken);
}
// emit field
- final String regexName = getCurrentFunctionNode().uniqueName(REGEX_PREFIX.tag());
- final ClassEmitter classEmitter = getCurrentCompileUnit().getClassEmitter();
+ final String regexName = getLexicalContext().getCurrentFunction().uniqueName(REGEX_PREFIX.symbolName());
+ final ClassEmitter classEmitter = unit.getClassEmitter();
classEmitter.field(EnumSet.of(PRIVATE, STATIC), regexName, Object.class);
regexFieldCount++;
// get field, if null create new regex, finally clone regex object
- method.getStatic(getCurrentCompileUnit().getUnitClassName(), regexName, typeDescriptor(Object.class));
+ method.getStatic(unit.getUnitClassName(), regexName, typeDescriptor(Object.class));
method.dup();
final Label cachedLabel = new Label("cached");
method.ifnonnull(cachedLabel);
@@ -1318,7 +1414,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.pop();
loadRegexToken(regexToken);
method.dup();
- method.putStatic(getCurrentCompileUnit().getUnitClassName(), regexName, typeDescriptor(Object.class));
+ method.putStatic(unit.getUnitClassName(), regexName, typeDescriptor(Object.class));
method.label(cachedLabel);
globalRegExpCopy();
@@ -1328,18 +1424,14 @@ final class CodeGenerator extends NodeOperatorVisitor {
@SuppressWarnings("rawtypes")
@Override
- public Node enterLiteralNode(final LiteralNode literalNode) {
+ public boolean enterLiteralNode(final LiteralNode literalNode) {
assert literalNode.getSymbol() != null : literalNode + " has no symbol";
load(literalNode).store(literalNode.getSymbol());
- return null;
+ return false;
}
@Override
- public Node enterObjectNode(final ObjectNode objectNode) {
- if (objectNode.testResolved()) {
- return null;
- }
-
+ public boolean enterObjectNode(final ObjectNode objectNode) {
final List<Node> elements = objectNode.getElements();
final int size = elements.size();
@@ -1404,14 +1496,14 @@ final class CodeGenerator extends NodeOperatorVisitor {
if (!hasGettersSetters) {
method.store(objectNode.getSymbol());
- return null;
+ return false;
}
for (final Node element : elements) {
final PropertyNode propertyNode = (PropertyNode)element;
final Object key = propertyNode.getKey();
- final FunctionNode getter = (FunctionNode)propertyNode.getGetter();
- final FunctionNode setter = (FunctionNode)propertyNode.getSetter();
+ final FunctionNode getter = propertyNode.getGetter();
+ final FunctionNode setter = propertyNode.getSetter();
if (getter == null && setter == null) {
continue;
@@ -1436,35 +1528,25 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.store(objectNode.getSymbol());
- return null;
+ return false;
}
@Override
- public Node enterReturnNode(final ReturnNode returnNode) {
- if (returnNode.testResolved()) {
- return null;
- }
-
- // Set the split return flag in the scope if this is a split method fragment.
- if (method.getSplitNode() != null) {
- assert method.getSplitNode().hasReturn() : "unexpected return in split node";
+ public boolean enterReturnNode(final ReturnNode returnNode) {
+ method.registerReturn();
- method.loadScope();
- method.checkcast(Scope.class);
- method.load(0);
- method.invoke(Scope.SET_SPLIT_STATE);
- }
+ final Type returnType = getLexicalContext().getCurrentFunction().getReturnType();
final Node expression = returnNode.getExpression();
if (expression != null) {
load(expression);
} else {
- method.loadUndefined(getCurrentFunctionNode().getReturnType());
+ method.loadUndefined(returnType);
}
- method._return(getCurrentFunctionNode().getReturnType());
+ method._return(returnType);
- return null;
+ return false;
}
private static boolean isNullLiteral(final Node node) {
@@ -1542,19 +1624,20 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
assert args.size() == 2;
- final Node lhs = args.get(0);
- final Node rhs = args.get(1);
-
final Type returnType = node.getType();
- load(lhs);
- load(rhs);
+
+ load(args.get(0));
+ load(args.get(1));
Request finalRequest = request;
+ //if the request is a comparison, i.e. one that can be reversed
+ //it keeps its semantic, but make sure that the object comes in
+ //last
final Request reverse = Request.reverse(request);
- if (method.peekType().isObject() && reverse != null) {
- if (!method.peekType(1).isObject()) {
- method.swap();
+ if (method.peekType().isObject() && reverse != null) { //rhs is object
+ if (!method.peekType(1).isObject()) { //lhs is not object
+ method.swap(); //prefer object as lhs
finalRequest = reverse;
}
}
@@ -1581,11 +1664,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
@Override
- public Node enterRuntimeNode(final RuntimeNode runtimeNode) {
- if (runtimeNode.testResolved()) {
- return null;
- }
-
+ public boolean enterRuntimeNode(final RuntimeNode runtimeNode) {
/*
* First check if this should be something other than a runtime node
* AccessSpecializer might have changed the type
@@ -1624,7 +1703,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.add();
method.convert(type);
method.store(symbol);
- return null;
+ return false;
default:
// it's ok to send this one on with only primitive arguments, maybe INSTANCEOF(true, true) or similar
// assert false : runtimeNode + " has all primitive arguments. This is an inconsistent state";
@@ -1636,11 +1715,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
final List<Node> args = runtimeNode.getArgs();
if (nullCheck(runtimeNode, args, new FunctionSignature(false, false, runtimeNode.getType(), args).toString())) {
- return null;
+ return false;
}
if (!runtimeNode.isFinal() && specializationCheck(runtimeNode.getRequest(), runtimeNode, args)) {
- return null;
+ return false;
}
for (final Node arg : runtimeNode.getArgs()) {
@@ -1658,129 +1737,146 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.convert(runtimeNode.getType());
method.store(runtimeNode.getSymbol());
- return null;
+ return false;
}
@Override
- public Node enterSplitNode(final SplitNode splitNode) {
- if (splitNode.testResolved()) {
- return null;
- }
-
+ public boolean enterSplitNode(final SplitNode splitNode) {
final CompileUnit splitCompileUnit = splitNode.getCompileUnit();
- final FunctionNode fn = getCurrentFunctionNode();
+ final FunctionNode fn = getLexicalContext().getCurrentFunction();
final String className = splitCompileUnit.getUnitClassName();
final String name = splitNode.getName();
- final Class<?> rtype = fn.getReturnType().getTypeClass();
- final boolean needsArguments = fn.needsArguments();
- final Class<?>[] ptypes = needsArguments ?
+ final Class<?> rtype = fn.getReturnType().getTypeClass();
+ final boolean needsArguments = fn.needsArguments();
+ final Class<?>[] ptypes = needsArguments ?
new Class<?>[] {ScriptFunction.class, Object.class, ScriptObject.class, Object.class} :
new Class<?>[] {ScriptFunction.class, Object.class, ScriptObject.class};
- setCurrentCompileUnit(splitCompileUnit);
- splitNode.setCompileUnit(splitCompileUnit);
+ final MethodEmitter caller = method;
+ push(splitCompileUnit);
final Call splitCall = staticCallNoLookup(
className,
name,
methodDescriptor(rtype, ptypes));
- setCurrentMethodEmitter(
- splitCompileUnit.getClassEmitter().method(
- EnumSet.of(Flag.PUBLIC, Flag.STATIC),
- name,
- rtype,
- ptypes));
+ final MethodEmitter splitEmitter =
+ splitCompileUnit.getClassEmitter().method(
+ splitNode,
+ name,
+ rtype,
+ ptypes);
+
+ pushMethodEmitter(splitEmitter);
method.setFunctionNode(fn);
- method.setSplitNode(splitNode);
- splitNode.setMethodEmitter(method);
- final MethodEmitter caller = splitNode.getCaller();
- if(fn.needsCallee()) {
- caller.loadCallee();
+ if (fn.needsCallee()) {
+ caller.loadCompilerConstant(CALLEE);
} else {
caller.loadNull();
}
- caller.loadThis();
- caller.loadScope();
+ caller.loadCompilerConstant(THIS);
+ caller.loadCompilerConstant(SCOPE);
if (needsArguments) {
- caller.loadArguments();
+ caller.loadCompilerConstant(ARGUMENTS);
}
caller.invoke(splitCall);
- caller.storeResult();
+ caller.storeCompilerConstant(RETURN);
method.begin();
method.loadUndefined(fn.getReturnType());
- method.storeResult();
+ method.storeCompilerConstant(RETURN);
fixScopeSlot();
- return splitNode;
+ return true;
}
private void fixScopeSlot() {
- if (getCurrentFunctionNode().getScopeNode().getSymbol().getSlot() != SCOPE.slot()) {
+ if (getLexicalContext().getCurrentFunction().compilerConstant(SCOPE).getSlot() != SCOPE.slot()) {
// TODO hack to move the scope to the expected slot (that's needed because split methods reuse the same slots as the root method)
method.load(Type.typeFor(ScriptObject.class), SCOPE.slot());
- method.storeScope();
+ method.storeCompilerConstant(SCOPE);
}
}
@Override
public Node leaveSplitNode(final SplitNode splitNode) {
+ assert method instanceof SplitMethodEmitter;
+ final boolean hasReturn = method.hasReturn();
+ final List<Label> targets = method.getExternalTargets();
+
try {
// Wrap up this method.
- method.loadResult();
- method._return(getCurrentFunctionNode().getReturnType());
+
+ method.loadCompilerConstant(RETURN);
+ method._return(getLexicalContext().getCurrentFunction().getReturnType());
method.end();
+
+ pop(splitNode.getCompileUnit());
+ popMethodEmitter(method);
+
} catch (final Throwable t) {
Context.printStackTrace(t);
- final VerifyError e = new VerifyError("Code generation bug in \"" + splitNode.getName() + "\": likely stack misaligned: " + t + " " + getCurrentFunctionNode().getSource().getName());
+ final VerifyError e = new VerifyError("Code generation bug in \"" + splitNode.getName() + "\": likely stack misaligned: " + t + " " + getLexicalContext().getCurrentFunction().getSource().getName());
e.initCause(t);
throw e;
}
// Handle return from split method if there was one.
- final MethodEmitter caller = splitNode.getCaller();
- final List<Label> targets = splitNode.getExternalTargets();
- final int targetCount = targets.size();
+ 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);
- if (splitNode.hasReturn() || targetCount > 0) {
+ 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
- caller.loadScope();
- caller.checkcast(Scope.class);
- caller.invoke(Scope.GET_SPLIT_STATE);
+ //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"));
+ method.loadCompilerConstant(RETURN);
+ caller._return(getLexicalContext().getCurrentFunction().getReturnType());
+ caller.label(breakLabel);
+ } else {
+ assert !targets.isEmpty();
- // Split state is -1 for no split state, 0 for return, 1..n+1 for break/continue
- final Label breakLabel = new Label("no_split_state");
- final int low = splitNode.hasReturn() ? 0 : 1;
- final int labelCount = targetCount + 1 - low;
- final Label[] labels = new Label[labelCount];
+ 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("split_state_" + 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.loadResult();
- caller._return(getCurrentFunctionNode().getReturnType());
+ caller.loadCompilerConstant(RETURN);
+ caller._return(getLexicalContext().getCurrentFunction().getReturnType());
} else {
// Clear split state.
- caller.loadScope();
+ caller.loadCompilerConstant(SCOPE);
caller.checkcast(Scope.class);
caller.load(-1);
caller.invoke(Scope.SET_SPLIT_STATE);
- caller.splitAwareGoto(targets.get(i - 1));
+ caller.splitAwareGoto(getLexicalContext(), targets.get(i - 1));
}
}
-
caller.label(breakLabel);
}
@@ -1788,11 +1884,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
@Override
- public Node enterSwitchNode(final SwitchNode switchNode) {
- if (switchNode.testResolved()) {
- return null;
- }
-
+ public boolean enterSwitchNode(final SwitchNode switchNode) {
final Node expression = switchNode.getExpression();
final Symbol tag = switchNode.getTag();
final boolean allInteger = tag.getSymbolType().isInteger();
@@ -1810,7 +1902,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
if (cases.isEmpty()) {
method.label(breakLabel);
- return null;
+ return false;
}
if (allInteger) {
@@ -1916,15 +2008,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.label(breakLabel);
}
- return null;
+ return false;
}
@Override
- public Node enterThrowNode(final ThrowNode throwNode) {
- if (throwNode.testResolved()) {
- return null;
- }
-
+ public boolean enterThrowNode(final ThrowNode throwNode) {
method._new(ECMAException.class).dup();
final Node expression = throwNode.getExpression();
@@ -1943,15 +2031,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.athrow();
- return null;
+ return false;
}
@Override
- public Node enterTryNode(final TryNode tryNode) {
- if (tryNode.testResolved()) {
- return null;
- }
-
+ public boolean enterTryNode(final TryNode tryNode) {
final Block body = tryNode.getBody();
final List<Block> catchBlocks = tryNode.getCatchBlocks();
final Symbol symbol = tryNode.getException();
@@ -1974,74 +2058,68 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.store(symbol);
for (int i = 0; i < catchBlocks.size(); i++) {
- final Block saveBlock = getCurrentBlock();
final Block catchBlock = catchBlocks.get(i);
- setCurrentBlock(catchBlock);
-
- try {
- enterBlock(catchBlock);
+ //TODO this is very ugly - try not to call enter/leave methods directly
+ //better to use the implicit lexical context scoping given by the visitor's
+ //accept method.
+ getLexicalContext().push(catchBlock);
+ enterBlock(catchBlock);
- final CatchNode catchNode = (CatchNode)catchBlocks.get(i).getStatements().get(0);
- final IdentNode exception = catchNode.getException();
- final Node exceptionCondition = catchNode.getExceptionCondition();
- final Block catchBody = catchNode.getBody();
+ final CatchNode catchNode = (CatchNode)catchBlocks.get(i).getStatements().get(0);
+ final IdentNode exception = catchNode.getException();
+ final Node exceptionCondition = catchNode.getExceptionCondition();
+ final Block catchBody = catchNode.getBody();
- if (catchNode.isSyntheticRethrow()) {
- // Generate catch body (inlined finally) and rethrow exception
- catchBody.accept(this);
- method.load(symbol).athrow();
- lexicalContext.pop(catchBlock);
- continue;
+ new Store<IdentNode>(exception) {
+ @Override
+ protected void storeNonDiscard() {
+ return;
+ }
+ @Override
+ protected void evaluate() {
+ /*
+ * If caught object is an instance of ECMAException, then
+ * bind obj.thrown to the script catch var. Or else bind the
+ * caught object itself to the script catch var.
+ */
+ final Label notEcmaException = new Label("no_ecma_exception");
+
+ method.load(symbol).dup()._instanceof(ECMAException.class).ifeq(notEcmaException);
+ method.checkcast(ECMAException.class); //TODO is this necessary?
+ method.getField(ECMAException.THROWN);
+ method.label(notEcmaException);
}
+ }.store();
- new Store<IdentNode>(exception) {
- @Override
- protected void evaluate() {
- /*
- * If caught object is an instance of ECMAException, then
- * bind obj.thrown to the script catch var. Or else bind the
- * caught object itself to the script catch var.
- */
- final Label notEcmaException = new Label("no_ecma_exception");
-
- method.load(symbol).dup()._instanceof(ECMAException.class).ifeq(notEcmaException);
- method.checkcast(ECMAException.class); //TODO is this necessary?
- method.getField(ECMAException.THROWN);
- method.label(notEcmaException);
- }
- }.store();
+ final Label next;
- final Label next;
+ if (exceptionCondition != null) {
+ next = new Label("next");
+ load(exceptionCondition).convert(Type.BOOLEAN).ifeq(next);
+ } else {
+ next = null;
+ }
- if (exceptionCondition != null) {
- next = new Label("next");
- load(exceptionCondition).convert(Type.BOOLEAN).ifeq(next);
- } else {
- next = null;
- }
+ catchBody.accept(this);
- catchBody.accept(this);
+ if (i + 1 != catchBlocks.size() && !catchBody.hasTerminalFlags()) {
+ method._goto(skip);
+ }
- if (i + 1 != catchBlocks.size() && !catchBody.hasTerminalFlags()) {
+ if (next != null) {
+ if (i + 1 == catchBlocks.size()) {
+ // no next catch block - rethrow if condition failed
method._goto(skip);
+ method.label(next);
+ method.load(symbol).athrow();
+ } else {
+ method.label(next);
}
-
- if (next != null) {
- if (i + 1 == catchBlocks.size()) {
- // no next catch block - rethrow if condition failed
- method._goto(skip);
- method.label(next);
- method.load(symbol).athrow();
- } else {
- method.label(next);
- }
- }
-
- leaveBlock(catchBlock);
- } finally {
- setCurrentBlock(saveBlock);
}
+
+ leaveBlock(catchBlock);
+ getLexicalContext().pop(catchBlock);
}
method.label(skip);
@@ -2049,15 +2127,15 @@ final class CodeGenerator extends NodeOperatorVisitor {
// Finally body is always inlined elsewhere so it doesn't need to be emitted
- return null;
+ return false;
}
@Override
- public Node enterVarNode(final VarNode varNode) {
+ public boolean enterVarNode(final VarNode varNode) {
final Node init = varNode.getInit();
- if (varNode.testResolved() || init == null) {
- return null;
+ if (init == null) {
+ return false;
}
final Symbol varSymbol = varNode.getSymbol();
@@ -2067,7 +2145,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
final boolean needsScope = varSymbol.isScope();
if (needsScope) {
- method.loadScope();
+ method.loadCompilerConstant(SCOPE);
}
load(init);
@@ -2087,22 +2165,18 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.store(varSymbol);
}
- return null;
+ return false;
}
@Override
- public Node enterWhileNode(final WhileNode whileNode) {
- if (whileNode.testResolved()) {
- return null;
- }
-
+ public boolean enterWhileNode(final WhileNode whileNode) {
final Node test = whileNode.getTest();
final Block body = whileNode.getBody();
final Label breakLabel = whileNode.getBreakLabel();
final Label continueLabel = whileNode.getContinueLabel();
final Label loopLabel = new Label("loop");
- if (!(whileNode instanceof DoWhileNode)) {
+ if (!whileNode.isDoWhile()) {
method._goto(continueLabel);
}
@@ -2114,90 +2188,95 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.label(breakLabel);
}
- return null;
+ return false;
}
private void closeWith() {
- method.loadScope();
- method.invoke(ScriptRuntime.CLOSE_WITH);
- method.storeScope();
+ if(method.hasScope()) {
+ method.loadCompilerConstant(SCOPE);
+ method.invoke(ScriptRuntime.CLOSE_WITH);
+ method.storeCompilerConstant(SCOPE);
+ }
}
@Override
- public Node enterWithNode(final WithNode withNode) {
- if (withNode.testResolved()) {
- return null;
- }
-
+ public boolean enterWithNode(final WithNode withNode) {
final Node expression = withNode.getExpression();
final Node body = withNode.getBody();
- final Label tryLabel = new Label("with_try");
- final Label endLabel = new Label("with_end");
- final Label catchLabel = new Label("with_catch");
- final Label exitLabel = new Label("with_exit");
-
- method.label(tryLabel);
+ // It is possible to have a "pathological" case where the with block does not reference *any* identifiers. It's
+ // pointless, but legal. In that case, if nothing else in the method forced the assignment of a slot to the
+ // scope object, its' possible that it won't have a slot assigned. In this case we'll only evaluate expression
+ // for its side effect and visit the body, and not bother opening and closing a WithObject.
+ final boolean hasScope = method.hasScope();
+
+ final Label tryLabel;
+ if(hasScope) {
+ tryLabel = new Label("with_try");
+ method.label(tryLabel);
+ method.loadCompilerConstant(SCOPE);
+ } else {
+ tryLabel = null;
+ }
- method.loadScope();
load(expression);
-
assert expression.getType().isObject() : "with expression needs to be object: " + expression;
- method.invoke(ScriptRuntime.OPEN_WITH);
- method.storeScope();
+ if(hasScope) {
+ // Construct a WithObject if we have a scope
+ method.invoke(ScriptRuntime.OPEN_WITH);
+ method.storeCompilerConstant(SCOPE);
+ } else {
+ // We just loaded the expression for its side effect; discard it
+ method.pop();
+ }
+
+ // Always process body
body.accept(this);
- if (!body.isTerminal()) {
- closeWith();
- method._goto(exitLabel);
- }
+ if(hasScope) {
+ // Ensure we always close the WithObject
+ final Label endLabel = new Label("with_end");
+ final Label catchLabel = new Label("with_catch");
+ final Label exitLabel = new Label("with_exit");
- method.label(endLabel);
+ if (!body.isTerminal()) {
+ closeWith();
+ method._goto(exitLabel);
+ }
- method._catch(catchLabel);
- closeWith();
- method.athrow();
+ method.label(endLabel);
- method.label(exitLabel);
+ method._catch(catchLabel);
+ closeWith();
+ method.athrow();
- method._try(tryLabel, endLabel, catchLabel);
+ method.label(exitLabel);
- return null;
+ method._try(tryLabel, endLabel, catchLabel);
+ }
+ return false;
}
@Override
- public Node enterADD(final UnaryNode unaryNode) {
- if (unaryNode.testResolved()) {
- return null;
- }
-
+ public boolean enterADD(final UnaryNode unaryNode) {
load(unaryNode.rhs());
assert unaryNode.rhs().getType().isNumber();
method.store(unaryNode.getSymbol());
- return null;
+ return false;
}
@Override
- public Node enterBIT_NOT(final UnaryNode unaryNode) {
- if (unaryNode.testResolved()) {
- return null;
- }
-
+ public boolean enterBIT_NOT(final UnaryNode unaryNode) {
load(unaryNode.rhs()).convert(Type.INT).load(-1).xor().store(unaryNode.getSymbol());
-
- return null;
+ return false;
}
// do this better with convert calls to method. TODO
@Override
- public Node enterCONVERT(final UnaryNode unaryNode) {
- if (unaryNode.testResolved()) {
- return null;
- }
-
+ public boolean enterCONVERT(final UnaryNode unaryNode) {
final Node rhs = unaryNode.rhs();
final Type to = unaryNode.getType();
@@ -2230,15 +2309,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.store(unaryNode.getSymbol());
- return null;
+ return false;
}
@Override
- public Node enterDECINC(final UnaryNode unaryNode) {
- if (unaryNode.testResolved()) {
- return null;
- }
-
+ public boolean enterDECINC(final UnaryNode unaryNode) {
final Node rhs = unaryNode.rhs();
final Type type = unaryNode.getType();
final TokenType tokenType = unaryNode.tokenType();
@@ -2282,32 +2357,28 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}.store();
- return null;
+ return false;
}
@Override
- public Node enterDISCARD(final UnaryNode unaryNode) {
- if (unaryNode.testResolved()) {
- return null;
- }
-
+ public boolean enterDISCARD(final UnaryNode unaryNode) {
final Node rhs = unaryNode.rhs();
+ // System.err.println("**** Enter discard " + unaryNode);
+ discard.push(rhs);
load(rhs);
- if (rhs.shouldDiscard()) {
+ if (discard.peek() == rhs) {
+ assert !rhs.isAssignment();
method.pop();
+ discard.pop();
}
-
- return null;
+ // System.err.println("**** Leave discard " + unaryNode);
+ return false;
}
@Override
- public Node enterNEW(final UnaryNode unaryNode) {
- if (unaryNode.testResolved()) {
- return null;
- }
-
+ public boolean enterNEW(final UnaryNode unaryNode) {
final CallNode callNode = (CallNode)unaryNode.rhs();
final List<Node> args = callNode.getArgs();
@@ -2317,15 +2388,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.dynamicNew(1 + loadArgs(args), getCallSiteFlags());
method.store(unaryNode.getSymbol());
- return null;
+ return false;
}
@Override
- public Node enterNOT(final UnaryNode unaryNode) {
- if (unaryNode.testResolved()) {
- return null;
- }
-
+ public boolean enterNOT(final UnaryNode unaryNode) {
final Node rhs = unaryNode.rhs();
load(rhs);
@@ -2342,18 +2409,14 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.label(afterLabel);
method.store(unaryNode.getSymbol());
- return null;
+ return false;
}
@Override
- public Node enterSUB(final UnaryNode unaryNode) {
- if (unaryNode.testResolved()) {
- return null;
- }
-
+ public boolean enterSUB(final UnaryNode unaryNode) {
load(unaryNode.rhs()).neg().store(unaryNode.getSymbol());
- return null;
+ return false;
}
private Node enterNumericAdd(final Node lhs, final Node rhs, final Type type, final Symbol symbol) {
@@ -2366,11 +2429,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
@Override
- public Node enterADD(final BinaryNode binaryNode) {
- if (binaryNode.testResolved()) {
- return null;
- }
-
+ public boolean enterADD(final BinaryNode binaryNode) {
final Node lhs = binaryNode.lhs();
final Node rhs = binaryNode.rhs();
@@ -2384,14 +2443,10 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.store(binaryNode.getSymbol());
}
- return null;
+ return false;
}
- private Node enterAND_OR(final BinaryNode binaryNode) {
- if (binaryNode.testResolved()) {
- return null;
- }
-
+ private boolean enterAND_OR(final BinaryNode binaryNode) {
final Node lhs = binaryNode.lhs();
final Node rhs = binaryNode.rhs();
@@ -2410,20 +2465,16 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.label(skip);
method.store(binaryNode.getSymbol());
- return null;
+ return false;
}
@Override
- public Node enterAND(final BinaryNode binaryNode) {
+ public boolean enterAND(final BinaryNode binaryNode) {
return enterAND_OR(binaryNode);
}
@Override
- public Node enterASSIGN(final BinaryNode binaryNode) {
- if (binaryNode.testResolved()) {
- return null;
- }
-
+ public boolean enterASSIGN(final BinaryNode binaryNode) {
final Node lhs = binaryNode.lhs();
final Node rhs = binaryNode.rhs();
@@ -2442,7 +2493,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}.store();
- return null;
+ return false;
}
/**
@@ -2473,14 +2524,6 @@ final class CodeGenerator extends NodeOperatorVisitor {
this.opType = opType;
}
- @Override
- public void store() {
- if (assignNode.testResolved()) {
- return;
- }
- super.store();
- }
-
protected abstract void op();
@Override
@@ -2493,35 +2536,43 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
@Override
- public Node enterASSIGN_ADD(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_ADD(final BinaryNode binaryNode) {
assert RuntimeNode.Request.ADD.canSpecialize();
+ final Type lhsType = binaryNode.lhs().getType();
+ final Type rhsType = binaryNode.rhs().getType();
final boolean specialize = binaryNode.getType() == Type.OBJECT;
new AssignOp(binaryNode) {
- @Override
- protected boolean isSelfModifying() {
- return !specialize;
- }
@Override
protected void op() {
- method.add();
+ if (specialize) {
+ method.dynamicRuntimeCall(
+ new SpecializedRuntimeNode(
+ Request.ADD,
+ new Type[] {
+ lhsType,
+ rhsType,
+ },
+ Type.OBJECT).getInitialName(),
+ Type.OBJECT,
+ Request.ADD);
+ } else {
+ method.add();
+ }
}
@Override
protected void evaluate() {
- if (specialize && specializationCheck(Request.ADD, assignNode, Arrays.asList(assignNode.lhs(), assignNode.rhs()))) {
- return;
- }
super.evaluate();
}
}.store();
- return null;
+ return false;
}
@Override
- public Node enterASSIGN_BIT_AND(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_BIT_AND(final BinaryNode binaryNode) {
new AssignOp(Type.INT, binaryNode) {
@Override
protected void op() {
@@ -2529,11 +2580,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}.store();
- return null;
+ return false;
}
@Override
- public Node enterASSIGN_BIT_OR(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_BIT_OR(final BinaryNode binaryNode) {
new AssignOp(Type.INT, binaryNode) {
@Override
protected void op() {
@@ -2541,11 +2592,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}.store();
- return null;
+ return false;
}
@Override
- public Node enterASSIGN_BIT_XOR(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_BIT_XOR(final BinaryNode binaryNode) {
new AssignOp(Type.INT, binaryNode) {
@Override
protected void op() {
@@ -2553,11 +2604,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}.store();
- return null;
+ return false;
}
@Override
- public Node enterASSIGN_DIV(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_DIV(final BinaryNode binaryNode) {
new AssignOp(binaryNode) {
@Override
protected void op() {
@@ -2565,11 +2616,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}.store();
- return null;
+ return false;
}
@Override
- public Node enterASSIGN_MOD(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_MOD(final BinaryNode binaryNode) {
new AssignOp(binaryNode) {
@Override
protected void op() {
@@ -2577,11 +2628,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}.store();
- return null;
+ return false;
}
@Override
- public Node enterASSIGN_MUL(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_MUL(final BinaryNode binaryNode) {
new AssignOp(binaryNode) {
@Override
protected void op() {
@@ -2589,11 +2640,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}.store();
- return null;
+ return false;
}
@Override
- public Node enterASSIGN_SAR(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_SAR(final BinaryNode binaryNode) {
new AssignOp(Type.INT, binaryNode) {
@Override
protected void op() {
@@ -2601,11 +2652,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}.store();
- return null;
+ return false;
}
@Override
- public Node enterASSIGN_SHL(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_SHL(final BinaryNode binaryNode) {
new AssignOp(Type.INT, binaryNode) {
@Override
protected void op() {
@@ -2613,24 +2664,24 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}.store();
- return null;
+ return false;
}
@Override
- public Node enterASSIGN_SHR(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_SHR(final BinaryNode binaryNode) {
new AssignOp(Type.INT, binaryNode) {
@Override
protected void op() {
method.shr();
- method.convert(Type.LONG).load(0xffff_ffffL).and();
+ method.convert(Type.LONG).load(JSType.MAX_UINT).and();
}
}.store();
- return null;
+ return false;
}
@Override
- public Node enterASSIGN_SUB(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_SUB(final BinaryNode binaryNode) {
new AssignOp(binaryNode) {
@Override
protected void op() {
@@ -2638,7 +2689,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}.store();
- return null;
+ return false;
}
/**
@@ -2649,9 +2700,6 @@ final class CodeGenerator extends NodeOperatorVisitor {
protected abstract void op();
protected void evaluate(final BinaryNode node) {
- if (node.testResolved()) {
- return;
- }
load(node.lhs());
load(node.rhs());
op();
@@ -2660,7 +2708,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
@Override
- public Node enterBIT_AND(final BinaryNode binaryNode) {
+ public boolean enterBIT_AND(final BinaryNode binaryNode) {
new BinaryArith() {
@Override
protected void op() {
@@ -2668,11 +2716,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}.evaluate(binaryNode);
- return null;
+ return false;
}
@Override
- public Node enterBIT_OR(final BinaryNode binaryNode) {
+ public boolean enterBIT_OR(final BinaryNode binaryNode) {
new BinaryArith() {
@Override
protected void op() {
@@ -2680,11 +2728,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}.evaluate(binaryNode);
- return null;
+ return false;
}
@Override
- public Node enterBIT_XOR(final BinaryNode binaryNode) {
+ public boolean enterBIT_XOR(final BinaryNode binaryNode) {
new BinaryArith() {
@Override
protected void op() {
@@ -2692,14 +2740,10 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}.evaluate(binaryNode);
- return null;
+ return false;
}
- private Node enterComma(final BinaryNode binaryNode) {
- if (binaryNode.testResolved()) {
- return null;
- }
-
+ private boolean enterComma(final BinaryNode binaryNode) {
final Node lhs = binaryNode.lhs();
final Node rhs = binaryNode.rhs();
@@ -2707,21 +2751,21 @@ final class CodeGenerator extends NodeOperatorVisitor {
load(rhs);
method.store(binaryNode.getSymbol());
- return null;
+ return false;
}
@Override
- public Node enterCOMMARIGHT(final BinaryNode binaryNode) {
+ public boolean enterCOMMARIGHT(final BinaryNode binaryNode) {
return enterComma(binaryNode);
}
@Override
- public Node enterCOMMALEFT(final BinaryNode binaryNode) {
+ public boolean enterCOMMALEFT(final BinaryNode binaryNode) {
return enterComma(binaryNode);
}
@Override
- public Node enterDIV(final BinaryNode binaryNode) {
+ public boolean enterDIV(final BinaryNode binaryNode) {
new BinaryArith() {
@Override
protected void op() {
@@ -2729,10 +2773,10 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}.evaluate(binaryNode);
- return null;
+ return false;
}
- private Node enterCmp(final Node lhs, final Node rhs, final Condition cond, final Type type, final Symbol symbol) {
+ private boolean enterCmp(final Node lhs, final Node rhs, final Condition cond, final Type type, final Symbol symbol) {
final Type lhsType = lhs.getType();
final Type rhsType = rhs.getType();
@@ -2758,48 +2802,45 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.convert(type);
method.store(symbol);
- return null;
+ return false;
}
- private Node enterCmp(final BinaryNode binaryNode, final Condition cond) {
- if (binaryNode.testResolved()) {
- return null;
- }
+ private boolean enterCmp(final BinaryNode binaryNode, final Condition cond) {
return enterCmp(binaryNode.lhs(), binaryNode.rhs(), cond, binaryNode.getType(), binaryNode.getSymbol());
}
@Override
- public Node enterEQ(final BinaryNode binaryNode) {
+ public boolean enterEQ(final BinaryNode binaryNode) {
return enterCmp(binaryNode, Condition.EQ);
}
@Override
- public Node enterEQ_STRICT(final BinaryNode binaryNode) {
+ public boolean enterEQ_STRICT(final BinaryNode binaryNode) {
return enterCmp(binaryNode, Condition.EQ);
}
@Override
- public Node enterGE(final BinaryNode binaryNode) {
+ public boolean enterGE(final BinaryNode binaryNode) {
return enterCmp(binaryNode, Condition.GE);
}
@Override
- public Node enterGT(final BinaryNode binaryNode) {
+ public boolean enterGT(final BinaryNode binaryNode) {
return enterCmp(binaryNode, Condition.GT);
}
@Override
- public Node enterLE(final BinaryNode binaryNode) {
+ public boolean enterLE(final BinaryNode binaryNode) {
return enterCmp(binaryNode, Condition.LE);
}
@Override
- public Node enterLT(final BinaryNode binaryNode) {
+ public boolean enterLT(final BinaryNode binaryNode) {
return enterCmp(binaryNode, Condition.LT);
}
@Override
- public Node enterMOD(final BinaryNode binaryNode) {
+ public boolean enterMOD(final BinaryNode binaryNode) {
new BinaryArith() {
@Override
protected void op() {
@@ -2807,11 +2848,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}.evaluate(binaryNode);
- return null;
+ return false;
}
@Override
- public Node enterMUL(final BinaryNode binaryNode) {
+ public boolean enterMUL(final BinaryNode binaryNode) {
new BinaryArith() {
@Override
protected void op() {
@@ -2819,26 +2860,26 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}.evaluate(binaryNode);
- return null;
+ return false;
}
@Override
- public Node enterNE(final BinaryNode binaryNode) {
+ public boolean enterNE(final BinaryNode binaryNode) {
return enterCmp(binaryNode, Condition.NE);
}
@Override
- public Node enterNE_STRICT(final BinaryNode binaryNode) {
+ public boolean enterNE_STRICT(final BinaryNode binaryNode) {
return enterCmp(binaryNode, Condition.NE);
}
@Override
- public Node enterOR(final BinaryNode binaryNode) {
+ public boolean enterOR(final BinaryNode binaryNode) {
return enterAND_OR(binaryNode);
}
@Override
- public Node enterSAR(final BinaryNode binaryNode) {
+ public boolean enterSAR(final BinaryNode binaryNode) {
new BinaryArith() {
@Override
protected void op() {
@@ -2846,11 +2887,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}.evaluate(binaryNode);
- return null;
+ return false;
}
@Override
- public Node enterSHL(final BinaryNode binaryNode) {
+ public boolean enterSHL(final BinaryNode binaryNode) {
new BinaryArith() {
@Override
protected void op() {
@@ -2858,24 +2899,24 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}.evaluate(binaryNode);
- return null;
+ return false;
}
@Override
- public Node enterSHR(final BinaryNode binaryNode) {
+ public boolean enterSHR(final BinaryNode binaryNode) {
new BinaryArith() {
@Override
protected void op() {
method.shr();
- method.convert(Type.LONG).load(0xffff_ffffL).and();
+ method.convert(Type.LONG).load(JSType.MAX_UINT).and();
}
}.evaluate(binaryNode);
- return null;
+ return false;
}
@Override
- public Node enterSUB(final BinaryNode binaryNode) {
+ public boolean enterSUB(final BinaryNode binaryNode) {
new BinaryArith() {
@Override
protected void op() {
@@ -2883,18 +2924,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
}.evaluate(binaryNode);
- return null;
+ return false;
}
- /*
- * Ternary visits.
- */
@Override
- public Node enterTernaryNode(final TernaryNode ternaryNode) {
- if (ternaryNode.testResolved()) {
- return null;
- }
-
+ public boolean enterTernaryNode(final TernaryNode ternaryNode) {
final Node lhs = ternaryNode.lhs();
final Node rhs = ternaryNode.rhs();
final Node third = ternaryNode.third();
@@ -2926,7 +2960,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.label(exitLabel);
method.store(symbol);
- return null;
+ return false;
}
/**
@@ -2955,7 +2989,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
if (scopeCalls.containsKey(scopeCall)) {
return scopeCalls.get(scopeCall);
}
- scopeCall.setClassAndName(getCurrentCompileUnit(), getCurrentFunctionNode().uniqueName("scopeCall"));
+ scopeCall.setClassAndName(unit, getLexicalContext().getCurrentFunction().uniqueName("scopeCall"));
scopeCalls.put(scopeCall, scopeCall);
return scopeCall;
}
@@ -2974,7 +3008,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
if (scopeCalls.containsKey(scopeCall)) {
return scopeCalls.get(scopeCall);
}
- scopeCall.setClassAndName(getCurrentCompileUnit(), getCurrentFunctionNode().uniqueName("scopeCall"));
+ scopeCall.setClassAndName(unit, getLexicalContext().getCurrentFunction().uniqueName("scopeCall"));
scopeCalls.put(scopeCall, scopeCall);
return scopeCall;
}
@@ -3037,9 +3071,6 @@ final class CodeGenerator extends NodeOperatorVisitor {
/** The target node to store to, e.g. x */
private final Node target;
- /** Should the result always be discarded, no matter what? */
- private final boolean alwaysDiscard;
-
/** How deep on the stack do the arguments go if this generates an indy call */
private int depth;
@@ -3055,7 +3086,6 @@ final class CodeGenerator extends NodeOperatorVisitor {
protected Store(final T assignNode, final Node target) {
this.assignNode = assignNode;
this.target = target;
- this.alwaysDiscard = assignNode == target;
}
/**
@@ -3077,21 +3107,21 @@ final class CodeGenerator extends NodeOperatorVisitor {
private void prologue() {
final Symbol targetSymbol = target.getSymbol();
- final Symbol scopeSymbol = getCurrentFunctionNode().getScopeNode().getSymbol();
+ final Symbol scopeSymbol = getLexicalContext().getCurrentFunction().compilerConstant(SCOPE);
/**
* This loads the parts of the target, e.g base and index. they are kept
* on the stack throughout the store and used at the end to execute it
*/
- target.accept(new NodeVisitor(getCurrentCompileUnit(), method) {
+ target.accept(new NodeVisitor() {
@Override
- public Node enterIdentNode(final IdentNode node) {
+ public boolean enterIdentNode(final IdentNode node) {
if (targetSymbol.isScope()) {
method.load(scopeSymbol);
depth++;
}
- return null;
+ return false;
}
private void enterBaseNode() {
@@ -3109,13 +3139,13 @@ final class CodeGenerator extends NodeOperatorVisitor {
}
@Override
- public Node enterAccessNode(final AccessNode node) {
+ public boolean enterAccessNode(final AccessNode node) {
enterBaseNode();
- return null;
+ return false;
}
@Override
- public Node enterIndexNode(final IndexNode node) {
+ public boolean enterIndexNode(final IndexNode node) {
enterBaseNode();
final Node index = node.getIndex();
@@ -3131,14 +3161,14 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.dup(1);
}
- return null;
+ return false;
}
});
}
private Symbol quickSymbol(final Type type) {
- return quickSymbol(type, QUICK_PREFIX.tag());
+ return quickSymbol(type, QUICK_PREFIX.symbolName());
}
/**
@@ -3151,22 +3181,28 @@ final class CodeGenerator extends NodeOperatorVisitor {
* @return the quick symbol
*/
private Symbol quickSymbol(final Type type, final String prefix) {
- final String name = getCurrentFunctionNode().uniqueName(prefix);
- final Symbol symbol = new Symbol(name, IS_TEMP | IS_INTERNAL, null, null);
+ final String name = getLexicalContext().getCurrentFunction().uniqueName(prefix);
+ final Symbol symbol = new Symbol(name, IS_TEMP | IS_INTERNAL);
symbol.setType(type);
- symbol.setSlot(getCurrentBlock().getFrame().getSlotCount());
+ final int quickSlot = nextFreeSlots[nextFreeSlotsSize - 1];
+ nextFreeSlots[nextFreeSlotsSize - 1] = quickSlot + symbol.slotCount();
+ symbol.setSlot(quickSlot);
return symbol;
}
// store the result that "lives on" after the op, e.g. "i" in i++ postfix.
protected void storeNonDiscard() {
- if (assignNode.shouldDiscard() || alwaysDiscard) {
- assignNode.setDiscard(false);
+ if (discard.peek() == assignNode) {
+ assert assignNode.isAssignment();
+ discard.pop();
return;
}
+ //System.err.println("Store with out discard that shouldn't just return " + assignNode);
+ //new Throwable().printStackTrace();
+
final Symbol symbol = assignNode.getSymbol();
if (symbol.hasSlot()) {
method.dup().store(symbol);
@@ -3191,22 +3227,22 @@ final class CodeGenerator extends NodeOperatorVisitor {
*/
method.convert(target.getType());
- target.accept(new NodeVisitor(getCurrentCompileUnit(), method) {
+ target.accept(new NodeVisitor() {
@Override
- protected Node enterDefault(Node node) {
+ protected boolean enterDefault(Node node) {
throw new AssertionError("Unexpected node " + node + " in store epilogue");
}
@Override
- public Node enterUnaryNode(final UnaryNode node) {
- if(node.tokenType() == TokenType.CONVERT && node.getSymbol() != null) {
+ public boolean enterUnaryNode(final UnaryNode node) {
+ if (node.tokenType() == TokenType.CONVERT && node.getSymbol() != null) {
method.convert(node.rhs().getType());
}
- return node;
+ return true;
}
@Override
- public Node enterIdentNode(final IdentNode node) {
+ public boolean enterIdentNode(final IdentNode node) {
final Symbol symbol = node.getSymbol();
assert symbol != null;
if (symbol.isScope()) {
@@ -3218,20 +3254,20 @@ final class CodeGenerator extends NodeOperatorVisitor {
} else {
method.store(symbol);
}
- return null;
+ return false;
}
@Override
- public Node enterAccessNode(final AccessNode node) {
+ public boolean enterAccessNode(final AccessNode node) {
method.dynamicSet(node.getProperty().getType(), node.getProperty().getName(), getCallSiteFlags());
- return null;
+ return false;
}
@Override
- public Node enterIndexNode(final IndexNode node) {
+ public boolean enterIndexNode(final IndexNode node) {
method.dynamicSetIndex(getCallSiteFlags());
- return null;
+ return false;
}
});
@@ -3250,10 +3286,23 @@ final class CodeGenerator extends NodeOperatorVisitor {
method.load(quick);
}
}
-
}
- private void newFunctionObject(final FunctionNode functionNode) {
+ private void newFunctionObject(final FunctionNode functionNode, final FunctionNode originalFunctionNode) {
+ final LexicalContext lc = getLexicalContext();
+ assert lc.peek() == functionNode;
+ // We don't emit a ScriptFunction on stack for:
+ // 1. the outermost compiled function (as there's no code being generated in its outer context that'd need it
+ // as a callee), and
+ // 2. for functions that are immediately called upon definition and they don't need a callee, e.g. (function(){})().
+ // Such immediately-called functions are invoked using INVOKESTATIC (see enterFunctionNode() of the embedded
+ // visitor of enterCallNode() for details), and if they don't need a callee, they don't have it on their
+ // static method's parameter list.
+ if(lc.getOutermostFunction() == functionNode ||
+ (!functionNode.needsCallee()) && lc.isFunctionDefinedInCurrentCall(originalFunctionNode)) {
+ return;
+ }
+
final boolean isLazy = functionNode.isLazy();
new ObjectCreator(this, new ArrayList<String>(), new ArrayList<Symbol>(), false, false) {
@@ -3265,7 +3314,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
loadConstant(new RecompilableScriptFunctionData(functionNode, compiler.getCodeInstaller(), Compiler.binaryName(getClassName()), makeMap()));
if (isLazy || functionNode.needsParentScope()) {
- m.loadScope();
+ m.loadCompilerConstant(SCOPE);
} else {
m.loadNull();
}
diff --git a/src/jdk/nashorn/internal/codegen/CompilationPhase.java b/src/jdk/nashorn/internal/codegen/CompilationPhase.java
index 8b905f87..884a68bf 100644
--- a/src/jdk/nashorn/internal/codegen/CompilationPhase.java
+++ b/src/jdk/nashorn/internal/codegen/CompilationPhase.java
@@ -15,6 +15,7 @@ import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;
import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.CallNode;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
@@ -29,8 +30,8 @@ import jdk.nashorn.internal.runtime.ScriptEnvironment;
import jdk.nashorn.internal.runtime.Timing;
/**
- * A compilation phase is a step in the processes of turning a JavaScript FunctionNode
- * into bytecode. It has an optional return value.
+ * A compilation phase is a step in the processes of turning a JavaScript
+ * FunctionNode into bytecode. It has an optional return value.
*/
enum CompilationPhase {
@@ -41,77 +42,87 @@ enum CompilationPhase {
*/
LAZY_INITIALIZATION_PHASE(EnumSet.of(INITIALIZED, PARSED)) {
@Override
- void transform(final Compiler compiler, final FunctionNode fn) {
+ FunctionNode transform(final Compiler compiler, final FunctionNode fn0) {
/*
- * For lazy compilation, we might be given a node previously marked as lazy
- * to compile as the outermost function node in the compiler. Unmark it
- * so it can be compiled and not cause recursion. Make sure the return type
- * is unknown so it can be correctly deduced. Return types are always
- * Objects in Lazy nodes as we haven't got a change to generate code for
- * them and decude its parameter specialization
+ * For lazy compilation, we might be given a node previously marked
+ * as lazy to compile as the outermost function node in the
+ * compiler. Unmark it so it can be compiled and not cause
+ * recursion. Make sure the return type is unknown so it can be
+ * correctly deduced. Return types are always Objects in Lazy nodes
+ * as we haven't got a change to generate code for them and decude
+ * its parameter specialization
*
- * TODO: in the future specializations from a callsite will be passed here
- * so we can generate a better non-lazy version of a function from a trampoline
+ * TODO: in the future specializations from a callsite will be
+ * passed here so we can generate a better non-lazy version of a
+ * function from a trampoline
*/
- //compute the signature from the callsite - todo - now just clone object params
+
final FunctionNode outermostFunctionNode = compiler.getFunctionNode();
- outermostFunctionNode.setIsLazy(false);
- outermostFunctionNode.setReturnType(Type.UNKNOWN);
+ assert outermostFunctionNode == fn0;
final Set<FunctionNode> neverLazy = new HashSet<>();
- final Set<FunctionNode> lazy = new HashSet<>();
+ final Set<FunctionNode> lazy = new HashSet<>();
+
+ FunctionNode newFunctionNode = outermostFunctionNode;
- outermostFunctionNode.accept(new NodeVisitor() {
- // self references are done with invokestatic and thus cannot have trampolines - never lazy
+ newFunctionNode = (FunctionNode)newFunctionNode.accept(new NodeVisitor() {
+ // self references are done with invokestatic and thus cannot
+ // have trampolines - never lazy
@Override
- public Node enterCallNode(final CallNode node) {
+ public boolean enterCallNode(final CallNode node) {
final Node callee = node.getFunction();
if (callee instanceof FunctionNode) {
neverLazy.add(((FunctionNode)callee));
- return null;
+ return false;
}
- return node;
+ return true;
}
+ //any function that isn't the outermost one must be marked as lazy
@Override
- public Node enterFunctionNode(final FunctionNode node) {
- if (node == outermostFunctionNode) {
- return node;
- }
+ public boolean enterFunctionNode(final FunctionNode node) {
assert compiler.isLazy();
lazy.add(node);
-
- //also needs scope, potentially needs arguments etc etc
-
- return node;
+ return true;
}
});
+ //at least one method is non lazy - the outermost one
+ neverLazy.add(newFunctionNode);
+
for (final FunctionNode node : neverLazy) {
- Compiler.LOG.fine("Marking " + node.getName() + " as non lazy, as it's a self reference");
- node.setIsLazy(false);
+ Compiler.LOG.fine(
+ "Marking ",
+ node.getName(),
+ " as non lazy, as it's a self reference");
lazy.remove(node);
}
- outermostFunctionNode.accept(new NodeOperatorVisitor() {
- private final LexicalContext lexicalContext = new LexicalContext();
+ newFunctionNode = (FunctionNode)newFunctionNode.accept(new NodeOperatorVisitor() {
@Override
- public Node enterFunctionNode(FunctionNode functionNode) {
- lexicalContext.push(functionNode);
- if(lazy.contains(functionNode)) {
- Compiler.LOG.fine("Marking " + functionNode.getName() + " as lazy");
- functionNode.setIsLazy(true);
- lexicalContext.getParentFunction(functionNode).setHasLazyChildren();
+ public Node leaveFunctionNode(final FunctionNode functionNode) {
+ final LexicalContext lc = getLexicalContext();
+ if (lazy.contains(functionNode)) {
+ Compiler.LOG.fine(
+ "Marking ",
+ functionNode.getName(),
+ " as lazy");
+ final FunctionNode parent = lc.getParentFunction(functionNode);
+ assert parent != null;
+ lc.setFlag(parent, FunctionNode.HAS_LAZY_CHILDREN);
+ lc.setFlag(parent.getBody(), Block.NEEDS_SCOPE);
+ lc.setFlag(functionNode, FunctionNode.IS_LAZY);
+ return functionNode;
}
- return functionNode;
- }
- @Override
- public Node leaveFunctionNode(FunctionNode functionNode) {
- lexicalContext.pop(functionNode);
- return functionNode;
+
+ return functionNode.
+ clearFlag(lc, FunctionNode.IS_LAZY).
+ setReturnType(lc, Type.UNKNOWN);
}
});
+
+ return newFunctionNode;
}
@Override
@@ -121,13 +132,13 @@ enum CompilationPhase {
},
/*
- * Constant folding pass
- * Simple constant folding that will make elementary constructs go away
+ * Constant folding pass Simple constant folding that will make elementary
+ * constructs go away
*/
CONSTANT_FOLDING_PHASE(EnumSet.of(INITIALIZED, PARSED)) {
@Override
- void transform(final Compiler compiler, final FunctionNode fn) {
- fn.accept(new FoldConstants());
+ FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
+ return (FunctionNode)fn.accept(new FoldConstants());
}
@Override
@@ -137,18 +148,16 @@ enum CompilationPhase {
},
/*
- * Lower (Control flow pass)
- * Finalizes the control flow. Clones blocks for finally constructs and
- * similar things. Establishes termination criteria for nodes
- * Guarantee return instructions to method making sure control flow
- * cannot fall off the end. Replacing high level nodes with lower such
- * as runtime nodes where applicable.
- *
+ * Lower (Control flow pass) Finalizes the control flow. Clones blocks for
+ * finally constructs and similar things. Establishes termination criteria
+ * for nodes Guarantee return instructions to method making sure control
+ * flow cannot fall off the end. Replacing high level nodes with lower such
+ * as runtime nodes where applicable.
*/
LOWERING_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED)) {
@Override
- void transform(final Compiler compiler, final FunctionNode fn) {
- fn.accept(new Lower());
+ FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
+ return (FunctionNode)fn.accept(new Lower());
}
@Override
@@ -158,13 +167,27 @@ enum CompilationPhase {
},
/*
- * Attribution
- * Assign symbols and types to all nodes.
+ * Attribution Assign symbols and types to all nodes.
*/
ATTRIBUTION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED)) {
@Override
- void transform(final Compiler compiler, final FunctionNode fn) {
- fn.accept(new Attr());
+ FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
+ return (FunctionNode)initReturnTypes(fn).accept(new Attr());
+ }
+
+ /**
+ * Pessimistically set all lazy functions' return types to Object
+ * @param functionNode node where to start iterating
+ */
+ private FunctionNode initReturnTypes(final FunctionNode functionNode) {
+ return (FunctionNode)functionNode.accept(new NodeVisitor() {
+ @Override
+ public Node leaveFunctionNode(final FunctionNode node) {
+ return node.isLazy() ?
+ node.setReturnType(getLexicalContext(), Type.OBJECT) :
+ node.setReturnType(getLexicalContext(), Type.UNKNOWN);
+ }
+ });
}
@Override
@@ -174,25 +197,35 @@ enum CompilationPhase {
},
/*
- * Splitter
- * Split the AST into several compile units based on a size heuristic
- * Splitter needs attributed AST for weight calculations (e.g. is
- * a + b a ScriptRuntime.ADD with call overhead or a dadd with much
- * less). Split IR can lead to scope information being changed.
+ * Splitter Split the AST into several compile units based on a size
+ * heuristic Splitter needs attributed AST for weight calculations (e.g. is
+ * a + b a ScriptRuntime.ADD with call overhead or a dadd with much less).
+ * Split IR can lead to scope information being changed.
*/
SPLITTING_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR)) {
@Override
- void transform(final Compiler compiler, final FunctionNode fn) {
+ FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
final CompileUnit outermostCompileUnit = compiler.addCompileUnit(compiler.firstCompileUnitName());
- new Splitter(compiler, fn, outermostCompileUnit).split();
+ final FunctionNode newFunctionNode = new Splitter(compiler, fn, outermostCompileUnit).split(fn);
- assert fn.getCompileUnit() == outermostCompileUnit : "fn.compileUnit (" + fn.getCompileUnit() + ") != " + outermostCompileUnit;
+ assert newFunctionNode.getCompileUnit() == outermostCompileUnit : "fn.compileUnit (" + newFunctionNode.getCompileUnit() + ") != " + outermostCompileUnit;
- if (fn.isStrictMode()) {
+ if (newFunctionNode.isStrict()) {
assert compiler.getStrictMode();
compiler.setStrictMode(true);
}
+
+ /*
+ newFunctionNode.accept(new NodeVisitor() {
+ @Override
+ public boolean enterFunctionNode(final FunctionNode functionNode) {
+ assert functionNode.getCompileUnit() != null : functionNode.getName() + " " + Debug.id(functionNode) + " has no compile unit";
+ return true;
+ }
+ });*/
+
+ return newFunctionNode;
}
@Override
@@ -204,30 +237,32 @@ enum CompilationPhase {
/*
* FinalizeTypes
*
- * This pass finalizes the types for nodes. If Attr created wider types than
- * known during the first pass, convert nodes are inserted or access nodes
- * are specialized where scope accesses.
+ * This pass finalizes the types for nodes. If Attr created wider types than
+ * known during the first pass, convert nodes are inserted or access nodes
+ * are specialized where scope accesses.
*
- * Runtime nodes may be removed and primitivized or reintroduced depending
- * on information that was established in Attr.
+ * Runtime nodes may be removed and primitivized or reintroduced depending
+ * on information that was established in Attr.
*
* Contract: all variables must have slot assignments and scope assignments
* before type finalization.
*/
TYPE_FINALIZATION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR, SPLIT)) {
@Override
- void transform(final Compiler compiler, final FunctionNode fn) {
+ FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
final ScriptEnvironment env = compiler.getEnv();
- fn.accept(new FinalizeTypes());
+ final FunctionNode newFunctionNode = (FunctionNode)fn.accept(new FinalizeTypes());
if (env._print_lower_ast) {
- env.getErr().println(new ASTWriter(fn));
+ env.getErr().println(new ASTWriter(newFunctionNode));
}
if (env._print_lower_parse) {
- env.getErr().println(new PrintVisitor(fn));
- }
+ env.getErr().println(new PrintVisitor(newFunctionNode));
+ }
+
+ return newFunctionNode;
}
@Override
@@ -239,31 +274,21 @@ enum CompilationPhase {
/*
* Bytecode generation:
*
- * Generate the byte code class(es) resulting from the compiled FunctionNode
+ * Generate the byte code class(es) resulting from the compiled FunctionNode
*/
BYTECODE_GENERATION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR, SPLIT, FINALIZED)) {
@Override
- void transform(final Compiler compiler, final FunctionNode fn) {
+ FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
final ScriptEnvironment env = compiler.getEnv();
+ FunctionNode newFunctionNode = fn;
try {
final CodeGenerator codegen = new CodeGenerator(compiler);
- fn.accept(codegen);
+ newFunctionNode = (FunctionNode)newFunctionNode.accept(codegen);
codegen.generateScopeCalls();
- fn.accept(new NodeOperatorVisitor() {
- @Override
- public Node enterFunctionNode(FunctionNode functionNode) {
- if(functionNode.isLazy()) {
- functionNode.resetResolved();
- return null;
- }
- return fn;
- }
- });
-
} catch (final VerifyError e) {
if (env._verify_code || env._print_code) {
- env.getErr().println(e.getClass().getSimpleName() + ": " + e.getMessage());
+ env.getErr().println(e.getClass().getSimpleName() + ": " + e.getMessage());
if (env._dump_on_error) {
e.printStackTrace(env.getErr());
}
@@ -283,25 +308,25 @@ enum CompilationPhase {
compiler.addClass(className, bytecode);
- //should could be printed to stderr for generate class?
+ // should could be printed to stderr for generate class?
if (env._print_code) {
final StringBuilder sb = new StringBuilder();
- sb.append("class: " + className).
- append('\n').
- append(ClassEmitter.disassemble(bytecode)).
- append("=====");
+ sb.append("class: " + className).append('\n')
+ .append(ClassEmitter.disassemble(bytecode))
+ .append("=====");
env.getErr().println(sb);
}
- //should we verify the generated code?
+ // should we verify the generated code?
if (env._verify_code) {
compiler.getCodeInstaller().verify(bytecode);
}
- //should code be dumped to disk - only valid in compile_only mode?
+ // should code be dumped to disk - only valid in compile_only
+ // mode?
if (env._dest_dir != null && env._compile_only) {
final String fileName = className.replace('.', File.separatorChar) + ".class";
- final int index = fileName.lastIndexOf(File.separatorChar);
+ final int index = fileName.lastIndexOf(File.separatorChar);
if (index != -1) {
final File dir = new File(fileName.substring(0, index));
@@ -314,11 +339,18 @@ enum CompilationPhase {
fos.write(bytecode);
}
} catch (final IOException e) {
- Compiler.LOG.warning("Skipping class dump for " + className + ": " + ECMAErrors.getMessage("io.error.cant.write", dir.toString()));
+ Compiler.LOG.warning("Skipping class dump for ",
+ className,
+ ": ",
+ ECMAErrors.getMessage(
+ "io.error.cant.write",
+ dir.toString()));
}
}
}
}
+
+ return newFunctionNode;
}
@Override
@@ -340,26 +372,28 @@ enum CompilationPhase {
return functionNode.hasState(pre);
}
- protected void begin(final FunctionNode functionNode) {
+ protected FunctionNode begin(final FunctionNode functionNode) {
if (pre != null) {
- //check that everything in pre is present
+ // check that everything in pre is present
for (final CompilationState state : pre) {
assert functionNode.hasState(state);
}
- //check that nothing else is present
+ // check that nothing else is present
for (final CompilationState state : CompilationState.values()) {
assert !(functionNode.hasState(state) && !pre.contains(state));
}
}
startTime = System.currentTimeMillis();
+ return functionNode;
}
- protected void end(final FunctionNode functionNode) {
+ protected FunctionNode end(final FunctionNode functionNode) {
endTime = System.currentTimeMillis();
Timing.accumulateTime(toString(), endTime - startTime);
isFinished = true;
+ return functionNode;
}
boolean isFinished() {
@@ -374,15 +408,13 @@ enum CompilationPhase {
return endTime;
}
- abstract void transform(final Compiler compiler, final FunctionNode functionNode) throws CompilationException;
+ abstract FunctionNode transform(final Compiler compiler, final FunctionNode functionNode) throws CompilationException;
- final void apply(final Compiler compiler, final FunctionNode functionNode) throws CompilationException {
+ final FunctionNode apply(final Compiler compiler, final FunctionNode functionNode) throws CompilationException {
if (!isApplicable(functionNode)) {
- throw new CompilationException("compile phase not applicable: " + this);
+ throw new CompilationException("compile phase not applicable: " + this + " to " + functionNode.getName() + " state=" + functionNode.getState());
}
- begin(functionNode);
- transform(compiler, functionNode);
- end(functionNode);
+ return end(transform(compiler, begin(functionNode)));
}
}
diff --git a/src/jdk/nashorn/internal/codegen/Compiler.java b/src/jdk/nashorn/internal/codegen/Compiler.java
index 397f39a5..b1e47d02 100644
--- a/src/jdk/nashorn/internal/codegen/Compiler.java
+++ b/src/jdk/nashorn/internal/codegen/Compiler.java
@@ -25,12 +25,16 @@
package jdk.nashorn.internal.codegen;
+import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
+import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
import static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS;
import static jdk.nashorn.internal.codegen.CompilerConstants.DEFAULT_SCRIPT_NAME;
import static jdk.nashorn.internal.codegen.CompilerConstants.LAZY;
+import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
import static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE;
import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
+import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
import java.io.File;
import java.lang.reflect.Field;
@@ -46,13 +50,12 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.logging.Level;
+
import jdk.internal.dynalink.support.NameCodec;
import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
-import jdk.nashorn.internal.ir.Node;
-import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.CodeInstaller;
import jdk.nashorn.internal.runtime.DebugLogger;
import jdk.nashorn.internal.runtime.ScriptEnvironment;
@@ -80,8 +83,6 @@ public final class Compiler {
private final ConstantData constantData;
- private final FunctionNode functionNode;
-
private final CompilationSequence sequence;
private final ScriptEnvironment env;
@@ -90,6 +91,8 @@ public final class Compiler {
private boolean strict;
+ private FunctionNode functionNode;
+
private CodeInstaller<ScriptEnvironment> installer;
/** logger for compiler, trampolines, splits and related code generation events
@@ -103,8 +106,12 @@ public final class Compiler {
* during a compile.
*/
private static String[] RESERVED_NAMES = {
- SCOPE.tag(),
- THIS.tag()
+ SCOPE.symbolName(),
+ THIS.symbolName(),
+ RETURN.symbolName(),
+ CALLEE.symbolName(),
+ VARARGS.symbolName(),
+ ARGUMENTS.symbolName()
};
/**
@@ -186,7 +193,7 @@ public final class Compiler {
private static String lazyTag(final FunctionNode functionNode) {
if (functionNode.isLazy()) {
- return '$' + LAZY.tag() + '$' + functionNode.getName();
+ return '$' + LAZY.symbolName() + '$' + functionNode.getName();
}
return "";
}
@@ -205,13 +212,13 @@ public final class Compiler {
this.functionNode = functionNode;
this.sequence = sequence;
this.installer = installer;
- this.strict = strict || functionNode.isStrictMode();
+ this.strict = strict || functionNode.isStrict();
this.constantData = new ConstantData();
this.compileUnits = new HashSet<>();
this.bytecode = new HashMap<>();
final StringBuilder sb = new StringBuilder();
- sb.append(functionNode.uniqueName(DEFAULT_SCRIPT_NAME.tag() + lazyTag(functionNode))).
+ sb.append(functionNode.uniqueName(DEFAULT_SCRIPT_NAME.symbolName() + lazyTag(functionNode))).
append('$').
append(safeSourceName(functionNode.getSource()));
@@ -253,9 +260,9 @@ public final class Compiler {
* Execute the compilation this Compiler was created with
* @params param types if known, for specialization
* @throws CompilationException if something goes wrong
- * @return this compiler, for possible chaining
+ * @return function node that results from code transforms
*/
- public Compiler compile() throws CompilationException {
+ public FunctionNode compile() throws CompilationException {
return compile(null);
}
@@ -263,9 +270,9 @@ public final class Compiler {
* Execute the compilation this Compiler was created with
* @param paramTypes param types if known, for specialization
* @throws CompilationException if something goes wrong
- * @return this compiler, for possible chaining
+ * @return function node that results from code transforms
*/
- public Compiler compile(final Class<?> paramTypes) throws CompilationException {
+ public FunctionNode compile(final Class<?> paramTypes) throws CompilationException {
for (final String reservedName : RESERVED_NAMES) {
functionNode.uniqueName(reservedName);
}
@@ -276,7 +283,7 @@ public final class Compiler {
long time = 0L;
for (final CompilationPhase phase : sequence) {
- phase.apply(this, functionNode);
+ this.functionNode = phase.apply(this, functionNode);
final long duration = Timing.isEnabled() ? (phase.getEndTime() - phase.getStartTime()) : 0L;
time += duration;
@@ -295,7 +302,7 @@ public final class Compiler {
append(" ms ");
}
- LOG.fine(sb.toString());
+ LOG.fine(sb);
}
}
@@ -311,14 +318,14 @@ public final class Compiler {
append(" ms");
}
- LOG.info(sb.toString());
+ LOG.info(sb);
}
- return this;
+ return functionNode;
}
private Class<?> install(final String className, final byte[] code) {
- LOG.fine("Installing class " + className);
+ LOG.fine("Installing class ", className);
final Class<?> clazz = installer.install(Compiler.binaryName(className), code);
@@ -330,8 +337,8 @@ public final class Compiler {
@Override
public Void run() throws Exception {
//use reflection to write source and constants table to installed classes
- final Field sourceField = clazz.getDeclaredField(SOURCE.tag());
- final Field constantsField = clazz.getDeclaredField(CONSTANTS.tag());
+ final Field sourceField = clazz.getDeclaredField(SOURCE.symbolName());
+ final Field constantsField = clazz.getDeclaredField(CONSTANTS.symbolName());
sourceField.setAccessible(true);
constantsField.setAccessible(true);
sourceField.set(null, source);
@@ -380,17 +387,6 @@ public final class Compiler {
unit.setCode(installedClasses.get(unit.getUnitClassName()));
}
- functionNode.accept(new NodeVisitor() {
- @Override
- public Node enterFunctionNode(final FunctionNode node) {
- if (node.isLazy()) {
- return null;
- }
- node.setState(CompilationState.INSTALLED);
- return node;
- }
- });
-
final StringBuilder sb;
if (LOG.isEnabled()) {
sb = new StringBuilder();
@@ -416,7 +412,7 @@ public final class Compiler {
}
if (sb != null) {
- LOG.info(sb.toString());
+ LOG.info(sb);
}
return rootClass;
@@ -495,7 +491,7 @@ public final class Compiler {
private CompileUnit addCompileUnit(final String unitClassName, final long initialWeight) {
final CompileUnit compileUnit = initCompileUnit(unitClassName, initialWeight);
compileUnits.add(compileUnit);
- LOG.fine("Added compile unit " + compileUnit);
+ LOG.fine("Added compile unit ", compileUnit);
return compileUnit;
}
diff --git a/src/jdk/nashorn/internal/codegen/CompilerConstants.java b/src/jdk/nashorn/internal/codegen/CompilerConstants.java
index 1d978d96..dfc9198c 100644
--- a/src/jdk/nashorn/internal/codegen/CompilerConstants.java
+++ b/src/jdk/nashorn/internal/codegen/CompilerConstants.java
@@ -52,9 +52,6 @@ public enum CompilerConstants {
/** lazy prefix for classes of jitted methods */
LAZY("Lazy"),
- /** leaf tag used for functions that require no scope */
- LEAF("__leaf__"),
-
/** constructor name */
INIT("<init>"),
@@ -90,55 +87,55 @@ public enum CompilerConstants {
THIS("this"),
/** this debugger symbol */
- THIS_DEBUGGER("__this__"),
+ THIS_DEBUGGER(":this"),
/** scope name, type and slot */
- SCOPE("__scope__", ScriptObject.class, 2),
+ SCOPE(":scope", ScriptObject.class, 2),
/** the return value variable name were intermediate results are stored for scripts */
- SCRIPT_RETURN("__return__"),
+ RETURN(":return"),
/** the callee value variable when necessary */
- CALLEE("__callee__", ScriptFunction.class),
+ CALLEE(":callee", ScriptFunction.class),
/** the varargs variable when necessary */
- VARARGS("__varargs__"),
+ VARARGS(":varargs"),
/** the arguments vector when necessary and the slot */
ARGUMENTS("arguments", Object.class, 2),
/** prefix for iterators for for (x in ...) */
- ITERATOR_PREFIX("$iter"),
+ ITERATOR_PREFIX(":iter"),
/** prefix for tag variable used for switch evaluation */
- SWITCH_TAG_PREFIX("$tag"),
+ SWITCH_TAG_PREFIX(":tag"),
/** prefix for all exceptions */
- EXCEPTION_PREFIX("$exception"),
+ EXCEPTION_PREFIX(":exception"),
/** prefix for quick slots generated in Store */
- QUICK_PREFIX("$quick"),
+ QUICK_PREFIX(":quick"),
/** prefix for temporary variables */
- TEMP_PREFIX("$temp"),
+ TEMP_PREFIX(":temp"),
/** prefix for literals */
- LITERAL_PREFIX("$lit"),
-
- /** prefix for map */
- MAP("$map", 1),
+ LITERAL_PREFIX(":lit"),
/** prefix for regexps */
- REGEX_PREFIX("$regex"),
+ REGEX_PREFIX(":regex"),
/** "this" used in non-static Java methods; always in slot 0 */
- JAVA_THIS("this", 0),
+ JAVA_THIS(null, 0),
+
+ /** Map parameter in scope object constructors; always in slot 1 */
+ INIT_MAP(null, 1),
- /** init scope */
- INIT_SCOPE("$scope", 2),
+ /** Parent scope parameter in scope object constructors; always in slot 2 */
+ INIT_SCOPE(null, 2),
- /** init arguments */
- INIT_ARGUMENTS("$arguments", 3),
+ /** Arguments parameter in scope object constructors; in slot 3 when present */
+ INIT_ARGUMENTS(null, 3),
/** prefix for all ScriptObject subclasses with fields, @see ObjectGenerator */
JS_OBJECT_PREFIX("JO"),
@@ -167,30 +164,30 @@ public enum CompilerConstants {
/** get array suffix */
GET_ARRAY_SUFFIX("$array");
- private final String tag;
+ private final String symbolName;
private final Class<?> type;
private final int slot;
private CompilerConstants() {
- this.tag = name();
+ this.symbolName = name();
this.type = null;
this.slot = -1;
}
- private CompilerConstants(final String tag) {
- this(tag, -1);
+ private CompilerConstants(final String symbolName) {
+ this(symbolName, -1);
}
- private CompilerConstants(final String tag, final int slot) {
- this(tag, null, slot);
+ private CompilerConstants(final String symbolName, final int slot) {
+ this(symbolName, null, slot);
}
- private CompilerConstants(final String tag, final Class<?> type) {
- this(tag, type, -1);
+ private CompilerConstants(final String symbolName, final Class<?> type) {
+ this(symbolName, type, -1);
}
- private CompilerConstants(final String tag, final Class<?> type, final int slot) {
- this.tag = tag;
+ private CompilerConstants(final String symbolName, final Class<?> type, final int slot) {
+ this.symbolName = symbolName;
this.type = type;
this.slot = slot;
}
@@ -202,8 +199,8 @@ public enum CompilerConstants {
*
* @return the tag
*/
- public final String tag() {
- return tag;
+ public final String symbolName() {
+ return symbolName;
}
/**
@@ -277,7 +274,7 @@ public enum CompilerConstants {
* @return Call representing void constructor for type
*/
public static Call constructorNoLookup(final Class<?> clazz) {
- return specialCallNoLookup(clazz, INIT.tag(), void.class);
+ return specialCallNoLookup(clazz, INIT.symbolName(), void.class);
}
/**
@@ -290,7 +287,7 @@ public enum CompilerConstants {
* @return Call representing constructor for type
*/
public static Call constructorNoLookup(final String className, final Class<?>... ptypes) {
- return specialCallNoLookup(className, INIT.tag(), methodDescriptor(void.class, ptypes));
+ return specialCallNoLookup(className, INIT.symbolName(), methodDescriptor(void.class, ptypes));
}
/**
@@ -303,7 +300,7 @@ public enum CompilerConstants {
* @return Call representing constructor for type
*/
public static Call constructorNoLookup(final Class<?> clazz, final Class<?>... ptypes) {
- return specialCallNoLookup(clazz, INIT.tag(), void.class, ptypes);
+ return specialCallNoLookup(clazz, INIT.symbolName(), void.class, ptypes);
}
/**
diff --git a/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java b/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java
index 83e75cc7..57b8b384 100644
--- a/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java
+++ b/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java
@@ -26,6 +26,7 @@
package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
+import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor;
import static jdk.nashorn.internal.codegen.types.Type.OBJECT;
@@ -86,7 +87,7 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator {
* @param method the method emitter to use
*/
protected void loadScope(final MethodEmitter method) {
- method.loadScope();
+ method.loadCompilerConstant(SCOPE);
}
/**
@@ -105,7 +106,7 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator {
loadScope(method);
if (hasArguments()) {
- method.loadArguments();
+ method.loadCompilerConstant(ARGUMENTS);
method.invoke(constructorNoLookup(getClassName(), PropertyMap.class, ScriptObject.class, ARGUMENTS.type()));
} else {
method.invoke(constructorNoLookup(getClassName(), PropertyMap.class, ScriptObject.class));
diff --git a/src/jdk/nashorn/internal/codegen/FinalizeTypes.java b/src/jdk/nashorn/internal/codegen/FinalizeTypes.java
index cd185240..5f6a63dc 100644
--- a/src/jdk/nashorn/internal/codegen/FinalizeTypes.java
+++ b/src/jdk/nashorn/internal/codegen/FinalizeTypes.java
@@ -25,7 +25,12 @@
package jdk.nashorn.internal.codegen;
+import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
+import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
+
+import java.util.ArrayList;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.AccessNode;
@@ -33,10 +38,8 @@ import jdk.nashorn.internal.ir.Assignment;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.CallNode;
-import jdk.nashorn.internal.ir.CallNode.EvalArgs;
import jdk.nashorn.internal.ir.CaseNode;
import jdk.nashorn.internal.ir.CatchNode;
-import jdk.nashorn.internal.ir.DoWhileNode;
import jdk.nashorn.internal.ir.ExecuteNode;
import jdk.nashorn.internal.ir.ForNode;
import jdk.nashorn.internal.ir.FunctionNode;
@@ -85,18 +88,11 @@ final class FinalizeTypes extends NodeOperatorVisitor {
private static final DebugLogger LOG = new DebugLogger("finalize");
- private final LexicalContext lexicalContext = new LexicalContext();
-
FinalizeTypes() {
}
@Override
public Node leaveCallNode(final CallNode callNode) {
- final EvalArgs evalArgs = callNode.getEvalArgs();
- if (evalArgs != null) {
- evalArgs.setCode(evalArgs.getCode().accept(this));
- }
-
// AccessSpecializer - call return type may change the access for this location
final Node function = callNode.getFunction();
if (function instanceof FunctionNode) {
@@ -133,8 +129,7 @@ final class FinalizeTypes extends NodeOperatorVisitor {
@Override
public Node leaveNEW(final UnaryNode unaryNode) {
assert unaryNode.getSymbol() != null && unaryNode.getSymbol().getSymbolType().isObject();
- ((CallNode)unaryNode.rhs()).setIsNew();
- return unaryNode;
+ return unaryNode.setRHS(((CallNode)unaryNode.rhs()).setIsNew());
}
@Override
@@ -254,7 +249,7 @@ final class FinalizeTypes extends NodeOperatorVisitor {
@Override
public Node leaveCOMMALEFT(final BinaryNode binaryNode) {
assert binaryNode.getSymbol() != null;
- final BinaryNode newBinaryNode = (BinaryNode)binaryNode.setRHS(discard(binaryNode.rhs()));
+ final BinaryNode newBinaryNode = binaryNode.setRHS(discard(binaryNode.rhs()));
// AccessSpecializer - the type of lhs, which is the remaining value of this node may have changed
// in that case, update the node type as well
propagateType(newBinaryNode, newBinaryNode.lhs().getType());
@@ -354,41 +349,30 @@ final class FinalizeTypes extends NodeOperatorVisitor {
}
@Override
- public Node enterBlock(final Block block) {
- lexicalContext.push(block);
+ public boolean enterBlock(final Block block) {
updateSymbols(block);
- return block;
+ return true;
}
+ /*
@Override
- public Node leaveBlock(Block block) {
- lexicalContext.pop(block);
- return super.leaveBlock(block);
- }
+ public Node leaveBlock(final Block block) {
+ final LexicalContext lc = getLexicalContext();
+ return block;//.setFlag(lc, lc.getFlags(block));
+ }*/
@Override
public Node leaveCatchNode(final CatchNode catchNode) {
final Node exceptionCondition = catchNode.getExceptionCondition();
if (exceptionCondition != null) {
- catchNode.setExceptionCondition(convert(exceptionCondition, Type.BOOLEAN));
+ return catchNode.setExceptionCondition(convert(exceptionCondition, Type.BOOLEAN));
}
return catchNode;
}
@Override
- public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
- return enterWhileNode(doWhileNode);
- }
-
- @Override
- public Node leaveDoWhileNode(final DoWhileNode doWhileNode) {
- return leaveWhileNode(doWhileNode);
- }
-
- @Override
public Node leaveExecuteNode(final ExecuteNode executeNode) {
- executeNode.setExpression(discard(executeNode.getExpression()));
- return executeNode;
+ return executeNode.setExpression(discard(executeNode.getExpression()));
}
@Override
@@ -397,69 +381,54 @@ final class FinalizeTypes extends NodeOperatorVisitor {
final Node test = forNode.getTest();
final Node modify = forNode.getModify();
- if (forNode.isForIn()) {
- forNode.setModify(convert(forNode.getModify(), Type.OBJECT)); // NASHORN-400
- return forNode;
- }
+ final LexicalContext lc = getLexicalContext();
- if (init != null) {
- forNode.setInit(discard(init));
- }
-
- if (test != null) {
- forNode.setTest(convert(test, Type.BOOLEAN));
- } else {
- assert forNode.hasGoto() : "forNode " + forNode + " needs goto and is missing it in " + getCurrentFunctionNode();
- }
-
- if (modify != null) {
- forNode.setModify(discard(modify));
+ if (forNode.isForIn()) {
+ return forNode.setModify(lc, convert(forNode.getModify(), Type.OBJECT)); // NASHORN-400
}
+ assert test != null || forNode.hasGoto() : "forNode " + forNode + " needs goto and is missing it in " + getLexicalContext().getCurrentFunction();
- return forNode;
+ return forNode.
+ setInit(lc, init == null ? null : discard(init)).
+ setTest(lc, test == null ? null : convert(test, Type.BOOLEAN)).
+ setModify(lc, modify == null ? null : discard(modify));
}
@Override
- public Node enterFunctionNode(final FunctionNode functionNode) {
+ public boolean enterFunctionNode(final FunctionNode functionNode) {
if (functionNode.isLazy()) {
- return null;
+ return false;
}
- lexicalContext.push(functionNode);
// If the function doesn't need a callee, we ensure its __callee__ symbol doesn't get a slot. We can't do
// this earlier, as access to scoped variables, self symbol, etc. in previous phases can all trigger the
// need for the callee.
if (!functionNode.needsCallee()) {
- functionNode.getCalleeNode().getSymbol().setNeedsSlot(false);
+ functionNode.compilerConstant(CALLEE).setNeedsSlot(false);
}
// Similar reasoning applies to __scope__ symbol: if the function doesn't need either parent scope or its
// own scope, we ensure it doesn't get a slot, but we can't determine whether it needs a scope earlier than
// this phase.
- if (!(functionNode.needsScope() || functionNode.needsParentScope())) {
- functionNode.getScopeNode().getSymbol().setNeedsSlot(false);
+ if (!(functionNode.getBody().needsScope() || functionNode.needsParentScope())) {
+ functionNode.compilerConstant(SCOPE).setNeedsSlot(false);
}
- updateSymbols(functionNode);
- functionNode.setState(CompilationState.FINALIZED);
-
- return functionNode;
+ return true;
}
@Override
- public Node leaveFunctionNode(FunctionNode functionNode) {
- lexicalContext.pop(functionNode);
- return super.leaveFunctionNode(functionNode);
+ public Node leaveFunctionNode(final FunctionNode functionNode) {
+ return functionNode.setState(getLexicalContext(), CompilationState.FINALIZED);
}
@Override
public Node leaveIfNode(final IfNode ifNode) {
- ifNode.setTest(convert(ifNode.getTest(), Type.BOOLEAN));
- return ifNode;
+ return ifNode.setTest(convert(ifNode.getTest(), Type.BOOLEAN));
}
@SuppressWarnings("rawtypes")
@Override
- public Node enterLiteralNode(final LiteralNode literalNode) {
+ public boolean enterLiteralNode(final LiteralNode literalNode) {
if (literalNode instanceof ArrayLiteralNode) {
final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode)literalNode;
final Node[] array = arrayLiteralNode.getValue();
@@ -473,14 +442,14 @@ final class FinalizeTypes extends NodeOperatorVisitor {
}
}
- return null;
+ return false;
}
@Override
public Node leaveReturnNode(final ReturnNode returnNode) {
final Node expr = returnNode.getExpression();
if (expr != null) {
- returnNode.setExpression(convert(expr, getCurrentFunctionNode().getReturnType()));
+ return returnNode.setExpression(convert(expr, getLexicalContext().getCurrentFunction().getReturnType()));
}
return returnNode;
}
@@ -496,21 +465,24 @@ final class FinalizeTypes extends NodeOperatorVisitor {
@Override
public Node leaveSwitchNode(final SwitchNode switchNode) {
+ final boolean allInteger = switchNode.getTag().getSymbolType().isInteger();
+
+ if (allInteger) {
+ return switchNode;
+ }
+
final Node expression = switchNode.getExpression();
final List<CaseNode> cases = switchNode.getCases();
- final boolean allInteger = switchNode.getTag().getSymbolType().isInteger();
-
- if (!allInteger) {
- switchNode.setExpression(convert(expression, Type.OBJECT));
- for (final CaseNode caseNode : cases) {
- final Node test = caseNode.getTest();
- if (test != null) {
- caseNode.setTest(convert(test, Type.OBJECT));
- }
- }
+ final List<CaseNode> newCases = new ArrayList<>();
+
+ for (final CaseNode caseNode : cases) {
+ final Node test = caseNode.getTest();
+ newCases.add(test != null ? caseNode.setTest(convert(test, Type.OBJECT)) : caseNode);
}
- return switchNode;
+ return switchNode.
+ setExpression(getLexicalContext(), convert(expression, Type.OBJECT)).
+ setCases(getLexicalContext(), newCases);
}
@Override
@@ -520,8 +492,7 @@ final class FinalizeTypes extends NodeOperatorVisitor {
@Override
public Node leaveThrowNode(final ThrowNode throwNode) {
- throwNode.setExpression(convert(throwNode.getExpression(), Type.OBJECT));
- return throwNode;
+ return throwNode.setExpression(convert(throwNode.getExpression(), Type.OBJECT));
}
@Override
@@ -544,23 +515,24 @@ final class FinalizeTypes extends NodeOperatorVisitor {
public Node leaveWhileNode(final WhileNode whileNode) {
final Node test = whileNode.getTest();
if (test != null) {
- whileNode.setTest(convert(test, Type.BOOLEAN));
+ return whileNode.setTest(getLexicalContext(), convert(test, Type.BOOLEAN));
}
return whileNode;
}
@Override
public Node leaveWithNode(final WithNode withNode) {
- withNode.setExpression(convert(withNode.getExpression(), Type.OBJECT));
- return withNode;
+ return withNode.setExpression(getLexicalContext(), convert(withNode.getExpression(), Type.OBJECT));
}
private static void updateSymbolsLog(final FunctionNode functionNode, final Symbol symbol, final boolean loseSlot) {
- if (!symbol.isScope()) {
- LOG.finest("updateSymbols: " + symbol + " => scope, because all vars in " + functionNode.getName() + " are in scope");
- }
- if (loseSlot && symbol.hasSlot()) {
- LOG.finest("updateSymbols: " + symbol + " => no slot, because all vars in " + functionNode.getName() + " are in scope");
+ if (LOG.isEnabled()) {
+ if (!symbol.isScope()) {
+ LOG.finest("updateSymbols: ", symbol, " => scope, because all vars in ", functionNode.getName(), " are in scope");
+ }
+ if (loseSlot && symbol.hasSlot()) {
+ LOG.finest("updateSymbols: ", symbol, " => no slot, because all vars in ", functionNode.getName(), " are in scope");
+ }
}
}
@@ -574,29 +546,28 @@ final class FinalizeTypes extends NodeOperatorVisitor {
return; // nothing to do
}
- final FunctionNode functionNode = lexicalContext.getFunction(block);
- assert !(block instanceof FunctionNode) || functionNode == block;
+ final LexicalContext lc = getLexicalContext();
+ final FunctionNode functionNode = lc.getFunction(block);
+ final boolean allVarsInScope = functionNode.allVarsInScope();
+ final boolean isVarArg = functionNode.isVarArg();
- final List<Symbol> symbols = block.getFrame().getSymbols();
- final boolean allVarsInScope = functionNode.allVarsInScope();
- final boolean isVarArg = functionNode.isVarArg();
-
- for (final Symbol symbol : symbols) {
- if (symbol.isInternal() || symbol.isThis()) {
+ for (final Iterator<Symbol> iter = block.symbolIterator(); iter.hasNext(); ) {
+ final Symbol symbol = iter.next();
+ if (symbol.isInternal() || symbol.isThis() || symbol.isTemp()) {
continue;
}
if (symbol.isVar()) {
if (allVarsInScope || symbol.isScope()) {
updateSymbolsLog(functionNode, symbol, true);
- symbol.setIsScope();
+ Symbol.setSymbolIsScope(lc, symbol);
symbol.setNeedsSlot(false);
} else {
assert symbol.hasSlot() : symbol + " should have a slot only, no scope";
}
} else if (symbol.isParam() && (allVarsInScope || isVarArg || symbol.isScope())) {
updateSymbolsLog(functionNode, symbol, isVarArg);
- symbol.setIsScope();
+ Symbol.setSymbolIsScope(lc, symbol);
symbol.setNeedsSlot(!isVarArg);
}
}
@@ -636,11 +607,7 @@ final class FinalizeTypes extends NodeOperatorVisitor {
//fallthru
default:
if (newRuntimeNode || widest.isObject()) {
- final RuntimeNode runtimeNode = new RuntimeNode(binaryNode, request);
- if (finalized) {
- runtimeNode.setIsFinal();
- }
- return runtimeNode;
+ return new RuntimeNode(binaryNode, request).setIsFinal(finalized);
}
break;
}
@@ -667,7 +634,8 @@ final class FinalizeTypes extends NodeOperatorVisitor {
}
private Node leaveBinary(final BinaryNode binaryNode, final Type lhsType, final Type rhsType) {
- return binaryNode.setLHS(convert(binaryNode.lhs(), lhsType)).setRHS(convert(binaryNode.rhs(), rhsType));
+ Node b = binaryNode.setLHS(convert(binaryNode.lhs(), lhsType)).setRHS(convert(binaryNode.rhs(), rhsType));
+ return b;
}
/**
@@ -683,28 +651,28 @@ final class FinalizeTypes extends NodeOperatorVisitor {
node.accept(new NodeVisitor() {
private void setCanBePrimitive(final Symbol symbol) {
- LOG.info("*** can be primitive symbol " + symbol + " " + Debug.id(symbol));
+ LOG.info("*** can be primitive symbol ", symbol, " ", Debug.id(symbol));
symbol.setCanBePrimitive(to);
}
@Override
- public Node enterIdentNode(final IdentNode identNode) {
+ public boolean enterIdentNode(final IdentNode identNode) {
if (!exclude.contains(identNode)) {
setCanBePrimitive(identNode.getSymbol());
}
- return null;
+ return false;
}
@Override
- public Node enterAccessNode(final AccessNode accessNode) {
+ public boolean enterAccessNode(final AccessNode accessNode) {
setCanBePrimitive(accessNode.getProperty().getSymbol());
- return null;
+ return false;
}
@Override
- public Node enterIndexNode(final IndexNode indexNode) {
+ public boolean enterIndexNode(final IndexNode indexNode) {
exclude.add(indexNode.getBase()); //prevent array base node to be flagged as primitive, but k in a[k++] is fine
- return indexNode;
+ return true;
}
});
}
@@ -785,12 +753,12 @@ final class FinalizeTypes extends NodeOperatorVisitor {
private static <T extends Node> T setTypeOverride(final T node, final Type to) {
final Type from = node.getType();
if (!node.getType().equals(to)) {
- LOG.info("Changing call override type for '" + node + "' from " + node.getType() + " to " + to);
+ LOG.info("Changing call override type for '", node, "' from ", node.getType(), " to ", to);
if (!to.isObject() && from.isObject()) {
setCanBePrimitive(node, to);
}
}
- LOG.info("Type override for lhs in '" + node + "' => " + to);
+ LOG.info("Type override for lhs in '", node, "' => ", to);
return ((TypeOverride<T>)node).setType(to);
}
@@ -814,8 +782,8 @@ final class FinalizeTypes extends NodeOperatorVisitor {
private Node convert(final Node node, final Type to) {
assert !to.isUnknown() : "unknown type for " + node + " class=" + node.getClass();
assert node != null : "node is null";
- assert node.getSymbol() != null : "node " + node + " has no symbol!";
- assert node.tokenType() != TokenType.CONVERT : "assert convert in convert " + node + " in " + getCurrentFunctionNode();
+ assert node.getSymbol() != null : "node " + node + " " + node.getClass() + " has no symbol! " + getLexicalContext().getCurrentFunction() + " " + node.getSource();
+ assert node.tokenType() != TokenType.CONVERT : "assert convert in convert " + node + " in " + getLexicalContext().getCurrentFunction();
final Type from = node.getType();
@@ -842,23 +810,23 @@ final class FinalizeTypes extends NodeOperatorVisitor {
resultNode = new UnaryNode(node.getSource(), Token.recast(node.getToken(), TokenType.CONVERT), node);
}
- LOG.info("CONVERT('" + node + "', " + to + ") => '" + resultNode + "'");
+ LOG.info("CONVERT('", node, "', ", to, ") => '", resultNode, "'");
+ final LexicalContext lc = getLexicalContext();
//This is the only place in this file that can create new temporaries
//FinalizeTypes may not introduce ANY node that is not a conversion.
- getCurrentFunctionNode().newTemporary(getCurrentBlock().getFrame(), to, resultNode);
- resultNode.copyTerminalFlags(node);
+ lc.getCurrentFunction().ensureSymbol(lc.getCurrentBlock(), to, resultNode);
+
+ assert !node.isTerminal();
return resultNode;
}
private static Node discard(final Node node) {
- node.setDiscard(true);
-
if (node.getSymbol() != null) {
final Node discard = new UnaryNode(node.getSource(), Token.recast(node.getToken(), TokenType.DISCARD), node);
//discard never has a symbol in the discard node - then it would be a nop
- discard.copyTerminalFlags(node);
+ assert !node.isTerminal();
return discard;
}
@@ -883,7 +851,7 @@ final class FinalizeTypes extends NodeOperatorVisitor {
final Symbol symbol = node.getSymbol();
if (symbol.isTemp()) {
symbol.setTypeOverride(to);
- LOG.info("Type override for temporary in '" + node + "' => " + to);
+ LOG.info("Type override for temporary in '", node, "' => ", to);
}
}
diff --git a/src/jdk/nashorn/internal/codegen/FoldConstants.java b/src/jdk/nashorn/internal/codegen/FoldConstants.java
index fbc62644..03accb4b 100644
--- a/src/jdk/nashorn/internal/codegen/FoldConstants.java
+++ b/src/jdk/nashorn/internal/codegen/FoldConstants.java
@@ -57,7 +57,7 @@ final class FoldConstants extends NodeVisitor {
public Node leaveUnaryNode(final UnaryNode unaryNode) {
final LiteralNode<?> literalNode = new UnaryNodeConstantEvaluator(unaryNode).eval();
if (literalNode != null) {
- LOG.info("Unary constant folded " + unaryNode + " to " + literalNode);
+ LOG.info("Unary constant folded ", unaryNode, " to ", literalNode);
return literalNode;
}
return unaryNode;
@@ -67,24 +67,20 @@ final class FoldConstants extends NodeVisitor {
public Node leaveBinaryNode(final BinaryNode binaryNode) {
final LiteralNode<?> literalNode = new BinaryNodeConstantEvaluator(binaryNode).eval();
if (literalNode != null) {
- LOG.info("Binary constant folded " + binaryNode + " to " + literalNode);
+ LOG.info("Binary constant folded ", binaryNode, " to ", literalNode);
return literalNode;
}
return binaryNode;
}
@Override
- public Node enterFunctionNode(final FunctionNode functionNode) {
- if (functionNode.isLazy()) {
- return null;
- }
- return functionNode;
+ public boolean enterFunctionNode(final FunctionNode functionNode) {
+ return !functionNode.isLazy();
}
@Override
public Node leaveFunctionNode(final FunctionNode functionNode) {
- functionNode.setState(CompilationState.CONSTANT_FOLDED);
- return functionNode;
+ return functionNode.setState(getLexicalContext(), CompilationState.CONSTANT_FOLDED);
}
@Override
@@ -251,7 +247,7 @@ final class FoldConstants extends NodeVisitor {
value = lhs.getNumber() - rhs.getNumber();
break;
case SHR:
- return LiteralNode.newInstance(source, token, finish, (lhs.getInt32() >>> rhs.getInt32()) & 0xffff_ffffL);
+ return LiteralNode.newInstance(source, token, finish, (lhs.getInt32() >>> rhs.getInt32()) & JSType.MAX_UINT);
case SAR:
return LiteralNode.newInstance(source, token, finish, lhs.getInt32() >> rhs.getInt32());
case SHL:
diff --git a/src/jdk/nashorn/internal/codegen/Frame.java b/src/jdk/nashorn/internal/codegen/Frame.java
deleted file mode 100644
index b72456b2..00000000
--- a/src/jdk/nashorn/internal/codegen/Frame.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package jdk.nashorn.internal.codegen;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import jdk.nashorn.internal.ir.Symbol;
-
-/**
- * Tracks the variable area state.
- *
- */
-public final class Frame {
- /** Previous frame. */
- private Frame previous;
-
- /** Current variables. */
- private final ArrayList<Symbol> symbols;
-
- /** Number of slots in previous frame. */
- private int baseCount;
-
- /** Number of slots in this frame. */
- private int count;
-
- /**
- * Constructor.
- *
- * @param previous frame, the parent variable frame
- */
- public Frame(final Frame previous) {
- this.previous = previous;
- this.symbols = new ArrayList<>();
- this.baseCount = getBaseCount();
- this.count = 0;
- }
-
- /**
- * Copy constructor
- * @param frame
- * @param symbols
- */
- private Frame(final Frame frame, final List<Symbol> symbols) {
- this.previous = frame.getPrevious() == null ? null : new Frame(frame.getPrevious(), frame.getPrevious().getSymbols());
- this.symbols = new ArrayList<>(frame.getSymbols());
- this.baseCount = frame.getBaseCount();
- this.count = frame.getCount();
- }
-
- /**
- * Copy the frame
- *
- * @return a new frame with the identical contents
- */
- public Frame copy() {
- return new Frame(this, getSymbols());
- }
-
- /**
- * Add a new variable to the frame.
- * @param symbol Symbol representing variable.
- */
- public void addSymbol(final Symbol symbol) {
- final int slot = symbol.getSlot();
- if (slot < 0) {
- symbols.add(symbol);
- count += symbol.slotCount();
- }
- }
-
- /**
- * Realign slot numbering prior to code generation.
- * @return Number of slots in frame.
- */
- public int realign() {
- baseCount = getBaseCount();
- count = 0;
-
- for (final Symbol symbol : symbols) {
- if (symbol.hasSlot()) {
- symbol.setSlot(baseCount + count);
- count += symbol.slotCount();
- }
- }
-
- return count;
- }
-
- /**
- * Return the slot count of previous frames.
- * @return Number of slots in previous frames.
- */
- private int getBaseCount() {
- return previous != null ? previous.getSlotCount() : 0;
- }
-
- /**
- * Determine the number of slots to top of frame.
- * @return Number of slots in total.
- */
- public int getSlotCount() {
- return baseCount + count;
- }
-
- @Override
- public String toString() {
- final StringBuilder sb = new StringBuilder();
- Frame f = this;
- boolean hasPrev = false;
- int pos = 0;
-
- do {
- if (hasPrev) {
- sb.append("\n");
- }
-
- sb.append("#").
- append(pos++).
- append(" {baseCount:").
- append(baseCount).
- append(", ").
- append("count:").
- append(count).
- append("} ");
-
- for (final Symbol var : f.getSymbols()) {
- sb.append('[').
- append(var.toString()).
- append(' ').
- append(var.hashCode()).
- append("] ");
- }
-
- f = f.getPrevious();
- hasPrev = true;
- } while (f != null);
-
- return sb.toString();
- }
-
- /**
- * Get variable count for this frame
- * @return variable count
- */
- public int getCount() {
- return count;
- }
-
- /**
- * Get previous frame
- * @return previous frame
- */
- public Frame getPrevious() {
- return previous;
- }
-
- /**
- * Set previous frame
- * @param previous previous frame
- */
- public void setPrevious(final Frame previous) {
- this.previous = previous;
- }
-
- /**
- * Get symbols in frame
- * @return a list of symbols in this frame
- */
- public List<Symbol> getSymbols() {
- return Collections.unmodifiableList(symbols);
- }
- }
diff --git a/src/jdk/nashorn/internal/codegen/Lower.java b/src/jdk/nashorn/internal/codegen/Lower.java
index 715ddc6f..d6209e4f 100644
--- a/src/jdk/nashorn/internal/codegen/Lower.java
+++ b/src/jdk/nashorn/internal/codegen/Lower.java
@@ -25,29 +25,21 @@
package jdk.nashorn.internal.codegen;
-import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
-import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
import static jdk.nashorn.internal.codegen.CompilerConstants.EVAL;
-import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
-import static jdk.nashorn.internal.codegen.CompilerConstants.SCRIPT_RETURN;
+import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
-import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
-import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Deque;
-import java.util.Iterator;
import java.util.List;
import jdk.nashorn.internal.ir.BaseNode;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.Block;
+import jdk.nashorn.internal.ir.BlockLexicalContext;
import jdk.nashorn.internal.ir.BreakNode;
import jdk.nashorn.internal.ir.CallNode;
-import jdk.nashorn.internal.ir.CaseNode;
import jdk.nashorn.internal.ir.CatchNode;
import jdk.nashorn.internal.ir.ContinueNode;
-import jdk.nashorn.internal.ir.DoWhileNode;
import jdk.nashorn.internal.ir.EmptyNode;
import jdk.nashorn.internal.ir.ExecuteNode;
import jdk.nashorn.internal.ir.ForNode;
@@ -56,10 +48,10 @@ import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.IfNode;
import jdk.nashorn.internal.ir.LabelNode;
-import jdk.nashorn.internal.ir.LabeledNode;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LineNumberNode;
import jdk.nashorn.internal.ir.LiteralNode;
+import jdk.nashorn.internal.ir.LoopNode;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.ReturnNode;
import jdk.nashorn.internal.ir.SwitchNode;
@@ -90,599 +82,415 @@ import jdk.nashorn.internal.runtime.Source;
final class Lower extends NodeOperatorVisitor {
- /**
- * Nesting level stack. Currently just used for loops to avoid the problem
- * with terminal bodies that end with throw/return but still do continues to
- * outer loops or same loop.
- */
- private final Deque<Node> nesting;
-
private static final DebugLogger LOG = new DebugLogger("lower");
- private Node lastStatement;
-
- private List<Node> statements;
-
- private LexicalContext lexicalContext = new LexicalContext();
-
/**
* Constructor.
*
* @param compiler the compiler
*/
Lower() {
- this.nesting = new ArrayDeque<>();
- this.statements = new ArrayList<>();
- }
+ super(new BlockLexicalContext() {
- @Override
- public Node enterBlock(final Block block) {
- final Node savedLastStatement = lastStatement;
- final List<Node> savedStatements = statements;
- lexicalContext.push(block);
- try {
- this.statements = new ArrayList<>();
- NodeVisitor visitor = this;
- for (final Node statement : block.getStatements()) {
- statement.accept(visitor);
- /*
- * This is slightly unsound, for example if we have a loop with
- * a guarded statement like if (x) continue in the body and the
- * body ends with TERMINAL, e.g. return; we removed the continue
- * before we had the loop stack, as all we cared about was a
- * return last in the loop.
- *
- * @see NASHORN-285
- */
- if (lastStatement != null && lastStatement.isTerminal()) {
- copyTerminal(block, lastStatement);
- visitor = new DeadCodeVarDeclarationVisitor();
+ @Override
+ public List<Node> popStatements() {
+ List<Node> newStatements = new ArrayList<>();
+ boolean terminated = false;
+
+ final List<Node> statements = super.popStatements();
+ for (final Node statement : statements) {
+ if (!terminated) {
+ newStatements.add(statement);
+ if (statement.isTerminal()) {
+ terminated = true;
+ }
+ } else {
+ if (statement instanceof VarNode) {
+ newStatements.add(((VarNode)statement).setInit(null));
+ }
+ }
}
+ return newStatements;
}
- block.setStatements(statements);
-
- } finally {
- this.statements = savedStatements;
- this.lastStatement = savedLastStatement;
- lexicalContext.pop(block);
- }
-
- return null;
+ });
}
@Override
- public Node enterBreakNode(final BreakNode breakNode) {
- return enterBreakOrContinue(breakNode);
+ public boolean enterBlock(final Block block) {
+ final LexicalContext lc = getLexicalContext();
+ if (lc.isFunctionBody() && lc.getCurrentFunction().isProgram() && !lc.getCurrentFunction().hasDeclaredFunctions()) {
+ new ExecuteNode(block.getSource(), block.getToken(), block.getFinish(), LiteralNode.newInstance(block, ScriptRuntime.UNDEFINED)).accept(this);
+ }
+ return true;
}
@Override
- public Node enterCallNode(final CallNode callNode) {
- final Node function = markerFunction(callNode.getFunction());
- callNode.setFunction(function);
- checkEval(callNode); //check if this is an eval call and store the information
- return callNode;
- }
+ public Node leaveBlock(final Block block) {
+ //now we have committed the entire statement list to the block, but we need to truncate
+ //whatever is after the last terminal. block append won't append past it
- @Override
- public Node leaveCaseNode(final CaseNode caseNode) {
- caseNode.copyTerminalFlags(caseNode.getBody());
- return caseNode;
+ final BlockLexicalContext lc = (BlockLexicalContext)getLexicalContext();
+
+ Node last = lc.getLastStatement();
+
+ if (lc.isFunctionBody()) {
+ final FunctionNode currentFunction = getLexicalContext().getCurrentFunction();
+ final boolean isProgram = currentFunction.isProgram();
+ final ReturnNode returnNode = new ReturnNode(
+ currentFunction.getSource(),
+ currentFunction.getToken(),
+ currentFunction.getFinish(),
+ isProgram ?
+ compilerConstant(RETURN) :
+ LiteralNode.newInstance(block, ScriptRuntime.UNDEFINED));
+
+ last = returnNode.accept(this);
+ }
+
+ if (last != null && last.isTerminal()) {
+ return block.setIsTerminal(lc, true);
+ }
+
+ return block;
}
@Override
- public Node leaveCatchNode(final CatchNode catchNode) {
- catchNode.copyTerminalFlags(catchNode.getBody());
- addStatement(catchNode);
- return catchNode;
+ public boolean enterBreakNode(final BreakNode breakNode) {
+ addStatement(breakNode);
+ return false;
}
@Override
- public Node enterContinueNode(final ContinueNode continueNode) {
- return enterBreakOrContinue(continueNode);
+ public Node leaveCallNode(final CallNode callNode) {
+ return checkEval(callNode.setFunction(markerFunction(callNode.getFunction())));
}
@Override
- public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
- return enterWhileNode(doWhileNode);
+ public Node leaveCatchNode(final CatchNode catchNode) {
+ return addStatement(catchNode);
}
@Override
- public Node leaveDoWhileNode(final DoWhileNode doWhileNode) {
- return leaveWhileNode(doWhileNode);
+ public boolean enterContinueNode(final ContinueNode continueNode) {
+ addStatement(continueNode);
+ return false;
}
@Override
- public Node enterEmptyNode(final EmptyNode emptyNode) {
- return null;
+ public boolean enterEmptyNode(final EmptyNode emptyNode) {
+ return false;
}
@Override
public Node leaveExecuteNode(final ExecuteNode executeNode) {
final Node expr = executeNode.getExpression();
+ ExecuteNode node = executeNode;
+
+ final FunctionNode currentFunction = getLexicalContext().getCurrentFunction();
- if (getCurrentFunctionNode().isProgram()) {
+ if (currentFunction.isProgram()) {
if (!(expr instanceof Block) || expr instanceof FunctionNode) { // it's not a block, but can be a function
if (!isInternalExpression(expr) && !isEvalResultAssignment(expr)) {
- executeNode.setExpression(new BinaryNode(executeNode.getSource(), Token.recast(executeNode.getToken(), TokenType.ASSIGN),
- getCurrentFunctionNode().getResultNode(),
- expr));
+ node = executeNode.setExpression(
+ new BinaryNode(
+ executeNode.getSource(),
+ Token.recast(
+ executeNode.getToken(),
+ TokenType.ASSIGN),
+ compilerConstant(RETURN),
+ expr));
}
}
}
- copyTerminal(executeNode, executeNode.getExpression());
- addStatement(executeNode);
-
- return executeNode;
- }
-
- @Override
- public Node enterForNode(final ForNode forNode) {
- nest(forNode);
- return forNode;
+ return addStatement(node);
}
@Override
public Node leaveForNode(final ForNode forNode) {
- final Node test = forNode.getTest();
- final Block body = forNode.getBody();
-
- if (!forNode.isForIn() && test == null) {
- setHasGoto(forNode);
- }
-
- final boolean escapes = controlFlowEscapes(body);
- if (escapes) {
- setTerminal(body, false);
- }
-
- // pop the loop from the loop context
- unnest(forNode);
+ ForNode newForNode = forNode;
+ final Node test = forNode.getTest();
if (!forNode.isForIn() && conservativeAlwaysTrue(test)) {
- forNode.setTest(null);
- setHasGoto(forNode);
- setTerminal(forNode, !escapes);
+ newForNode = forNode.setTest(getLexicalContext(), null);
}
- addStatement(forNode);
-
- return forNode;
+ return addStatement(checkEscape(newForNode));
}
@Override
- public Node enterFunctionNode(final FunctionNode functionNode) {
- LOG.info("START FunctionNode: " + functionNode.getName());
-
- if (functionNode.isLazy()) {
- LOG.info("LAZY: " + functionNode.getName());
- return null;
- }
- lexicalContext.push(functionNode);
- initFunctionNode(functionNode);
-
- nest(functionNode);
-
- /*
- * As we are evaluating a nested structure, we need to store the
- * statement list for the surrounding block and restore it when the
- * function is done
- */
- final List<Node> savedStatements = statements;
- final Node savedLastStatement = lastStatement;
+ public boolean enterFunctionNode(final FunctionNode functionNode) {
+ return !functionNode.isLazy();
+ }
- statements = new ArrayList<>();
- lastStatement = null;
+ @Override
+ public Node leaveFunctionNode(final FunctionNode functionNode) {
+ LOG.info("END FunctionNode: ", functionNode.getName());
+ return functionNode.setState(getLexicalContext(), CompilationState.LOWERED);
+ }
- if (functionNode.needsSelfSymbol()) {
- //function needs to start with var funcIdent = __callee_;
- statements.add(functionNode.getSelfSymbolInit().accept(this));
- }
+ @Override
+ public Node leaveIfNode(final IfNode ifNode) {
+ return addStatement(ifNode);
+ }
- NodeVisitor visitor = this;
- try {
- //do the statements - this fills the block with code
- boolean needsInitialEvalResult = functionNode.isProgram();
- for (final Node statement : functionNode.getStatements()) {
- // If this function is a program, then insert an assignment to the initial eval result after all
- // function declarations.
- if(needsInitialEvalResult && !(statement instanceof LineNumberNode || (statement instanceof VarNode && ((VarNode)statement).isFunctionDeclaration()))) {
- addInitialEvalResult(functionNode);
- needsInitialEvalResult = false;
- }
- statement.accept(visitor);
- //If there are unused terminated endpoints in the function, we need
- // to add a "return undefined" in those places for correct semantics
- LOG.info("Checking lastStatement="+lastStatement+" for terminal flags");
- if (lastStatement != null && lastStatement.hasTerminalFlags()) {
- copyTerminal(functionNode, lastStatement);
- assert !needsInitialEvalResult;
- visitor = new DeadCodeVarDeclarationVisitor();
- }
- }
- if(needsInitialEvalResult) {
- addInitialEvalResult(functionNode);
- }
- functionNode.setStatements(statements);
+ @Override
+ public Node leaveLabelNode(final LabelNode labelNode) {
+ return addStatement(labelNode);
+ }
- if (!functionNode.isTerminal()) {
- guaranteeReturn(functionNode);
- }
- } finally {
- statements = savedStatements;
- lastStatement = savedLastStatement;
- }
+ @Override
+ public boolean enterLineNumberNode(final LineNumberNode lineNumberNode) {
+ addStatement(lineNumberNode); // don't put it in lastStatement cache
+ return false;
+ }
- LOG.info("END FunctionNode: " + functionNode.getName());
- unnest(functionNode);
- lexicalContext.pop(functionNode);
+ @Override
+ public Node leaveReturnNode(final ReturnNode returnNode) {
+ addStatement(returnNode); //ReturnNodes are always terminal, marked as such in constructor
+ return returnNode;
+ }
- functionNode.setState(CompilationState.LOWERED);
- return null;
+ @Override
+ public Node leaveSwitchNode(final SwitchNode switchNode) {
+ return addStatement(switchNode);
}
- /**
- * This visitor is used to go over statements after a terminal statement. Those statements are dead code, but the
- * var declarations in them still have the effect of declaring a local variable on the function level. Therefore,
- * they aren't really dead code and must be preserved. Note that they're only preserved as no-op declarations; their
- * initializers are wiped out as those are, in fact, dead code.
- */
- private class DeadCodeVarDeclarationVisitor extends NodeOperatorVisitor {
- DeadCodeVarDeclarationVisitor() {
- }
-
- @Override
- public Node enterVarNode(VarNode varNode) {
- // Can't ever see a function declaration, as this visitor is only ever used after a terminal statement was
- // encountered, and all function declarations precede any terminal statements.
- assert !varNode.isFunctionDeclaration();
- if(varNode.getInit() == null) {
- // No initializer, just pass it to Lower.
- return varNode.accept(Lower.this);
- }
- // Wipe out the initializer and then pass it to Lower.
- return varNode.setInit(null).accept(Lower.this);
- }
+ @Override
+ public Node leaveThrowNode(final ThrowNode throwNode) {
+ addStatement(throwNode); //ThrowNodes are always terminal, marked as such in constructor
+ return throwNode;
}
- private void addInitialEvalResult(final FunctionNode functionNode) {
- new ExecuteNode(functionNode.getSource(), functionNode.getFirstToken(), functionNode.getFinish(),
- getInitialEvalResult(functionNode)).accept(this);
+ private static Node ensureUniqueLabelsIn(final Node node) {
+ return node.accept(new NodeVisitor() {
+ @Override
+ public Node leaveDefault(final Node labelledNode) {
+ return labelledNode.ensureUniqueLabels(getLexicalContext());
+ }
+ });
}
- /**
- * Result of initial result of evaluating a particular program, which is either the last function it declares, or
- * undefined if it doesn't declare any functions.
- * @param program
- * @return the initial result of evaluating the program
- */
- private static Node getInitialEvalResult(final FunctionNode program) {
- IdentNode lastFnName = null;
- for (final FunctionNode fn : program.getDeclaredFunctions()) {
- assert fn.isDeclared();
- final IdentNode fnName = fn.getIdent();
- if(fnName != null) {
- lastFnName = fnName;
+ private static List<Node> copyFinally(final Block finallyBody) {
+ final List<Node> newStatements = new ArrayList<>();
+ for (final Node statement : finallyBody.getStatements()) {
+ newStatements.add(ensureUniqueLabelsIn(statement));
+ if (statement.hasTerminalFlags()) {
+ return newStatements;
}
}
- return lastFnName != null ? new IdentNode(lastFnName) : LiteralNode.newInstance(program, ScriptRuntime.UNDEFINED);
+ return newStatements;
}
- @Override
- public Node enterIfNode(final IfNode ifNode) {
- return nest(ifNode);
- }
+ private Block catchAllBlock(final TryNode tryNode) {
+ final Source source = tryNode.getSource();
+ final long token = tryNode.getToken();
+ final int finish = tryNode.getFinish();
- @Override
- public Node leaveIfNode(final IfNode ifNode) {
- final Node pass = ifNode.getPass();
- final Node fail = ifNode.getFail();
+ final IdentNode exception = new IdentNode(source, token, finish, getLexicalContext().getCurrentFunction().uniqueName("catch_all"));
- if (pass.isTerminal() && fail != null && fail.isTerminal()) {
- setTerminal(ifNode, true);
- }
+ final Block catchBody = new Block(source, token, finish, new ThrowNode(source, token, finish, new IdentNode(exception))).
+ setIsTerminal(getLexicalContext(), true); //ends with throw, so terminal
- addStatement(ifNode);
- unnest(ifNode);
+ final CatchNode catchAllNode = new CatchNode(source, token, finish, new IdentNode(exception), null, catchBody);
+ final Block catchAllBlock = new Block(source, token, finish, catchAllNode);
- return ifNode;
+ //catchallblock -> catchallnode (catchnode) -> exception -> throw
+
+ return (Block)catchAllBlock.accept(this); //not accepted. has to be accepted by lower
}
- @Override
- public Node enterLabelNode(LabelNode labelNode) {
- final Block body = labelNode.getBody();
- body.accept(this);
- copyTerminal(labelNode, body);
- addStatement(labelNode);
- return null;
+ private IdentNode compilerConstant(final CompilerConstants cc) {
+ final FunctionNode functionNode = getLexicalContext().getCurrentFunction();
+ return new IdentNode(functionNode.getSource(), functionNode.getToken(), functionNode.getFinish(), cc.symbolName());
}
- @Override
- public Node enterLineNumberNode(final LineNumberNode lineNumberNode) {
- addStatement(lineNumberNode, false); // don't put it in lastStatement cache
- return null;
+ private static boolean isTerminal(final List<Node> statements) {
+ return !statements.isEmpty() && statements.get(statements.size() - 1).hasTerminalFlags();
}
- @Override
- public Node enterReturnNode(final ReturnNode returnNode) {
- final TryNode tryNode = returnNode.getTryChain();
- final Node expr = returnNode.getExpression();
-
- if (tryNode != null) {
- //we are inside a try block - we don't necessarily have a result node yet. attr will do that.
- if (expr != null) {
- final Source source = getCurrentFunctionNode().getSource();
-
- //we need to evaluate the result of the return in case it is complex while
- //still in the try block, store it in a result value and return it afterwards
- final long token = returnNode.getToken();
- final Node resultNode = new IdentNode(getCurrentFunctionNode().getResultNode());
- final Node assignResult = new BinaryNode(source, Token.recast(token, TokenType.ASSIGN), resultNode, expr);
-
- //add return_in_try = expr; to try block
- new ExecuteNode(source, token, Token.descPosition(token), assignResult).accept(this);
-
- //splice in the finally code, inlining it here
- if (copyFinally(tryNode, null)) {
- return null;
- }
+ /**
+ * Splice finally code into all endpoints of a trynode
+ * @param tryNode the try node
+ * @param list of rethrowing throw nodes from synthetic catch blocks
+ * @param finallyBody the code in the original finally block
+ * @return new try node after splicing finally code (same if nop)
+ */
+ private Node spliceFinally(final TryNode tryNode, final List<ThrowNode> rethrows, final Block finallyBody) {
+ final Source source = tryNode.getSource();
+ final int finish = tryNode.getFinish();
- //make sure that the return node now returns 'return_in_try'
- returnNode.setExpression(resultNode);
- } else if (copyFinally(tryNode, null)) {
- return null;
- }
- } else if (expr != null) {
- returnNode.setExpression(expr.accept(this));
- }
+ assert tryNode.getFinallyBody() == null;
- addStatement(returnNode);
+ final TryNode newTryNode = (TryNode)tryNode.accept(new NodeVisitor() {
+ final List<Node> insideTry = new ArrayList<>();
- return null;
- }
+ @Override
+ public boolean enterDefault(final Node node) {
+ insideTry.add(node);
+ return true;
+ }
- @Override
- public Node leaveReturnNode(final ReturnNode returnNode) {
- addStatement(returnNode); //ReturnNodes are always terminal, marked as such in constructor
- return returnNode;
- }
+ @Override
+ public boolean enterFunctionNode(final FunctionNode functionNode) {
+ // do not enter function nodes - finally code should not be inlined into them
+ return false;
+ }
- @Override
- public Node enterSwitchNode(final SwitchNode switchNode) {
- nest(switchNode);
- return switchNode;
- }
+ @Override
+ public Node leaveThrowNode(final ThrowNode throwNode) {
+ if (rethrows.contains(throwNode)) {
+ final List<Node> newStatements = copyFinally(finallyBody);
+ if (!isTerminal(newStatements)) {
+ newStatements.add(throwNode);
+ }
+ return new Block(source, throwNode.getToken(), throwNode.getFinish(), newStatements);
+ }
+ return throwNode;
+ }
- @Override
- public Node leaveSwitchNode(final SwitchNode switchNode) {
- unnest(switchNode);
+ @Override
+ public Node leaveBreakNode(final BreakNode breakNode) {
+ return copy(breakNode, Lower.this.getLexicalContext().getBreakable(breakNode.getLabel()));
+ }
- final List<CaseNode> cases = switchNode.getCases();
- final CaseNode defaultCase = switchNode.getDefaultCase();
+ @Override
+ public Node leaveContinueNode(final ContinueNode continueNode) {
+ return copy(continueNode, Lower.this.getLexicalContext().getContinueTo(continueNode.getLabel()));
+ }
- boolean allTerminal = !cases.isEmpty();
- for (final CaseNode caseNode : switchNode.getCases()) {
- allTerminal &= caseNode.isTerminal();
- }
+ @Override
+ public Node leaveReturnNode(final ReturnNode returnNode) {
+ final Node expr = returnNode.getExpression();
+ final List<Node> newStatements = new ArrayList<>();
+
+ final Node resultNode;
+ if (expr != null) {
+ //we need to evaluate the result of the return in case it is complex while
+ //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 ExecuteNode(new BinaryNode(source, Token.recast(returnNode.getToken(), TokenType.ASSIGN), resultNode, expr)));
+ } else {
+ resultNode = null;
+ }
- if (allTerminal && defaultCase != null && defaultCase.isTerminal()) {
- setTerminal(switchNode, true);
- }
+ newStatements.addAll(copyFinally(finallyBody));
+ if (!isTerminal(newStatements)) {
+ newStatements.add(expr == null ? returnNode : returnNode.setExpression(resultNode));
+ }
- addStatement(switchNode);
+ return new ExecuteNode(new Block(source, returnNode.getToken(), getLexicalContext().getCurrentBlock().getFinish(), newStatements));
+ }
- return switchNode;
- }
+ private Node copy(final Node endpoint, final Node targetNode) {
+ if (!insideTry.contains(targetNode)) {
+ final List<Node> newStatements = copyFinally(finallyBody);
+ if (!isTerminal(newStatements)) {
+ newStatements.add(endpoint);
+ }
+ return new ExecuteNode(new Block(source, endpoint.getToken(), finish, newStatements));
+ }
+ return endpoint;
+ }
+ });
- @Override
- public Node leaveThrowNode(final ThrowNode throwNode) {
- addStatement(throwNode); //ThrowNodes are always terminal, marked as such in constructor
- return throwNode;
+ addStatement(newTryNode);
+ for (final Node statement : finallyBody.getStatements()) {
+ addStatement(statement);
+ }
+
+ return newTryNode;
}
@Override
- public Node enterTryNode(final TryNode tryNode) {
- final Block finallyBody = tryNode.getFinallyBody();
- final long token = tryNode.getToken();
- final int finish = tryNode.getFinish();
-
- nest(tryNode);
+ public Node leaveTryNode(final TryNode tryNode) {
+ final Block finallyBody = tryNode.getFinallyBody();
if (finallyBody == null) {
- //do nothing if no finally exists
- return tryNode;
+ return addStatement(tryNode);
}
/*
- * We have a finally clause.
+ * create a new trynode
+ * if we have catches:
*
- * Transform to do finally tail duplication as follows:
+ * try try
+ * x try
+ * catch x
+ * y catch
+ * finally z y
+ * catchall
+ * rethrow
*
- * <pre>
- * try {
- * try_body
- * } catch e1 {
- * catchbody_1
- * }
- * ...
- * } catch en {
- * catchbody_n
- * } finally {
- * finally_body
- * }
+ * otheriwse
*
- * (where e1 ... en are optional)
+ * try try
+ * x x
+ * finally catchall
+ * y rethrow
*
- * turns into
*
- * try {
- * try {
- * try_body
- * } catch e1 {
- * catchbody1
- * //nothing inlined explicitly here, return, break other
- * //terminals may inline the finally body
- * ...
- * } catch en {
- * catchbody2
- * //nothing inlined explicitly here, return, break other
- * //terminals may inline the finally body
- * }
- * } catch all ex {
- * finally_body_inlined
- * rethrow ex
- * }
- * finally_body_inlined
- * </pre>
+ * now splice in finally code wherever needed
*
- * If tries are catches are terminal, visitors for return, break &
- * continue will handle the tail duplications. Throw needs to be
- * treated specially with the catchall as described in the above
- * ASCII art.
- *
- * If the try isn't terminal we do the finally_body_inlined at the
- * end. If the try is terminated with continue/break/return the
- * existing visitor logic will inline the finally before that
- * operation. if the try is terminated with a throw, the catches e1
- * ... en will have a chance to process the exception. If the
- * appropriate catch e1..en is non terminal we fall through to the
- * last finally_body_inlined. if the catch e1...en IS terminal with
- * continue/break/return existing visitor logic will fix it. If they
- * are terminal with another throw it goes to the catchall and the
- * finally_body_inlined marked (*) will fix it before rethrowing
- * whatever problem there was for identical semantic.
*/
- final Source source = getCurrentFunctionNode().getSource();
-
- // if try node does not contain a catch we can skip creation of a new
- // try node and just append our synthetic catch to the existing try node.
- if (!tryNode.getCatchBlocks().isEmpty()) {
- // insert an intermediate try-catch* node, where we move the body and all catch blocks.
- // the original try node become a try-finally container for the new try-catch* node.
- // because we don't clone (to avoid deep copy), we have to fix the block chain in the end.
- final TryNode innerTryNode;
- innerTryNode = new TryNode(source, token, finish, tryNode.getNext());
- innerTryNode.setBody(tryNode.getBody());
- innerTryNode.setCatchBlocks(tryNode.getCatchBlocks());
-
- // set outer tryNode's body to innerTryNode
- final Block outerBody;
- outerBody = new Block(source, token, finish);
- outerBody.setStatements(new ArrayList<Node>(Arrays.asList(innerTryNode)));
- tryNode.setBody(outerBody);
- tryNode.setCatchBlocks(null);
- }
-
- // create a catch-all that inlines finally and rethrows
-
- final Block catchBlock = new Block(source, token, finish);
- //this catch block should get define symbol
+ TryNode newTryNode;
- final Block catchBody = new Block(source, token, finish);
- final Node catchAllFinally = finallyBody.copy();
+ final Block catchAll = catchAllBlock(tryNode);
- catchBody.addStatement(new ExecuteNode(source, finallyBody.getToken(), finallyBody.getFinish(), catchAllFinally));
- setTerminal(catchBody, true);
-
- final CatchNode catchAllNode;
- final IdentNode exception;
-
- exception = new IdentNode(source, token, finish, getCurrentFunctionNode().uniqueName("catch_all"));
- catchAllNode = new CatchNode(source, token, finish, new IdentNode(exception), null, catchBody);
- catchAllNode.setIsSyntheticRethrow();
+ final List<ThrowNode> rethrows = new ArrayList<>();
+ catchAll.accept(new NodeVisitor() {
+ @Override
+ public boolean enterThrowNode(final ThrowNode throwNode) {
+ rethrows.add(throwNode);
+ return true;
+ }
+ });
+ assert rethrows.size() == 1;
- catchBlock.addStatement(catchAllNode);
+ if (tryNode.getCatchBlocks().isEmpty()) {
+ newTryNode = tryNode.setFinallyBody(null);
+ } else {
+ Block outerBody = new Block(tryNode.getSource(), tryNode.getToken(), tryNode.getFinish(), new ArrayList<Node>(Arrays.asList(tryNode.setFinallyBody(null))));
+ newTryNode = tryNode.setBody(outerBody).setCatchBlocks(null);
+ }
- // replace all catches of outer tryNode with the catch-all
- tryNode.setCatchBlocks(new ArrayList<>(Arrays.asList(catchBlock)));
+ newTryNode = newTryNode.setCatchBlocks(Arrays.asList(catchAll)).setFinallyBody(null);
/*
- * We leave the finally block for the original try in place for now
- * so that children visitations will work. It is removed and placed
- * afterwards in the else case below, after all children are visited
+ * Now that the transform is done, we have to go into the try and splice
+ * the finally block in front of any statement that is outside the try
*/
-
- return tryNode;
- }
-
- @Override
- public Node leaveTryNode(final TryNode tryNode) {
- final Block finallyBody = tryNode.getFinallyBody();
-
- boolean allTerminal = tryNode.getBody().isTerminal() && (finallyBody == null || finallyBody.isTerminal());
-
- for (final Block catchBlock : tryNode.getCatchBlocks()) {
- allTerminal &= catchBlock.isTerminal();
- }
-
- tryNode.setIsTerminal(allTerminal);
-
- addStatement(tryNode);
- unnest(tryNode);
-
- // if finally body is present, place it after the tryNode
- if (finallyBody != null) {
- tryNode.setFinallyBody(null);
- addStatement(finallyBody);
- }
-
- return tryNode;
+ return spliceFinally(newTryNode, rethrows, finallyBody);
}
@Override
public Node leaveVarNode(final VarNode varNode) {
addStatement(varNode);
+ if (varNode.getFlag(VarNode.IS_LAST_FUNCTION_DECLARATION) && getLexicalContext().getCurrentFunction().isProgram()) {
+ new ExecuteNode(varNode.getSource(), varNode.getToken(), varNode.getFinish(), new IdentNode(varNode.getName())).accept(this);
+ }
return varNode;
}
@Override
- public Node enterWhileNode(final WhileNode whileNode) {
- return nest(whileNode);
- }
-
- @Override
public Node leaveWhileNode(final WhileNode whileNode) {
final Node test = whileNode.getTest();
+ final Block body = whileNode.getBody();
- if (test == null) {
- setHasGoto(whileNode);
- }
-
- final Block body = whileNode.getBody();
- final boolean escapes = controlFlowEscapes(body);
- if (escapes) {
- setTerminal(body, false);
+ if (conservativeAlwaysTrue(test)) {
+ //turn it into a for node without a test.
+ final ForNode forNode = (ForNode)new ForNode(whileNode.getSource(), whileNode.getToken(), whileNode.getFinish(), null, null, body, null, ForNode.IS_FOR).accept(this);
+ getLexicalContext().replace(whileNode, forNode);
+ return forNode;
}
- Node node = whileNode;
-
- if (body.isTerminal()) {
- if (whileNode instanceof DoWhileNode) {
- setTerminal(whileNode, true);
- } else if (conservativeAlwaysTrue(test)) {
- node = new ForNode(whileNode.getSource(), whileNode.getToken(), whileNode.getFinish());
- ((ForNode)node).setBody(body);
- node.accept(this);
- setTerminal(node, !escapes);
- }
- }
-
- // pop the loop from the loop context
- unnest(whileNode);
- addStatement(node);
-
- return node;
+ return addStatement(checkEscape(whileNode));
}
@Override
public Node leaveWithNode(final WithNode withNode) {
- if (withNode.getBody().isTerminal()) {
- setTerminal(withNode, true);
- }
- addStatement(withNode);
-
- return withNode;
+ return addStatement(withNode);
}
@Override
@@ -741,23 +549,25 @@ final class Lower extends NodeOperatorVisitor {
*
* @param callNode call node to check if it's an eval
*/
- private void checkEval(final CallNode callNode) {
+ private CallNode checkEval(final CallNode callNode) {
if (callNode.getFunction() instanceof IdentNode) {
final List<Node> args = callNode.getArgs();
final IdentNode callee = (IdentNode)callNode.getFunction();
// 'eval' call with at least one argument
- if (args.size() >= 1 && EVAL.tag().equals(callee.getName())) {
- final CallNode.EvalArgs evalArgs =
+ if (args.size() >= 1 && EVAL.symbolName().equals(callee.getName())) {
+ final FunctionNode currentFunction = getLexicalContext().getCurrentFunction();
+ return callNode.setEvalArgs(
new CallNode.EvalArgs(
- args.get(0).copy().accept(this), //clone as we use this for the "is eval case". original evaluated separately for "is not eval case"
- getCurrentFunctionNode().getThisNode(),
+ ensureUniqueLabelsIn(args.get(0)).accept(this),
+ compilerConstant(THIS),
evalLocation(callee),
- getCurrentFunctionNode().isStrictMode());
- callNode.setEvalArgs(evalArgs);
+ currentFunction.isStrict()));
}
}
+
+ return callNode;
}
private static boolean conservativeAlwaysTrue(final Node node) {
@@ -773,7 +583,7 @@ final class Lower extends NodeOperatorVisitor {
* @param loopBody the loop body to check
* @return true if control flow may escape the loop
*/
- private boolean controlFlowEscapes(final Node loopBody) {
+ private static boolean controlFlowEscapes(final LexicalContext lex, final Block loopBody) {
final List<Node> escapes = new ArrayList<>();
loopBody.accept(new NodeVisitor() {
@@ -786,7 +596,7 @@ final class Lower extends NodeOperatorVisitor {
@Override
public Node leaveContinueNode(final ContinueNode node) {
// all inner loops have been popped.
- if (nesting.contains(node.getTargetNode())) {
+ if (lex.contains(lex.getContinueTo(node.getLabel()))) {
escapes.add(node);
}
return node;
@@ -796,135 +606,23 @@ final class Lower extends NodeOperatorVisitor {
return !escapes.isEmpty();
}
- private void guaranteeReturn(final FunctionNode functionNode) {
- Node resultNode;
-
- if (functionNode.isProgram()) {
- resultNode = functionNode.getResultNode(); // the eval result, symbol assigned in Attr
- } else {
- if (lastStatement != null && lastStatement.isTerminal() || lastStatement instanceof ReturnNode) {
- return; //already in place or not needed, as it should be for a non-undefined returning function
- }
- resultNode = LiteralNode.newInstance(functionNode, ScriptRuntime.UNDEFINED);
- }
-
- //create a return statement
- final Node returnNode = new ReturnNode(functionNode.getSource(), functionNode.getLastToken(), functionNode.getFinish(), resultNode, null);
- returnNode.accept(this);
- }
-
-
- private Node nest(final Node node) {
- LOG.info("Nesting: " + node);
- LOG.indent();
- nesting.push(node);
- return node;
- }
-
- private void unnest(final Node node) {
- LOG.unindent();
- assert nesting.getFirst() == node : "inconsistent nesting order : " + nesting.getFirst() + " != " + node;
- LOG.info("Unnesting: " + nesting);
- nesting.pop();
- }
-
- private static void setTerminal(final Node node, final boolean isTerminal) {
- LOG.info("terminal = " + isTerminal + " for " + node);
- node.setIsTerminal(isTerminal);
- }
-
- private static void setHasGoto(final Node node) { //, final boolean hasGoto) {
- LOG.info("hasGoto = true for " + node);
- node.setHasGoto();
- }
-
- private static void copyTerminal(final Node node, final Node sourceNode) {
- LOG.info("copy terminal flags " + sourceNode + " -> " + node);
- node.copyTerminalFlags(sourceNode);
- }
-
- private void addStatement(final Node statement, final boolean storeInLastStatement) {
- LOG.info("add statement = " + statement + " (lastStatement = " + lastStatement + ")");
- statements.add(statement);
- if (storeInLastStatement) {
- lastStatement = statement;
- }
- }
-
- private void addStatement(final Node statement) {
- addStatement(statement, true);
- }
-
- /**
- * Determine if Try block is inside target block.
- *
- * @param tryNode Try node to test.
- * @param target Target block.
- *
- * @return true if try block is inside the target, false otherwise.
- */
- private boolean isNestedTry(final TryNode tryNode, final Block target) {
- for(Iterator<Block> blocks = lexicalContext.getBlocks(getCurrentBlock()); blocks.hasNext();) {
- final Block block = blocks.next();
- if(block == target) {
- return false;
- }
- if(tryNode.isChildBlock(block)) {
- return true;
- }
+ private LoopNode checkEscape(final LoopNode loopNode) {
+ final LexicalContext lc = getLexicalContext();
+ final boolean escapes = controlFlowEscapes(lc, loopNode.getBody());
+ if (escapes) {
+ return loopNode.
+ setBody(lc, loopNode.getBody().setIsTerminal(lc, false)).
+ setControlFlowEscapes(lc, escapes);
}
- return false;
+ return loopNode;
}
- /**
- * Clones the body of the try finallys up to the target block.
- *
- * @param node first try node in the chain.
- * @param targetNode target block of the break/continue statement or null for return
- *
- * @return true if terminates.
- */
- private boolean copyFinally(final TryNode node, final Node targetNode) {
- Block target = null;
-
- if (targetNode instanceof Block) {
- target = (Block)targetNode;
- }
-
- for (TryNode tryNode = node; tryNode != null; tryNode = tryNode.getNext()) {
- if (target != null && !isNestedTry(tryNode, target)) {
- return false;
- }
-
- Block finallyBody = tryNode.getFinallyBody();
- if (finallyBody == null) {
- continue;
- }
-
- finallyBody = (Block)finallyBody.copy();
- final boolean hasTerminalFlags = finallyBody.hasTerminalFlags();
-
- new ExecuteNode(finallyBody.getSource(), finallyBody.getToken(), finallyBody.getFinish(), finallyBody).accept(this);
- if (hasTerminalFlags) {
- getCurrentBlock().copyTerminalFlags(finallyBody);
- return true;
- }
- }
-
- return false;
- }
-
- private Node enterBreakOrContinue(final LabeledNode labeledNode) {
- final TryNode tryNode = labeledNode.getTryChain();
- if (tryNode != null && copyFinally(tryNode, labeledNode.getTargetNode())) {
- return null;
- }
- addStatement(labeledNode);
- return null;
+ private Node addStatement(final Node statement) {
+ ((BlockLexicalContext)getLexicalContext()).appendStatement(statement);
+ return statement;
}
-
/**
* An internal expression has a symbol that is tagged internal. Check if
* this is such a node
@@ -939,40 +637,21 @@ final class Lower extends NodeOperatorVisitor {
/**
* Is this an assignment to the special variable that hosts scripting eval
- * results?
+ * results, i.e. __return__?
*
* @param expression expression to check whether it is $evalresult = X
* @return true if an assignment to eval result, false otherwise
*/
- private boolean isEvalResultAssignment(final Node expression) {
+ private static boolean isEvalResultAssignment(final Node expression) {
Node e = expression;
- if (e.tokenType() == TokenType.DISCARD) {
- e = ((UnaryNode)expression).rhs();
- }
- final Node resultNode = getCurrentFunctionNode().getResultNode();
- return e instanceof BinaryNode && ((BinaryNode)e).lhs().equals(resultNode);
- }
-
- /**
- * Prepare special function nodes.
- * TODO : only create those that are needed.
- * TODO : make sure slot numbering is not hardcoded in {@link CompilerConstants} - now creation order is significant
- */
- private static void initFunctionNode(final FunctionNode functionNode) {
- final Source source = functionNode.getSource();
- final long token = functionNode.getToken();
- final int finish = functionNode.getFinish();
-
- functionNode.setThisNode(new IdentNode(source, token, finish, THIS.tag()));
- functionNode.setScopeNode(new IdentNode(source, token, finish, SCOPE.tag()));
- functionNode.setResultNode(new IdentNode(source, token, finish, SCRIPT_RETURN.tag()));
- functionNode.setCalleeNode(new IdentNode(source, token, finish, CALLEE.tag()));
- if (functionNode.isVarArg()) {
- functionNode.setVarArgsNode(new IdentNode(source, token, finish, VARARGS.tag()));
- if (functionNode.needsArguments()) {
- functionNode.setArgumentsNode(new IdentNode(source, token, finish, ARGUMENTS.tag()));
+ assert e.tokenType() != TokenType.DISCARD; //there are no discards this early anymore
+ if (e instanceof BinaryNode) {
+ final Node lhs = ((BinaryNode)e).lhs();
+ if (lhs instanceof IdentNode) {
+ return ((IdentNode)lhs).getName().equals(RETURN.symbolName());
}
}
+ return false;
}
}
diff --git a/src/jdk/nashorn/internal/codegen/MethodEmitter.java b/src/jdk/nashorn/internal/codegen/MethodEmitter.java
index ae40ed33..4fbb57a6 100644
--- a/src/jdk/nashorn/internal/codegen/MethodEmitter.java
+++ b/src/jdk/nashorn/internal/codegen/MethodEmitter.java
@@ -53,9 +53,12 @@ import static jdk.internal.org.objectweb.asm.Opcodes.NEW;
import static jdk.internal.org.objectweb.asm.Opcodes.PUTFIELD;
import static jdk.internal.org.objectweb.asm.Opcodes.PUTSTATIC;
import static jdk.internal.org.objectweb.asm.Opcodes.RETURN;
+import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
import static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS;
+import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
import static jdk.nashorn.internal.codegen.CompilerConstants.THIS_DEBUGGER;
+import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
import static jdk.nashorn.internal.codegen.CompilerConstants.className;
import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor;
@@ -67,6 +70,7 @@ import java.lang.reflect.Array;
import java.util.ArrayDeque;
import java.util.EnumSet;
import java.util.Iterator;
+import java.util.List;
import jdk.internal.dynalink.support.NameCodec;
import jdk.internal.org.objectweb.asm.Handle;
import jdk.internal.org.objectweb.asm.MethodVisitor;
@@ -79,14 +83,14 @@ import jdk.nashorn.internal.codegen.types.NumericType;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.IdentNode;
+import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.RuntimeNode;
-import jdk.nashorn.internal.ir.SplitNode;
import jdk.nashorn.internal.ir.Symbol;
import jdk.nashorn.internal.runtime.ArgumentSetter;
+import jdk.nashorn.internal.runtime.Debug;
import jdk.nashorn.internal.runtime.DebugLogger;
import jdk.nashorn.internal.runtime.JSType;
-import jdk.nashorn.internal.runtime.Scope;
import jdk.nashorn.internal.runtime.ScriptEnvironment;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.linker.Bootstrap;
@@ -116,10 +120,10 @@ public class MethodEmitter implements Emitter {
private final ClassEmitter classEmitter;
/** FunctionNode representing this method, or null if none exists */
- private FunctionNode functionNode;
+ protected FunctionNode functionNode;
- /** SplitNode representing the current split, or null if none exists */
- private SplitNode splitNode;
+ /** Check whether this emitter ever has a function return point */
+ private boolean hasReturn;
/** The script environment */
private final ScriptEnvironment env;
@@ -203,7 +207,7 @@ public class MethodEmitter implements Emitter {
@Override
public String toString() {
- return "methodEmitter: " + (functionNode == null ? method : functionNode.getName()).toString() + ' ' + stack;
+ return "methodEmitter: " + (functionNode == null ? method : functionNode.getName()).toString() + ' ' + Debug.id(this);
}
/**
@@ -476,8 +480,8 @@ public class MethodEmitter implements Emitter {
String name = symbol.getName();
- if (name.equals(THIS.tag())) {
- name = THIS_DEBUGGER.tag();
+ if (name.equals(THIS.symbolName())) {
+ name = THIS_DEBUGGER.symbolName();
}
method.visitLocalVariable(name, symbol.getSymbolType().getDescriptor(), null, start, end, symbol.getSlot());
@@ -654,7 +658,7 @@ public class MethodEmitter implements Emitter {
* @return this method emitter
*/
MethodEmitter loadConstants() {
- getStatic(classEmitter.getUnitClassName(), CONSTANTS.tag(), CONSTANTS.descriptor());
+ getStatic(classEmitter.getUnitClassName(), CONSTANTS.symbolName(), CONSTANTS.descriptor());
assert peekType().isArray() : peekType();
return this;
}
@@ -669,7 +673,7 @@ public class MethodEmitter implements Emitter {
* @return the method emitter
*/
MethodEmitter loadUndefined(final Type type) {
- debug("load undefined " + type);
+ debug("load undefined ", type);
pushType(type.loadUndefined(method));
return this;
}
@@ -681,7 +685,7 @@ public class MethodEmitter implements Emitter {
* @return the method emitter
*/
MethodEmitter loadEmpty(final Type type) {
- debug("load empty " + type);
+ debug("load empty ", type);
pushType(type.loadEmpty(method));
return this;
}
@@ -814,7 +818,7 @@ public class MethodEmitter implements Emitter {
}
/**
- * Push an local variable to the stack. If the symbol representing
+ * Push a local variable to the stack. If the symbol representing
* the local variable doesn't have a slot, this is a NOP
*
* @param symbol the symbol representing the local variable.
@@ -835,13 +839,13 @@ public class MethodEmitter implements Emitter {
if (functionNode.needsArguments()) {
// ScriptObject.getArgument(int) on arguments
debug("load symbol", symbol.getName(), " arguments index=", index);
- loadArguments();
+ loadCompilerConstant(ARGUMENTS);
load(index);
ScriptObject.GET_ARGUMENT.invoke(this);
} else {
// array load from __varargs__
debug("load symbol", symbol.getName(), " array index=", index);
- loadVarArgs();
+ loadCompilerConstant(VARARGS);
load(symbol.getFieldIndex());
arrayload();
}
@@ -870,48 +874,13 @@ public class MethodEmitter implements Emitter {
if(functionNode == null) {
return slot == CompilerConstants.JAVA_THIS.slot();
}
- final int thisSlot = functionNode.getThisNode().getSymbol().getSlot();
+ final int thisSlot = compilerConstant(THIS).getSlot();
assert !functionNode.needsCallee() || thisSlot == 1; // needsCallee -> thisSlot == 1
assert functionNode.needsCallee() || thisSlot == 0; // !needsCallee -> thisSlot == 0
return slot == thisSlot;
}
/**
- * Push the this object to the stack.
- *
- * @return the method emitter
- */
- MethodEmitter loadThis() {
- load(functionNode.getThisNode().getSymbol());
- return this;
- }
-
- /**
- * Push the scope object to the stack.
- *
- * @return the method emitter
- */
- MethodEmitter loadScope() {
- if (peekType() == Type.SCOPE) {
- dup();
- return this;
- }
- load(functionNode.getScopeNode().getSymbol());
- return this;
- }
-
- /**
- * Push the return object to the stack.
- *
- * @return the method emitter
- */
- MethodEmitter loadResult() {
- load(functionNode.getResultNode().getSymbol());
- return this;
- }
-
-
- /**
* Push a method handle to the stack
*
* @param className class name
@@ -927,62 +896,33 @@ public class MethodEmitter implements Emitter {
return this;
}
- /**
- * Push the varargs object to the stack
- *
- * @return the method emitter
- */
- MethodEmitter loadVarArgs() {
- debug("load var args " + functionNode.getVarArgsNode().getSymbol());
- return load(functionNode.getVarArgsNode().getSymbol());
+ private Symbol compilerConstant(final CompilerConstants cc) {
+ return functionNode.getBody().getExistingSymbol(cc.symbolName());
}
/**
- * Push the arguments array to the stack
- *
- * @return the method emitter
+ * True if this method has a slot allocated for the scope variable (meaning, something in the method actually needs
+ * the scope).
+ * @return if this method has a slot allocated for the scope variable.
*/
- MethodEmitter loadArguments() {
- debug("load arguments ", functionNode.getArgumentsNode().getSymbol());
- assert functionNode.getArgumentsNode().getSymbol().getSlot() != 0;
- return load(functionNode.getArgumentsNode().getSymbol());
+ boolean hasScope() {
+ return compilerConstant(SCOPE).hasSlot();
}
- /**
- * Push the callee object to the stack
- *
- * @return the method emitter
- */
- MethodEmitter loadCallee() {
- final Symbol calleeSymbol = functionNode.getCalleeNode().getSymbol();
- debug("load callee ", calleeSymbol);
- assert calleeSymbol.getSlot() == 0 : "callee has wrong slot " + calleeSymbol.getSlot() + " in " + functionNode.getName();
-
- return load(calleeSymbol);
- }
-
- /**
- * Pop the scope from the stack and store it in its predefined slot
- */
- void storeScope() {
- debug("store scope");
- store(functionNode.getScopeNode().getSymbol());
+ MethodEmitter loadCompilerConstant(final CompilerConstants cc) {
+ final Symbol symbol = compilerConstant(cc);
+ if (cc == SCOPE && peekType() == Type.SCOPE) {
+ dup();
+ return this;
+ }
+ debug("load compiler constant ", symbol);
+ return load(symbol);
}
- /**
- * Pop the return from the stack and store it in its predefined slot
- */
- void storeResult() {
- debug("store result");
- store(functionNode.getResultNode().getSymbol());
- }
-
- /**
- * Pop the arguments array from the stack and store it in its predefined slot
- */
- void storeArguments() {
- debug("store arguments");
- store(functionNode.getArgumentsNode().getSymbol());
+ void storeCompilerConstant(final CompilerConstants cc) {
+ final Symbol symbol = compilerConstant(cc);
+ debug("store compiler constant ", symbol);
+ store(symbol);
}
/**
@@ -1030,13 +970,13 @@ public class MethodEmitter implements Emitter {
final int index = symbol.getFieldIndex();
if (functionNode.needsArguments()) {
debug("store symbol", symbol.getName(), " arguments index=", index);
- loadArguments();
+ loadCompilerConstant(ARGUMENTS);
load(index);
ArgumentSetter.SET_ARGUMENT.invoke(this);
} else {
// varargs without arguments object - just do array store to __varargs__
debug("store symbol", symbol.getName(), " array index=", index);
- loadVarArgs();
+ loadCompilerConstant(VARARGS);
load(index);
ArgumentSetter.SET_ARRAY_ELEMENT.invoke(this);
}
@@ -1144,7 +1084,7 @@ public class MethodEmitter implements Emitter {
* @return the method emitter
*/
MethodEmitter newarray(final ArrayType arrayType) {
- debug("newarray ", "arrayType=" + arrayType);
+ debug("newarray ", "arrayType=", arrayType);
popType(Type.INT); //LENGTH
pushType(arrayType.newarray(method));
return this;
@@ -1223,7 +1163,7 @@ public class MethodEmitter implements Emitter {
* @return the method emitter
*/
MethodEmitter invokespecial(final String className, final String methodName, final String methodDescriptor) {
- debug("invokespecial", className + "." + methodName + methodDescriptor);
+ debug("invokespecial", className, ".", methodName, methodDescriptor);
return invoke(INVOKESPECIAL, className, methodName, methodDescriptor, true);
}
@@ -1237,7 +1177,7 @@ public class MethodEmitter implements Emitter {
* @return the method emitter
*/
MethodEmitter invokevirtual(final String className, final String methodName, final String methodDescriptor) {
- debug("invokevirtual", className + "." + methodName + methodDescriptor + " " + stack);
+ debug("invokevirtual", className, ".", methodName, methodDescriptor, " ", stack);
return invoke(INVOKEVIRTUAL, className, methodName, methodDescriptor, true);
}
@@ -1251,7 +1191,7 @@ public class MethodEmitter implements Emitter {
* @return the method emitter
*/
MethodEmitter invokestatic(final String className, final String methodName, final String methodDescriptor) {
- debug("invokestatic", className + "." + methodName + methodDescriptor);
+ debug("invokestatic", className, ".", methodName, methodDescriptor);
invoke(INVOKESTATIC, className, methodName, methodDescriptor, false);
return this;
}
@@ -1284,7 +1224,7 @@ public class MethodEmitter implements Emitter {
* @return the method emitter
*/
MethodEmitter invokeinterface(final String className, final String methodName, final String methodDescriptor) {
- debug("invokeinterface", className + "." + methodName + methodDescriptor);
+ debug("invokeinterface", className, ".", methodName, methodDescriptor);
return invoke(INVOKEINTERFACE, className, methodName, methodDescriptor, true);
}
@@ -1336,15 +1276,20 @@ public class MethodEmitter implements Emitter {
*/
void conditionalJump(final Condition cond, final boolean isCmpG, final Label trueLabel) {
if (peekType().isCategory2()) {
- debug("[ld]cmp isCmpG=" + isCmpG);
+ debug("[ld]cmp isCmpG=", isCmpG);
pushType(get2n().cmp(method, isCmpG));
jump(Condition.toUnary(cond), trueLabel, 1);
} else {
- debug("if" + cond);
+ debug("if", cond);
jump(Condition.toBinary(cond, peekType().isObject()), trueLabel, 2);
}
}
+ MethodEmitter registerReturn() {
+ this.hasReturn = true;
+ return this;
+ }
+
/**
* Perform a non void return, popping the type from the stack
*
@@ -1385,22 +1330,7 @@ public class MethodEmitter implements Emitter {
*
* @param label destination label
*/
- void splitAwareGoto(final Label label) {
-
- if (splitNode != null) {
- final int index = splitNode.getExternalTargets().indexOf(label);
-
- if (index > -1) {
- loadScope();
- checkcast(Scope.class);
- load(index + 1);
- invoke(Scope.SET_SPLIT_STATE);
- loadUndefined(Type.OBJECT);
- _return(functionNode.getReturnType());
- return;
- }
- }
-
+ void splitAwareGoto(final LexicalContext lc, final Label label) {
_goto(label);
}
@@ -1595,7 +1525,7 @@ public class MethodEmitter implements Emitter {
*/
private void mergeStackTo(final Label label) {
final ArrayDeque<Type> labelStack = label.getStack();
- //debug(labelStack == null ? " >> Control flow - first visit " + label : " >> Control flow - JOIN with " + labelStack + " at " + label);
+ //debug(labelStack == null ? " >> Control flow - first visit ", label : " >> Control flow - JOIN with ", labelStack, " at ", label);
if (labelStack == null) {
assert stack != null;
label.setStack(stack.clone());
@@ -1788,7 +1718,7 @@ public class MethodEmitter implements Emitter {
* @return the method emitter
*/
MethodEmitter dynamicNew(final int argCount, final int flags) {
- debug("dynamic_new", "argcount=" + argCount);
+ debug("dynamic_new", "argcount=", argCount);
final String signature = getDynamicSignature(Type.OBJECT, argCount);
method.visitInvokeDynamicInsn("dyn:new", signature, LINKERBOOTSTRAP, flags);
pushType(Type.OBJECT); //TODO fix result type
@@ -1805,7 +1735,7 @@ public class MethodEmitter implements Emitter {
* @return the method emitter
*/
MethodEmitter dynamicCall(final Type returnType, final int argCount, final int flags) {
- debug("dynamic_call", "args=" + argCount, "returnType=" + returnType);
+ debug("dynamic_call", "args=", argCount, "returnType=", returnType);
final String signature = getDynamicSignature(returnType, argCount); // +1 because the function itself is the 1st parameter for dynamic calls (what you call - call target)
debug(" signature", signature);
method.visitInvokeDynamicInsn("dyn:call", signature, LINKERBOOTSTRAP, flags);
@@ -1824,7 +1754,7 @@ public class MethodEmitter implements Emitter {
* @return the method emitter
*/
MethodEmitter dynamicRuntimeCall(final String name, final Type returnType, final RuntimeNode.Request request) {
- debug("dynamic_runtime_call", name, "args=" + request.getArity(), "returnType=" + returnType);
+ debug("dynamic_runtime_call", name, "args=", request.getArity(), "returnType=", returnType);
final String signature = getDynamicSignature(returnType, request.getArity());
debug(" signature", signature);
method.visitInvokeDynamicInsn(name, signature, RUNTIMEBOOTSTRAP);
@@ -1895,7 +1825,7 @@ public class MethodEmitter implements Emitter {
* @return the method emitter
*/
MethodEmitter dynamicGetIndex(final Type result, final int flags, final boolean isMethod) {
- debug("dynamic_get_index", peekType(1) + "[" + peekType() + "]");
+ debug("dynamic_get_index", peekType(1), "[", peekType(), "]");
Type resultType = result;
if (result.isBoolean()) {
@@ -1931,7 +1861,7 @@ public class MethodEmitter implements Emitter {
* @param flags call site flags for setter
*/
void dynamicSetIndex(final int flags) {
- debug("dynamic_set_index", peekType(2) + "[" + peekType(1) + "] =", peekType());
+ debug("dynamic_set_index", peekType(2), "[", peekType(1), "] =", peekType());
Type value = peekType();
if (value.isObject() || value.isBoolean()) {
@@ -2031,7 +1961,7 @@ public class MethodEmitter implements Emitter {
* @return the method emitter
*/
MethodEmitter getField(final String className, final String fieldName, final String fieldDescriptor) {
- debug("getfield", "receiver=" + peekType(), className + "." + fieldName + fieldDescriptor);
+ debug("getfield", "receiver=", peekType(), className, ".", fieldName, fieldDescriptor);
final Type receiver = popType();
assert receiver.isObject();
method.visitFieldInsn(GETFIELD, className, fieldName, fieldDescriptor);
@@ -2049,7 +1979,7 @@ public class MethodEmitter implements Emitter {
* @return the method emitter
*/
MethodEmitter getStatic(final String className, final String fieldName, final String fieldDescriptor) {
- debug("getstatic", className + "." + fieldName + "." + fieldDescriptor);
+ debug("getstatic", className, ".", fieldName, ".", fieldDescriptor);
method.visitFieldInsn(GETSTATIC, className, fieldName, fieldDescriptor);
pushType(fieldType(fieldDescriptor));
return this;
@@ -2063,7 +1993,7 @@ public class MethodEmitter implements Emitter {
* @param fieldDescriptor field descriptor
*/
void putField(final String className, final String fieldName, final String fieldDescriptor) {
- debug("putfield", "receiver=" + peekType(1), "value=" + peekType());
+ debug("putfield", "receiver=", peekType(1), "value=", peekType());
popType(fieldType(fieldDescriptor));
popType(Type.OBJECT);
method.visitFieldInsn(PUTFIELD, className, fieldName, fieldDescriptor);
@@ -2077,7 +2007,7 @@ public class MethodEmitter implements Emitter {
* @param fieldDescriptor field descriptor
*/
void putStatic(final String className, final String fieldName, final String fieldDescriptor) {
- debug("putfield", "value=" + peekType());
+ debug("putfield", "value=", peekType());
popType(fieldType(fieldDescriptor));
method.visitFieldInsn(PUTSTATIC, className, fieldName, fieldDescriptor);
}
@@ -2237,7 +2167,7 @@ public class MethodEmitter implements Emitter {
}
if (env != null) { //early bootstrap code doesn't have inited context yet
- LOG.info(sb.toString());
+ LOG.info(sb);
if (DEBUG_TRACE_LINE == linePrefix) {
new Throwable().printStackTrace(LOG.getOutputStream());
}
@@ -2254,21 +2184,12 @@ public class MethodEmitter implements Emitter {
this.functionNode = functionNode;
}
- /**
- * Get the split node for this method emitter, if this is code
- * generation due to splitting large methods
- *
- * @return split node
- */
- SplitNode getSplitNode() {
- return splitNode;
+ boolean hasReturn() {
+ return hasReturn;
}
- /**
- * Set the split node for this method emitter
- * @param splitNode split node
- */
- void setSplitNode(final SplitNode splitNode) {
- this.splitNode = splitNode;
+ List<Label> getExternalTargets() {
+ return null;
}
+
}
diff --git a/src/jdk/nashorn/internal/codegen/Namespace.java b/src/jdk/nashorn/internal/codegen/Namespace.java
index 02fdb942..5de2fdf8 100644
--- a/src/jdk/nashorn/internal/codegen/Namespace.java
+++ b/src/jdk/nashorn/internal/codegen/Namespace.java
@@ -53,7 +53,7 @@ public class Namespace {
*/
public Namespace(final Namespace parent) {
this.parent = parent;
- directory = new HashMap<>();
+ this.directory = new HashMap<>();
}
/**
@@ -65,10 +65,6 @@ public class Namespace {
return parent;
}
- private HashMap<String, Integer> getDirectory() {
- return directory;
- }
-
/**
* Create a uniqueName name in the namespace in the form base$n where n varies
* .
@@ -78,7 +74,7 @@ public class Namespace {
*/
public String uniqueName(final String base) {
for (Namespace namespace = this; namespace != null; namespace = namespace.getParent()) {
- final HashMap<String, Integer> namespaceDirectory = namespace.getDirectory();
+ final HashMap<String, Integer> namespaceDirectory = namespace.directory;
final Integer counter = namespaceDirectory.get(base);
if (counter != null) {
diff --git a/src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java b/src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java
index 6e032e2c..b7514526 100644
--- a/src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java
+++ b/src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java
@@ -28,10 +28,10 @@ package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.codegen.Compiler.SCRIPTS_PACKAGE;
import static jdk.nashorn.internal.codegen.CompilerConstants.ALLOCATE;
import static jdk.nashorn.internal.codegen.CompilerConstants.INIT_ARGUMENTS;
+import static jdk.nashorn.internal.codegen.CompilerConstants.INIT_MAP;
import static jdk.nashorn.internal.codegen.CompilerConstants.INIT_SCOPE;
import static jdk.nashorn.internal.codegen.CompilerConstants.JAVA_THIS;
import static jdk.nashorn.internal.codegen.CompilerConstants.JS_OBJECT_PREFIX;
-import static jdk.nashorn.internal.codegen.CompilerConstants.MAP;
import static jdk.nashorn.internal.codegen.CompilerConstants.className;
import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
import static jdk.nashorn.internal.lookup.Lookup.MH;
@@ -204,8 +204,8 @@ public final class ObjectClassGenerator {
* @return The class name.
*/
public static String getClassName(final int fieldCount) {
- return fieldCount != 0 ? SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.tag() + fieldCount :
- SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.tag();
+ return fieldCount != 0 ? SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.symbolName() + fieldCount :
+ SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.symbolName();
}
/**
@@ -218,7 +218,23 @@ public final class ObjectClassGenerator {
* @return The class name.
*/
public static String getClassName(final int fieldCount, final int paramCount) {
- return SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.tag() + fieldCount + SCOPE_MARKER + paramCount;
+ return SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.symbolName() + fieldCount + SCOPE_MARKER + paramCount;
+ }
+
+ /**
+ * Returns the number of fields in the JavaScript scope class. Its name had to be generated using either
+ * {@link #getClassName(int)} or {@link #getClassName(int, int)}.
+ * @param clazz the JavaScript scope class.
+ * @return the number of fields in the scope class.
+ */
+ public static int getFieldCount(Class<?> clazz) {
+ final String name = clazz.getSimpleName();
+ final String prefix = JS_OBJECT_PREFIX.symbolName();
+ if(prefix.equals(name)) {
+ return 0;
+ }
+ final int scopeMarker = name.indexOf(SCOPE_MARKER);
+ return Integer.parseInt(scopeMarker == -1 ? name.substring(prefix.length()) : name.substring(prefix.length(), scopeMarker));
}
/**
@@ -387,7 +403,7 @@ public final class ObjectClassGenerator {
final MethodEmitter init = classEmitter.init(PropertyMap.class);
init.begin();
init.load(Type.OBJECT, JAVA_THIS.slot());
- init.load(Type.OBJECT, MAP.slot());
+ init.load(Type.OBJECT, INIT_MAP.slot());
init.invoke(constructorNoLookup(ScriptObject.class, PropertyMap.class));
return init;
@@ -402,7 +418,7 @@ public final class ObjectClassGenerator {
final MethodEmitter init = classEmitter.init(PropertyMap.class, ScriptObject.class);
init.begin();
init.load(Type.OBJECT, JAVA_THIS.slot());
- init.load(Type.OBJECT, MAP.slot());
+ init.load(Type.OBJECT, INIT_MAP.slot());
init.load(Type.OBJECT, INIT_SCOPE.slot());
init.invoke(constructorNoLookup(FunctionScope.class, PropertyMap.class, ScriptObject.class));
@@ -418,7 +434,7 @@ public final class ObjectClassGenerator {
final MethodEmitter init = classEmitter.init(PropertyMap.class, ScriptObject.class, Object.class);
init.begin();
init.load(Type.OBJECT, JAVA_THIS.slot());
- init.load(Type.OBJECT, MAP.slot());
+ init.load(Type.OBJECT, INIT_MAP.slot());
init.load(Type.OBJECT, INIT_SCOPE.slot());
init.load(Type.OBJECT, INIT_ARGUMENTS.slot());
init.invoke(constructorNoLookup(FunctionScope.class, PropertyMap.class, ScriptObject.class, Object.class));
@@ -449,7 +465,7 @@ public final class ObjectClassGenerator {
* @param className Name of JavaScript class.
*/
private static void newAllocate(final ClassEmitter classEmitter, final String className) {
- final MethodEmitter allocate = classEmitter.method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), ALLOCATE.tag(), ScriptObject.class, PropertyMap.class);
+ final MethodEmitter allocate = classEmitter.method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), ALLOCATE.symbolName(), ScriptObject.class, PropertyMap.class);
allocate.begin();
allocate._new(className);
allocate.dup();
diff --git a/src/jdk/nashorn/internal/codegen/ObjectCreator.java b/src/jdk/nashorn/internal/codegen/ObjectCreator.java
index e4e7b7da..a9c494cc 100644
--- a/src/jdk/nashorn/internal/codegen/ObjectCreator.java
+++ b/src/jdk/nashorn/internal/codegen/ObjectCreator.java
@@ -36,7 +36,7 @@ import jdk.nashorn.internal.runtime.PropertyMap;
public abstract class ObjectCreator {
/** Compile unit for this ObjectCreator, see CompileUnit */
- protected final CompileUnit compileUnit;
+ //protected final CompileUnit compileUnit;
/** List of keys to initiate in this ObjectCreator */
protected final List<String> keys;
@@ -66,7 +66,6 @@ public abstract class ObjectCreator {
*/
protected ObjectCreator(final CodeGenerator codegen, final List<String> keys, final List<Symbol> symbols, final boolean isScope, final boolean hasArguments) {
this.codegen = codegen;
- this.compileUnit = codegen.getCurrentCompileUnit();
this.keys = keys;
this.symbols = symbols;
this.isScope = isScope;
diff --git a/src/jdk/nashorn/internal/codegen/SplitMethodEmitter.java b/src/jdk/nashorn/internal/codegen/SplitMethodEmitter.java
new file mode 100644
index 00000000..5fa4486a
--- /dev/null
+++ b/src/jdk/nashorn/internal/codegen/SplitMethodEmitter.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.codegen;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
+
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.nashorn.internal.codegen.types.Type;
+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<>();
+
+ SplitMethodEmitter(final ClassEmitter classEmitter, final MethodVisitor mv, SplitNode splitNode) {
+ super(classEmitter, mv);
+ this.splitNode = splitNode;
+ }
+
+ @Override
+ void splitAwareGoto(final LexicalContext lc, final Label label) {
+ assert splitNode != null;
+ final int index = findExternalTarget(lc, label);
+ 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);
+ }
+
+ private int findExternalTarget(final LexicalContext lc, final Label label) {
+ final int index = externalTargets.indexOf(label);
+
+ if (index >= 0) {
+ return index;
+ }
+
+ if (lc.isExternalTarget(splitNode, label)) {
+ externalTargets.add(label);
+ return externalTargets.size() - 1;
+ }
+ return -1;
+ }
+
+ @Override
+ MethodEmitter registerReturn() {
+ super.registerReturn();
+ loadCompilerConstant(SCOPE);
+ checkcast(Scope.class);
+ load(0);
+ invoke(Scope.SET_SPLIT_STATE);
+ return this;
+ }
+
+ @Override
+ final List<Label> getExternalTargets() {
+ return externalTargets;
+ }
+}
diff --git a/src/jdk/nashorn/internal/codegen/Splitter.java b/src/jdk/nashorn/internal/codegen/Splitter.java
index f9a84f91..f482e4df 100644
--- a/src/jdk/nashorn/internal/codegen/Splitter.java
+++ b/src/jdk/nashorn/internal/codegen/Splitter.java
@@ -28,29 +28,18 @@ package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.codegen.CompilerConstants.SPLIT_PREFIX;
import java.util.ArrayList;
-import java.util.Deque;
import java.util.HashMap;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import jdk.nashorn.internal.ir.Block;
-import jdk.nashorn.internal.ir.BreakNode;
-import jdk.nashorn.internal.ir.ContinueNode;
-import jdk.nashorn.internal.ir.DoWhileNode;
-import jdk.nashorn.internal.ir.ForNode;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
-import jdk.nashorn.internal.ir.LabelNode;
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.ReturnNode;
import jdk.nashorn.internal.ir.SplitNode;
-import jdk.nashorn.internal.ir.SwitchNode;
-import jdk.nashorn.internal.ir.WhileNode;
-import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.DebugLogger;
import jdk.nashorn.internal.runtime.Source;
@@ -64,7 +53,7 @@ final class Splitter extends NodeVisitor {
private final Compiler compiler;
/** IR to be broken down. */
- private final FunctionNode functionNode;
+ private FunctionNode outermost;
/** Compile unit for the main script. */
private final CompileUnit outermostCompileUnit;
@@ -72,8 +61,6 @@ final class Splitter extends NodeVisitor {
/** Cache for calculated block weights. */
private final Map<Node, Long> weightCache = new HashMap<>();
- private final LexicalContext lexicalContext = new LexicalContext();
-
/** Weight threshold for when to start a split. */
public static final long SPLIT_THRESHOLD = Options.getIntProperty("nashorn.compiler.splitter.threshold", 32 * 1024);
@@ -88,70 +75,92 @@ final class Splitter extends NodeVisitor {
*/
public Splitter(final Compiler compiler, final FunctionNode functionNode, final CompileUnit outermostCompileUnit) {
this.compiler = compiler;
- this.functionNode = functionNode;
+ this.outermost = functionNode;
this.outermostCompileUnit = outermostCompileUnit;
}
/**
* Execute the split
*/
- void split() {
+ FunctionNode split(final FunctionNode fn) {
+ FunctionNode functionNode = fn;
+
if (functionNode.isLazy()) {
- LOG.finest("Postponing split of '" + functionNode.getName() + "' as it's lazy");
- return;
+ LOG.finest("Postponing split of '", functionNode.getName(), "' as it's lazy");
+ return functionNode;
}
- LOG.finest("Initiating split of '" + functionNode.getName() + "'");
+ LOG.finest("Initiating split of '", functionNode.getName(), "'");
+
+ final LexicalContext lc = getLexicalContext();
long weight = WeighNodes.weigh(functionNode);
+ final boolean top = compiler.getFunctionNode() == outermost;
if (weight >= SPLIT_THRESHOLD) {
- LOG.finest("Splitting '" + functionNode.getName() + "' as its weight " + weight + " exceeds split threshold " + SPLIT_THRESHOLD);
-
- functionNode.accept(this);
+ LOG.finest("Splitting '", functionNode.getName(), "' as its weight ", weight, " exceeds split threshold ", SPLIT_THRESHOLD);
+ functionNode = (FunctionNode)functionNode.accept(this);
if (functionNode.isSplit()) {
// Weight has changed so weigh again, this time using block weight cache
weight = WeighNodes.weigh(functionNode, weightCache);
+ functionNode = functionNode.setBody(lc, functionNode.getBody().setNeedsScope(lc));
}
if (weight >= SPLIT_THRESHOLD) {
- weight = splitBlock(functionNode, functionNode);
- }
-
- if (functionNode.isSplit()) {
- functionNode.accept(new SplitFlowAnalyzer());
+ functionNode = functionNode.setBody(lc, splitBlock(functionNode.getBody(), functionNode));
+ weight = WeighNodes.weigh(functionNode.getBody(), weightCache);
}
}
- assert functionNode.getCompileUnit() == null : "compile unit already set";
+ assert functionNode.getCompileUnit() == null : "compile unit already set for " + functionNode.getName();
- if (compiler.getFunctionNode() == functionNode) { //functionNode.isScript()) {
+ if (top) {
assert outermostCompileUnit != null : "outermost compile unit is null";
-
- functionNode.setCompileUnit(outermostCompileUnit);
+ functionNode = functionNode.setCompileUnit(lc, outermostCompileUnit);
outermostCompileUnit.addWeight(weight + WeighNodes.FUNCTION_WEIGHT);
} else {
- functionNode.setCompileUnit(findUnit(weight));
+ functionNode = functionNode.setCompileUnit(lc, findUnit(weight));
}
- // Recursively split nested functions
- functionNode.accept(new NodeOperatorVisitor() {
+ final Block body = functionNode.getBody();
+ final List<FunctionNode> dc = directChildren(functionNode);
+
+ final Block newBody = (Block)body.accept(new NodeVisitor() {
+ @Override
+ public boolean enterFunctionNode(final FunctionNode nestedFunction) {
+ return dc.contains(nestedFunction);
+ }
+
+ @Override
+ public Node leaveFunctionNode(final FunctionNode nestedFunction) {
+ FunctionNode split = new Splitter(compiler, nestedFunction, outermostCompileUnit).split(nestedFunction);
+ getLexicalContext().replace(nestedFunction, split);
+ return split;
+ }
+ });
+ functionNode = functionNode.setBody(lc, newBody);
+
+ assert functionNode.getCompileUnit() != null;
+
+ return functionNode.setState(lc, CompilationState.SPLIT);
+ }
+
+ private static List<FunctionNode> directChildren(final FunctionNode functionNode) {
+ final List<FunctionNode> dc = new ArrayList<>();
+ functionNode.accept(new NodeVisitor() {
@Override
- public Node enterFunctionNode(FunctionNode function) {
- if(function == functionNode) {
- // Don't process outermost function (it was already processed) but descend into it to find nested
- // functions.
- return function;
+ public boolean enterFunctionNode(final FunctionNode child) {
+ if (child == functionNode) {
+ return true;
}
- // Process a nested function
- new Splitter(compiler, function, outermostCompileUnit).split();
- // Don't descend into a a nested function; Splitter.split() has taken care of nested-in-nested functions.
- return null;
+ if (getLexicalContext().getParentFunction(child) == functionNode) {
+ dc.add(child);
+ }
+ return false;
}
});
-
- functionNode.setState(CompilationState.SPLIT);
+ return dc;
}
/**
@@ -170,8 +179,8 @@ final class Splitter extends NodeVisitor {
*
* @return new weight for the resulting block.
*/
- private long splitBlock(final Block block, final FunctionNode function) {
- functionNode.setIsSplit();
+ private Block splitBlock(final Block block, final FunctionNode function) {
+ getLexicalContext().setFlag(getLexicalContext().getCurrentFunction(), FunctionNode.IS_SPLIT);
final List<Node> splits = new ArrayList<>();
List<Node> statements = new ArrayList<>();
@@ -186,7 +195,6 @@ final class Splitter extends NodeVisitor {
statements = new ArrayList<>();
statementsWeight = 0;
}
-
}
if (statement.isTerminal()) {
@@ -201,9 +209,7 @@ final class Splitter extends NodeVisitor {
splits.add(createBlockSplitNode(block, function, statements, statementsWeight));
}
- block.setStatements(splits);
-
- return WeighNodes.weigh(block, weightCache);
+ return block.setStatements(getLexicalContext(), splits);
}
/**
@@ -218,51 +224,44 @@ final class Splitter extends NodeVisitor {
final Source source = parent.getSource();
final long token = parent.getToken();
final int finish = parent.getFinish();
- final String name = function.uniqueName(SPLIT_PREFIX.tag());
+ final String name = function.uniqueName(SPLIT_PREFIX.symbolName());
- final Block newBlock = new Block(source, token, finish);
- newBlock.setFrame(new Frame(parent.getFrame()));
- newBlock.setStatements(statements);
+ final Block newBlock = new Block(source, token, finish, statements);
- final SplitNode splitNode = new SplitNode(name, functionNode, newBlock);
-
- splitNode.setCompileUnit(compiler.findUnit(weight + WeighNodes.FUNCTION_WEIGHT));
-
- return splitNode;
+ return new SplitNode(name, newBlock, compiler.findUnit(weight + WeighNodes.FUNCTION_WEIGHT));
}
@Override
- public Node enterBlock(final Block block) {
+ public boolean enterBlock(final Block block) {
if (block.isCatchBlock()) {
- return null;
+ return false;
}
- lexicalContext.push(block);
final long weight = WeighNodes.weigh(block, weightCache);
if (weight < SPLIT_THRESHOLD) {
weightCache.put(block, weight);
- lexicalContext.pop(block);
- return null;
+ return false;
}
- return block;
+ return true;
}
@Override
public Node leaveBlock(final Block block) {
assert !block.isCatchBlock();
+ Block newBlock = block;
+
// Block was heavier than SLIT_THRESHOLD in enter, but a sub-block may have
// been split already, so weigh again before splitting.
long weight = WeighNodes.weigh(block, weightCache);
if (weight >= SPLIT_THRESHOLD) {
- weight = splitBlock(block, lexicalContext.getFunction(block));
+ newBlock = splitBlock(block, getLexicalContext().getFunction(block));
+ weight = WeighNodes.weigh(newBlock, weightCache);
}
- weightCache.put(block, weight);
-
- lexicalContext.pop(block);
- return block;
+ weightCache.put(newBlock, weight);
+ return newBlock;
}
@SuppressWarnings("rawtypes")
@@ -274,7 +273,7 @@ final class Splitter extends NodeVisitor {
return literal;
}
- functionNode.setIsSplit();
+ getLexicalContext().setFlag(getLexicalContext().getCurrentFunction(), FunctionNode.IS_SPLIT);
if (literal instanceof ArrayLiteralNode) {
final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode) literal;
@@ -312,123 +311,12 @@ final class Splitter extends NodeVisitor {
}
@Override
- public Node enterFunctionNode(final FunctionNode node) {
- if(node == functionNode && !node.isLazy()) {
- lexicalContext.push(node);
- node.visitStatements(this);
- lexicalContext.pop(node);
- }
- return null;
- }
-
- static class SplitFlowAnalyzer extends NodeVisitor {
-
- /** Stack of visited Split nodes, deepest node first. */
- private final Deque<SplitNode> splitStack;
-
- /** Map of possible jump targets to containing split node */
- private final Map<Node,SplitNode> targetNodes = new HashMap<>();
-
- SplitFlowAnalyzer() {
- this.splitStack = new LinkedList<>();
- }
-
- @Override
- public Node enterLabelNode(final LabelNode labelNode) {
- registerJumpTarget(labelNode.getBreakNode());
- registerJumpTarget(labelNode.getContinueNode());
- return labelNode;
- }
-
- @Override
- public Node enterWhileNode(final WhileNode whileNode) {
- registerJumpTarget(whileNode);
- return whileNode;
- }
-
- @Override
- public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
- registerJumpTarget(doWhileNode);
- return doWhileNode;
- }
-
- @Override
- public Node enterForNode(final ForNode forNode) {
- registerJumpTarget(forNode);
- return forNode;
- }
-
- @Override
- public Node enterSwitchNode(final SwitchNode switchNode) {
- registerJumpTarget(switchNode);
- return switchNode;
- }
-
- @Override
- public Node enterReturnNode(final ReturnNode returnNode) {
- for (final SplitNode split : splitStack) {
- split.setHasReturn(true);
- }
- return returnNode;
- }
-
- @Override
- public Node enterContinueNode(final ContinueNode continueNode) {
- searchJumpTarget(continueNode.getTargetNode(), continueNode.getTargetLabel());
- return continueNode;
- }
-
- @Override
- public Node enterBreakNode(final BreakNode breakNode) {
- searchJumpTarget(breakNode.getTargetNode(), breakNode.getTargetLabel());
- return breakNode;
- }
-
- @Override
- public Node enterSplitNode(final SplitNode splitNode) {
- splitStack.addFirst(splitNode);
- return splitNode;
- }
-
- @Override
- public Node leaveSplitNode(final SplitNode splitNode) {
- assert splitNode == splitStack.peekFirst();
- splitStack.removeFirst();
- return splitNode;
- }
-
- /**
- * Register the split node containing a potential jump target.
- * @param targetNode a potential target node.
- */
- private void registerJumpTarget(final Node targetNode) {
- final SplitNode splitNode = splitStack.peekFirst();
- if (splitNode != null) {
- targetNodes.put(targetNode, splitNode);
- }
- }
-
- /**
- * Check if a jump target is outside the current split node and its parent split nodes.
- * @param targetNode the jump target node.
- * @param targetLabel the jump target label.
- */
- private void searchJumpTarget(final Node targetNode, final Label targetLabel) {
-
- final SplitNode targetSplit = targetNodes.get(targetNode);
- // Note that targetSplit may be null, indicating that targetNode is in top level method.
- // In this case we have to add the external jump target to all split nodes.
-
- for (final SplitNode split : splitStack) {
- if (split == targetSplit) {
- break;
- }
- final List<Label> externalTargets = split.getExternalTargets();
- if (!externalTargets.contains(targetLabel)) {
- split.addExternalTarget(targetLabel);
- }
- }
+ public boolean enterFunctionNode(final FunctionNode node) {
+ //only go into the function node for this splitter. any subfunctions are rejected
+ if (node == outermost && !node.isLazy()) {
+ return true;
}
+ return false;
}
}
diff --git a/src/jdk/nashorn/internal/codegen/WeighNodes.java b/src/jdk/nashorn/internal/codegen/WeighNodes.java
index 18bd9552..002cc902 100644
--- a/src/jdk/nashorn/internal/codegen/WeighNodes.java
+++ b/src/jdk/nashorn/internal/codegen/WeighNodes.java
@@ -35,7 +35,6 @@ import jdk.nashorn.internal.ir.BreakNode;
import jdk.nashorn.internal.ir.CallNode;
import jdk.nashorn.internal.ir.CatchNode;
import jdk.nashorn.internal.ir.ContinueNode;
-import jdk.nashorn.internal.ir.DoWhileNode;
import jdk.nashorn.internal.ir.ExecuteNode;
import jdk.nashorn.internal.ir.ForNode;
import jdk.nashorn.internal.ir.FunctionNode;
@@ -101,7 +100,7 @@ final class WeighNodes extends NodeOperatorVisitor {
* @param weightCache cache of already calculated block weights
*/
private WeighNodes(FunctionNode topFunction, final Map<Node, Long> weightCache) {
- super(null, null);
+ super();
this.topFunction = topFunction;
this.weightCache = weightCache;
}
@@ -123,13 +122,13 @@ final class WeighNodes extends NodeOperatorVisitor {
}
@Override
- public Node enterBlock(final Block block) {
+ public boolean enterBlock(final Block block) {
if (weightCache != null && weightCache.containsKey(block)) {
weight += weightCache.get(block);
- return null;
+ return false;
}
- return block;
+ return true;
}
@Override
@@ -157,12 +156,6 @@ final class WeighNodes extends NodeOperatorVisitor {
}
@Override
- public Node leaveDoWhileNode(final DoWhileNode doWhileNode) {
- weight += LOOP_WEIGHT;
- return doWhileNode;
- }
-
- @Override
public Node leaveExecuteNode(final ExecuteNode executeNode) {
return executeNode;
}
@@ -174,15 +167,15 @@ final class WeighNodes extends NodeOperatorVisitor {
}
@Override
- public Node enterFunctionNode(final FunctionNode functionNode) {
- if(functionNode == topFunction) {
+ public boolean enterFunctionNode(final FunctionNode functionNode) {
+ if (functionNode == topFunction) {
// the function being weighted; descend into its statements
- functionNode.visitStatements(this);
- } else {
- // just a reference to inner function from outer function
- weight += FUNC_EXPR_WEIGHT;
+ return true;
+// functionNode.visitStatements(this);
}
- return null;
+ // just a reference to inner function from outer function
+ weight += FUNC_EXPR_WEIGHT;
+ return false;
}
@Override
@@ -205,7 +198,7 @@ final class WeighNodes extends NodeOperatorVisitor {
@SuppressWarnings("rawtypes")
@Override
- public Node enterLiteralNode(final LiteralNode literalNode) {
+ public boolean enterLiteralNode(final LiteralNode literalNode) {
weight += LITERAL_WEIGHT;
if (literalNode instanceof ArrayLiteralNode) {
@@ -224,10 +217,10 @@ final class WeighNodes extends NodeOperatorVisitor {
}
}
- return null;
+ return false;
}
- return literalNode;
+ return true;
}
@Override
@@ -249,9 +242,9 @@ final class WeighNodes extends NodeOperatorVisitor {
}
@Override
- public Node enterSplitNode(final SplitNode splitNode) {
+ public boolean enterSplitNode(final SplitNode splitNode) {
weight += SPLIT_WEIGHT;
- return null;
+ return false;
}
@Override
diff --git a/src/jdk/nashorn/internal/codegen/types/BooleanType.java b/src/jdk/nashorn/internal/codegen/types/BooleanType.java
index c27c0584..4331cb55 100644
--- a/src/jdk/nashorn/internal/codegen/types/BooleanType.java
+++ b/src/jdk/nashorn/internal/codegen/types/BooleanType.java
@@ -93,12 +93,6 @@ public final class BooleanType extends Type {
}
@Override
- public Type loadEmpty(final MethodVisitor method) {
- assert false : "unsupported operation";
- return null;
- }
-
- @Override
public void _return(final MethodVisitor method) {
method.visitInsn(IRETURN);
}
diff --git a/src/jdk/nashorn/internal/codegen/types/IntType.java b/src/jdk/nashorn/internal/codegen/types/IntType.java
index 9b859ddd..c282d109 100644
--- a/src/jdk/nashorn/internal/codegen/types/IntType.java
+++ b/src/jdk/nashorn/internal/codegen/types/IntType.java
@@ -241,12 +241,6 @@ class IntType extends BitwiseType {
}
@Override
- public Type loadEmpty(final MethodVisitor method) {
- assert false : "unsupported operation";
- return null;
- }
-
- @Override
public Type cmp(final MethodVisitor method, final boolean isCmpG) {
assert false : "unsupported operation";
return null;
diff --git a/src/jdk/nashorn/internal/codegen/types/LongType.java b/src/jdk/nashorn/internal/codegen/types/LongType.java
index d3e00b67..b57ea6d3 100644
--- a/src/jdk/nashorn/internal/codegen/types/LongType.java
+++ b/src/jdk/nashorn/internal/codegen/types/LongType.java
@@ -217,12 +217,6 @@ class LongType extends BitwiseType {
}
@Override
- public Type loadEmpty(final MethodVisitor method) {
- assert false : "unsupported operation";
- return null;
- }
-
- @Override
public Type cmp(final MethodVisitor method, final boolean isCmpG) {
return cmp(method);
}
diff --git a/src/jdk/nashorn/internal/codegen/types/NumberType.java b/src/jdk/nashorn/internal/codegen/types/NumberType.java
index 4dc62c52..6a280b79 100644
--- a/src/jdk/nashorn/internal/codegen/types/NumberType.java
+++ b/src/jdk/nashorn/internal/codegen/types/NumberType.java
@@ -89,12 +89,6 @@ class NumberType extends NumericType {
}
@Override
- public Type loadEmpty(final MethodVisitor method) {
- assert false : "unsupported operation";
- return null;
- }
-
- @Override
public Type ldc(final MethodVisitor method, final Object c) {
assert c instanceof Double;
diff --git a/src/jdk/nashorn/internal/codegen/types/Type.java b/src/jdk/nashorn/internal/codegen/types/Type.java
index 77558835..747793e8 100644
--- a/src/jdk/nashorn/internal/codegen/types/Type.java
+++ b/src/jdk/nashorn/internal/codegen/types/Type.java
@@ -616,6 +616,12 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
return this;
}
+ @Override
+ public Type loadEmpty(final MethodVisitor method) {
+ assert false : "unsupported operation";
+ return null;
+ }
+
/**
* Superclass logic for pop for all types
*
@@ -663,7 +669,6 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
method.visitInsn(SWAP);
}
}
-
}
/**
@@ -841,12 +846,6 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
}
@Override
- public Type loadEmpty(final MethodVisitor method) {
- assert false : "unsupported operation";
- return null;
- }
-
- @Override
public Type convert(final MethodVisitor method, final Type to) {
assert false : "unsupported operation";
return null;
diff --git a/src/jdk/nashorn/internal/ir/AccessNode.java b/src/jdk/nashorn/internal/ir/AccessNode.java
index b7b76684..2f739bf0 100644
--- a/src/jdk/nashorn/internal/ir/AccessNode.java
+++ b/src/jdk/nashorn/internal/ir/AccessNode.java
@@ -25,23 +25,18 @@
package jdk.nashorn.internal.ir;
-import static jdk.nashorn.internal.codegen.ObjectClassGenerator.DEBUG_FIELDS;
-
-import jdk.nashorn.internal.codegen.ObjectClassGenerator;
import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
/**
* IR representation of a property access (period operator.)
- *
*/
-public class AccessNode extends BaseNode implements TypeOverride<AccessNode> {
+@Immutable
+public final class AccessNode extends BaseNode {
/** Property ident. */
- private IdentNode property;
-
- /** Does this node have a type override */
- private boolean hasCallSiteType;
+ private final IdentNode property;
/**
* Constructor
@@ -53,49 +48,13 @@ public class AccessNode extends BaseNode implements TypeOverride<AccessNode> {
* @param property property
*/
public AccessNode(final Source source, final long token, final int finish, final Node base, final IdentNode property) {
- super(source, token, finish, base);
-
- this.start = base.getStart();
+ super(source, token, finish, base, false, false);
this.property = property.setIsPropertyName();
}
- /**
- * Copy constructor
- *
- * @param accessNode source node
- */
- public AccessNode(final AccessNode accessNode) {
- this(accessNode, new CopyState());
- }
-
- /**
- * Internal copy constructor
- *
- * @param accessNode source node
- * @param cs copy state
- */
- protected AccessNode(final AccessNode accessNode, final CopyState cs) {
- super(accessNode, cs);
- this.property = (IdentNode)cs.existingOrCopy(accessNode.getProperty());
- }
-
- @Override
- protected Node copy(final CopyState cs) {
- return new AccessNode(this, cs);
- }
-
- @Override
- public boolean equals(final Object other) {
- if (!super.equals(other)) {
- return false;
- }
- final AccessNode accessNode = (AccessNode)other;
- return property.equals(accessNode.getProperty());
- }
-
- @Override
- public int hashCode() {
- return super.hashCode() ^ property.hashCode();
+ private AccessNode(final AccessNode accessNode, final Node base, final IdentNode property, final boolean isFunction, final boolean hasCallSiteType) {
+ super(accessNode, base, isFunction, hasCallSiteType);
+ this.property = property;
}
/**
@@ -104,12 +63,11 @@ public class AccessNode extends BaseNode implements TypeOverride<AccessNode> {
*/
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterAccessNode(this) != null) {
- base = base.accept(visitor);
- property = (IdentNode)property.accept(visitor);
- return visitor.leaveAccessNode(this);
+ if (visitor.enterAccessNode(this)) {
+ return visitor.leaveAccessNode(
+ setBase(base.accept(visitor)).
+ setProperty((IdentNode)property.accept(visitor)));
}
-
return this;
}
@@ -117,7 +75,7 @@ public class AccessNode extends BaseNode implements TypeOverride<AccessNode> {
public void toString(final StringBuilder sb) {
final boolean needsParen = tokenType().needsParens(getBase().tokenType(), true);
- if (hasCallSiteType) {
+ if (hasCallSiteType()) {
sb.append('{');
final String desc = getType().getDescriptor();
sb.append(desc.charAt(desc.length() - 1) == ';' ? "O" : getType().getDescriptor());
@@ -147,19 +105,34 @@ public class AccessNode extends BaseNode implements TypeOverride<AccessNode> {
return property;
}
+ private AccessNode setBase(final Node base) {
+ if (this.base == base) {
+ return this;
+ }
+ return new AccessNode(this, base, property, isFunction(), hasCallSiteType());
+ }
+
+
+ private AccessNode setProperty(final IdentNode property) {
+ if (this.property == property) {
+ return this;
+ }
+ return new AccessNode(this, base, property, isFunction(), hasCallSiteType());
+ }
+
@Override
public AccessNode setType(final Type type) {
- if (DEBUG_FIELDS && !Type.areEquivalent(getSymbol().getSymbolType(), type)) {
- ObjectClassGenerator.LOG.info(getClass().getName() + " " + this + " => " + type + " instead of " + getType());
- }
- property = property.setType(type);
+ logTypeChange(type);
getSymbol().setTypeOverride(type); //always a temp so this is fine.
- hasCallSiteType = true;
- return this;
+ return new AccessNode(this, base, property.setType(type), isFunction(), hasCallSiteType());
}
@Override
- public boolean canHaveCallSiteType() {
- return true; //carried by the symbol and always the same nodetype==symboltype
+ public BaseNode setIsFunction() {
+ if (isFunction()) {
+ return this;
+ }
+ return new AccessNode(this, base, property, true, hasCallSiteType());
}
+
}
diff --git a/src/jdk/nashorn/internal/ir/BaseNode.java b/src/jdk/nashorn/internal/ir/BaseNode.java
index 26a28368..5e5bfb13 100644
--- a/src/jdk/nashorn/internal/ir/BaseNode.java
+++ b/src/jdk/nashorn/internal/ir/BaseNode.java
@@ -25,6 +25,10 @@
package jdk.nashorn.internal.ir;
+import static jdk.nashorn.internal.codegen.ObjectClassGenerator.DEBUG_FIELDS;
+import jdk.nashorn.internal.codegen.ObjectClassGenerator;
+import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.runtime.Source;
/**
@@ -33,12 +37,15 @@ import jdk.nashorn.internal.runtime.Source;
* @see AccessNode
* @see IndexNode
*/
-public abstract class BaseNode extends Node implements FunctionCall {
+@Immutable
+public abstract class BaseNode extends Node implements FunctionCall, TypeOverride<BaseNode> {
/** Base Node. */
- protected Node base;
+ protected final Node base;
- private boolean function;
+ private final boolean isFunction;
+
+ private final boolean hasCallSiteType;
/**
* Constructor
@@ -47,37 +54,28 @@ public abstract class BaseNode extends Node implements FunctionCall {
* @param token token
* @param finish finish
* @param base base node
+ * @param isFunction is this a function
+ * @param hasCallSiteType does this access have a callsite type
*/
- public BaseNode(final Source source, final long token, final int finish, final Node base) {
- super(source, token, finish);
- this.base = base;
- setStart(base.getStart());
+ public BaseNode(final Source source, final long token, final int finish, final Node base, final boolean isFunction, final boolean hasCallSiteType) {
+ super(source, token, base.getStart(), finish);
+ this.base = base;
+ this.isFunction = isFunction;
+ this.hasCallSiteType = hasCallSiteType;
}
/**
- * Copy constructor
- *
- * @param baseNode the base node
- * @param cs a copy state
+ * Copy constructor for immutable nodes
+ * @param baseNode node to inherit from
+ * @param base base
+ * @param isFunction is this a function
+ * @param hasCallSiteType does this access have a callsite type
*/
- protected BaseNode(final BaseNode baseNode, final CopyState cs) {
+ protected BaseNode(final BaseNode baseNode, final Node base, final boolean isFunction, final boolean hasCallSiteType) {
super(baseNode);
- this.base = cs.existingOrCopy(baseNode.getBase());
- setStart(base.getStart());
- }
-
- @Override
- public boolean equals(final Object other) {
- if (!super.equals(other)) {
- return false;
- }
- final BaseNode baseNode = (BaseNode)other;
- return base.equals(baseNode.getBase());
- }
-
- @Override
- public int hashCode() {
- return base.hashCode();
+ this.base = base;
+ this.isFunction = isFunction;
+ this.hasCallSiteType = hasCallSiteType;
}
/**
@@ -88,25 +86,37 @@ public abstract class BaseNode extends Node implements FunctionCall {
return base;
}
- /**
- * Reset the base node for this access
- * @param base new base node
- */
- public void setBase(final Node base) {
- this.base = base;
- }
-
@Override
public boolean isFunction() {
- return function;
+ return isFunction;
}
/**
* Mark this node as being the callee operand of a {@link CallNode}.
* @return a base node identical to this one in all aspects except with its function flag set.
*/
- public BaseNode setIsFunction() {
- function = true;
- return this;
+ public abstract BaseNode setIsFunction();
+
+ @Override
+ public boolean canHaveCallSiteType() {
+ return true; //carried by the symbol and always the same nodetype==symboltype
+ }
+
+ /**
+ * Does the access have a call site type override?
+ * @return true if overridden
+ */
+ protected boolean hasCallSiteType() {
+ return hasCallSiteType;
+ }
+
+ /**
+ * Debug type change
+ * @param type new type
+ */
+ protected final void logTypeChange(final Type type) {
+ if (DEBUG_FIELDS && !Type.areEquivalent(getSymbol().getSymbolType(), type)) {
+ ObjectClassGenerator.LOG.info(getClass().getName(), " ", this, " => ", type, " instead of ", getType());
+ }
}
}
diff --git a/src/jdk/nashorn/internal/ir/BinaryNode.java b/src/jdk/nashorn/internal/ir/BinaryNode.java
index 42c7a6cc..28df8edf 100644
--- a/src/jdk/nashorn/internal/ir/BinaryNode.java
+++ b/src/jdk/nashorn/internal/ir/BinaryNode.java
@@ -26,6 +26,7 @@
package jdk.nashorn.internal.ir;
import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.TokenType;
import jdk.nashorn.internal.runtime.Source;
@@ -33,9 +34,12 @@ import jdk.nashorn.internal.runtime.Source;
/**
* BinaryNode nodes represent two operand operations.
*/
-public class BinaryNode extends UnaryNode {
+@Immutable
+public final class BinaryNode extends Node implements Assignment<Node> {
/** Left hand side argument. */
- private Node lhs;
+ private final Node lhs;
+
+ private final Node rhs;
/**
* Constructor
@@ -46,28 +50,15 @@ public class BinaryNode extends UnaryNode {
* @param rhs right hand side
*/
public BinaryNode(final Source source, final long token, final Node lhs, final Node rhs) {
- super(source, token, rhs);
-
- start = lhs.getStart();
- finish = rhs.getFinish();
-
- this.lhs = lhs;
- }
-
- /**
- * Copy constructor
- *
- * @param binaryNode the binary node
- * @param cs copy state
- */
- protected BinaryNode(final BinaryNode binaryNode, final CopyState cs) {
- super(binaryNode, cs);
- lhs = cs.existingOrCopy(binaryNode.lhs);
+ super(source, token, lhs.getStart(), rhs.getFinish());
+ this.lhs = lhs;
+ this.rhs = rhs;
}
- @Override
- protected Node copy(final CopyState cs) {
- return new BinaryNode(this, cs);
+ private BinaryNode(final BinaryNode binaryNode, final Node lhs, final Node rhs) {
+ super(binaryNode);
+ this.lhs = lhs;
+ this.rhs = rhs;
}
/**
@@ -149,28 +140,14 @@ public class BinaryNode extends UnaryNode {
return rhs();
}
- @Override
- public boolean equals(final Object other) {
- if (!super.equals(other)) {
- return false;
- }
- return lhs.equals(((BinaryNode)other).lhs());
- }
-
- @Override
- public int hashCode() {
- return super.hashCode() ^ lhs().hashCode();
- }
-
/**
* Assist in IR navigation.
* @param visitor IR navigating visitor.
*/
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterBinaryNode(this) != null) {
- // TODO: good cause for a separate visitMembers: we could delegate to UnaryNode.visitMembers
- return visitor.leaveBinaryNode((BinaryNode)setLHS(lhs.accept(visitor)).setRHS(rhs().accept(visitor)));
+ if (visitor.enterBinaryNode(this)) {
+ return visitor.leaveBinaryNode(setLHS(lhs.accept(visitor)).setRHS(rhs.accept(visitor)));
}
return this;
@@ -231,14 +208,35 @@ public class BinaryNode extends UnaryNode {
}
/**
+ * Get the right hand side expression for this node
+ * @return the left hand side expression
+ */
+ public Node rhs() {
+ return rhs;
+ }
+
+ /**
* Set the left hand side expression for this node
* @param lhs new left hand side expression
* @return a node equivalent to this one except for the requested change.
*/
public BinaryNode setLHS(final Node lhs) {
- if(this.lhs == lhs) return this;
- final BinaryNode n = (BinaryNode)clone();
- n.lhs = lhs;
- return n;
+ if (this.lhs == lhs) {
+ return this;
+ }
+ return new BinaryNode(this, lhs, rhs);
}
+
+ /**
+ * Set the right hand side expression for this node
+ * @param rhs new left hand side expression
+ * @return a node equivalent to this one except for the requested change.
+ */
+ public BinaryNode setRHS(final Node rhs) {
+ if (this.rhs == rhs) {
+ return this;
+ }
+ return new BinaryNode(this, lhs, rhs);
+ }
+
}
diff --git a/src/jdk/nashorn/internal/ir/Block.java b/src/jdk/nashorn/internal/ir/Block.java
index 6f138f85..48b9ed69 100644
--- a/src/jdk/nashorn/internal/ir/Block.java
+++ b/src/jdk/nashorn/internal/ir/Block.java
@@ -27,14 +27,15 @@ package jdk.nashorn.internal.ir;
import java.io.PrintWriter;
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.List;
-import java.util.ListIterator;
-import jdk.nashorn.internal.codegen.Frame;
+import java.util.Map;
import jdk.nashorn.internal.codegen.Label;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
@@ -42,97 +43,79 @@ import jdk.nashorn.internal.runtime.Source;
* IR representation for a list of statements and functions. All provides the
* basis for script body.
*/
-public class Block extends Node {
+@Immutable
+public class Block extends BreakableNode implements Flags<Block> {
/** List of statements */
- protected List<Node> statements;
+ protected final List<Node> statements;
- /** Symbol table. */
- protected final HashMap<String, Symbol> symbols;
-
- /** Variable frame. */
- protected Frame frame;
+ /** Symbol table - keys must be returned in the order they were put in. */
+ protected final Map<String, Symbol> symbols;
/** Entry label. */
protected final Label entryLabel;
- /** Break label. */
- protected final Label breakLabel;
-
/** Does the block/function need a new scope? */
- protected boolean needsScope;
+ protected final int flags;
+
+ /** Flag indicating that this block needs scope */
+ public static final int NEEDS_SCOPE = 1 << 0;
/**
- * Constructor
- *
- * @param source source code
- * @param token token
- * @param finish finish
+ * Flag indicating whether this block needs
+ * self symbol assignment at the start. This is used only for
+ * blocks that are the bodies of function nodes who refer to themselves
+ * by name. It causes codegen to insert a var [fn_name] = __callee__
+ * at the start of the body
*/
- public Block(final Source source, final long token, final int finish) {
- super(source, token, finish);
+ public static final int NEEDS_SELF_SYMBOL = 1 << 1;
- this.statements = new ArrayList<>();
- this.symbols = new HashMap<>();
- this.entryLabel = new Label("block_entry");
- this.breakLabel = new Label("block_break");
- }
+ /**
+ * Is this block tagged as terminal based on its contents
+ * (usually the last statement)
+ */
+ public static final int IS_TERMINAL = 1 << 2;
/**
- * Internal copy constructor
+ * Constructor
*
- * @param block the source block
- * @param cs the copy state
+ * @param source source code
+ * @param token token
+ * @param finish finish
+ * @param statements statements
*/
- protected Block(final Block block, final CopyState cs) {
- super(block);
+ public Block(final Source source, final long token, final int finish, final Node... statements) {
+ super(source, token, finish, new Label("block_break"));
- this.statements = new ArrayList<>();
- for (final Node statement : block.getStatements()) {
- statements.add(cs.existingOrCopy(statement));
- }
- this.symbols = new HashMap<>();
- this.frame = block.frame == null ? null : block.frame.copy();
- this.entryLabel = new Label(block.entryLabel);
- this.breakLabel = new Label(block.breakLabel);
-
- assert block.symbols.isEmpty() : "must not clone with symbols";
- }
-
- @Override
- protected Node copy(final CopyState cs) {
- return new Block(this, cs);
+ this.statements = Arrays.asList(statements);
+ this.symbols = new LinkedHashMap<>();
+ this.entryLabel = new Label("block_entry");
+ this.flags = 0;
}
/**
- * Add a new statement to the statement list.
+ * Constructor
*
- * @param statement Statement node to add.
+ * @param source source code
+ * @param token token
+ * @param finish finish
+ * @param statements statements
*/
- public void addStatement(final Node statement) {
- if (statement != null) {
- statements.add(statement);
- if (getFinish() < statement.getFinish()) {
- setFinish(statement.getFinish());
- }
- }
+ public Block(final Source source, final long token, final int finish, final List<Node> statements) {
+ this(source, token, finish, statements.toArray(new Node[statements.size()]));
}
- /**
- * Prepend statements to the statement list
- *
- * @param prepended statement to add
- */
- public void prependStatements(final List<Node> prepended) {
- statements.addAll(0, prepended);
+ private Block(final Block block, final int finish, final List<Node> statements, final int flags) {
+ super(block);
+ this.statements = statements;
+ this.flags = flags;
+ this.symbols = block.symbols; //todo - symbols have no dependencies on any IR node and can as far as we understand it be shallow copied now
+ this.entryLabel = new Label(block.entryLabel);
+ this.finish = finish;
}
- /**
- * Add a list of statements to the statement list.
- *
- * @param statementList Statement nodes to add.
- */
- public void addStatements(final List<Node> statementList) {
- statements.addAll(statementList);
+ @Override
+ public Node ensureUniqueLabels(final LexicalContext lc) {
+ return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags));
}
/**
@@ -142,19 +125,9 @@ public class Block extends Node {
* @return new or same node
*/
@Override
- public Node accept(final NodeVisitor visitor) {
- final Block saveBlock = visitor.getCurrentBlock();
- visitor.setCurrentBlock(this);
-
- try {
- // Ignore parent to avoid recursion.
-
- if (visitor.enterBlock(this) != null) {
- visitStatements(visitor);
- return visitor.leaveBlock(this);
- }
- } finally {
- visitor.setCurrentBlock(saveBlock);
+ public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
+ if (visitor.enterBlock(this)) {
+ return visitor.leaveBlock(setStatements(lc, Node.accept(visitor, Node.class, statements)));
}
return this;
@@ -222,11 +195,18 @@ public class Block extends Node {
}
/**
- * Get the break label for this block
- * @return the break label
+ * Tag block as terminal or non terminal
+ * @param lc lexical context
+ * @param isTerminal is block terminal
+ * @return same block, or new if flag changed
*/
- public Label getBreakLabel() {
- return breakLabel;
+ public Block setIsTerminal(final LexicalContext lc, final boolean isTerminal) {
+ return isTerminal ? setFlag(lc, IS_TERMINAL) : clearFlag(lc, IS_TERMINAL);
+ }
+
+ @Override
+ public boolean isTerminal() {
+ return getFlag(IS_TERMINAL);
}
/**
@@ -238,23 +218,6 @@ public class Block extends Node {
}
/**
- * Get the frame for this block
- * @return the frame
- */
- public Frame getFrame() {
- return frame;
- }
-
- /**
- * Reset the frame for this block
- *
- * @param frame the new frame
- */
- public void setFrame(final Frame frame) {
- this.frame = frame;
- }
-
- /**
* Get the list of statements in this block
*
* @return a list of statements
@@ -264,21 +227,21 @@ public class Block extends Node {
}
/**
- * Applies the specified visitor to all statements in the block.
- * @param visitor the visitor.
- */
- public void visitStatements(NodeVisitor visitor) {
- for (ListIterator<Node> stmts = statements.listIterator(); stmts.hasNext();) {
- stmts.set(stmts.next().accept(visitor));
- }
- }
- /**
* Reset the statement list for this block
*
- * @param statements new statement list
+ * @param lc lexical context
+ * @param statements new statement list
+ * @return new block if statements changed, identity of statements == block.statements
*/
- public void setStatements(final List<Node> statements) {
- this.statements = statements;
+ public Block setStatements(final LexicalContext lc, final List<Node> statements) {
+ if (this.statements == statements) {
+ return this;
+ }
+ int lastFinish = 0;
+ if (!statements.isEmpty()) {
+ lastFinish = statements.get(statements.size() - 1).getFinish();
+ }
+ return Node.replaceInLexicalContext(lc, this, new Block(this, Math.max(finish, lastFinish), statements, flags));
}
/**
@@ -297,39 +260,65 @@ public class Block extends Node {
* @return true if this function needs a scope
*/
public boolean needsScope() {
- return needsScope;
+ return (flags & NEEDS_SCOPE) == NEEDS_SCOPE;
}
- /**
- * Set the needs scope flag.
- */
- public void setNeedsScope() {
- needsScope = true;
+ @Override
+ public Block setFlags(final LexicalContext lc, int flags) {
+ if (this.flags == flags) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags));
+ }
+
+ @Override
+ public Block clearFlag(final LexicalContext lc, int flag) {
+ return setFlags(lc, flags & ~flag);
+ }
+
+ @Override
+ public Block setFlag(final LexicalContext lc, int flag) {
+ return setFlags(lc, flags | flag);
+ }
+
+ @Override
+ public boolean getFlag(final int flag) {
+ return (flags & flag) == flag;
}
/**
- * Marks this block as using a specified scoped symbol. The block and its parent blocks up to but not
- * including the block defining the symbol will be marked as needing parent scope. The block defining the symbol
- * will be marked as one that needs to have its own scope.
- * @param symbol the symbol being used.
- * @param ancestors the iterator over block's containing lexical context
+ * Set the needs scope flag.
+ * @param lc lexicalContext
+ * @return new block if state changed, otherwise this
*/
- public void setUsesScopeSymbol(final Symbol symbol, Iterator<Block> ancestors) {
- if(symbol.getBlock() == this) {
- setNeedsScope();
- } else {
- setUsesParentScopeSymbol(symbol, ancestors);
+ public Block setNeedsScope(final LexicalContext lc) {
+ if (needsScope()) {
+ return this;
}
+
+ return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags | NEEDS_SCOPE));
}
/**
- * Invoked when this block uses a scope symbol defined in one of its ancestors.
- * @param symbol the scope symbol being used
- * @param ancestors iterator over ancestor blocks
+ * Computationally determine the next slot for this block,
+ * indexed from 0. Use this as a relative base when computing
+ * frames
+ * @return next slot
*/
- void setUsesParentScopeSymbol(final Symbol symbol, Iterator<Block> ancestors) {
- if(ancestors.hasNext()) {
- ancestors.next().setUsesScopeSymbol(symbol, ancestors);
+ public int nextSlot() {
+ final Iterator<Symbol> iter = symbolIterator();
+ int next = 0;
+ while (iter.hasNext()) {
+ final Symbol symbol = iter.next();
+ if (symbol.hasSlot()) {
+ next += symbol.slotCount();
}
+ }
+ return next;
+ }
+
+ @Override
+ protected boolean isBreakableWithoutLabel() {
+ return false;
}
}
diff --git a/src/jdk/nashorn/internal/ir/BlockLexicalContext.java b/src/jdk/nashorn/internal/ir/BlockLexicalContext.java
new file mode 100644
index 00000000..38186f56
--- /dev/null
+++ b/src/jdk/nashorn/internal/ir/BlockLexicalContext.java
@@ -0,0 +1,120 @@
+/*
+ * 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.ir;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.List;
+import java.util.ListIterator;
+
+/**
+ * This is a subclass of lexical context used for filling
+ * blocks (and function nodes) with statements. When popping
+ * a block from the lexical context, any statements that have
+ * been generated in it are commited to the block. This saves
+ * unnecessary object mutations and lexical context replacement
+ */
+public class BlockLexicalContext extends LexicalContext {
+ /** statement stack, each block on the lexical context maintains one of these, which is
+ * committed to the block on pop */
+ private Deque<List<Node>> sstack = new ArrayDeque<>();
+
+ /** Last non debug statement emitted in this context */
+ protected Node lastStatement;
+
+ @Override
+ public <T extends LexicalContextNode> T push(final T node) {
+ T pushed = super.push(node);
+ if (node instanceof Block) {
+ sstack.push(new ArrayList<Node>());
+ }
+ return pushed;
+ }
+
+ /**
+ * Get the statement list from the stack, possibly filtered
+ * @return statement list
+ */
+ protected List<Node> popStatements() {
+ return sstack.pop();
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T extends LexicalContextNode> T pop(final T node) {
+ T expected = node;
+ if (node instanceof Block) {
+ final List<Node> newStatements = popStatements();
+ expected = (T)((Block)node).setStatements(this, newStatements);
+ if (!sstack.isEmpty()) {
+ lastStatement = lastStatement(sstack.peek());
+ }
+ }
+ return super.pop(expected);
+ }
+
+ /**
+ * Append a statement to the block being generated
+ * @param statement statement to add
+ */
+ public void appendStatement(final Node statement) {
+ assert statement != null;
+ sstack.peek().add(statement);
+ if (!statement.isDebug()) {
+ lastStatement = statement;
+ }
+ }
+
+ /**
+ * Prepend a statement to the block being generated
+ * @param statement statement to prepend
+ * @return the prepended statement
+ */
+ public Node prependStatement(final Node statement) {
+ assert statement != null;
+ sstack.peek().add(0, statement);
+ return statement;
+ }
+
+ /**
+ * Get the last (non debug) statement that was emitted into a block
+ * @return the last statement emitted
+ */
+ public Node getLastStatement() {
+ return lastStatement;
+ }
+
+ private static Node lastStatement(final List<Node> statements) {
+ for (final ListIterator<Node> iter = statements.listIterator(statements.size()); iter.hasPrevious(); ) {
+ final Node node = iter.previous();
+ if (!node.isDebug()) {
+ return node;
+ }
+ }
+ return null;
+ }
+}
diff --git a/src/jdk/nashorn/internal/ir/BreakNode.java b/src/jdk/nashorn/internal/ir/BreakNode.java
index 7ad0dc6d..f0966b46 100644
--- a/src/jdk/nashorn/internal/ir/BreakNode.java
+++ b/src/jdk/nashorn/internal/ir/BreakNode.java
@@ -25,37 +25,34 @@
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;
import jdk.nashorn.internal.runtime.Source;
/**
* IR representation for {@code break} statements.
*/
-public class BreakNode extends LabeledNode {
+@Immutable
+public final class BreakNode extends Node {
- /**
+ private final IdentNode label;
+
+ /**
* Constructor
*
- * @param source source code
- * @param token token
- * @param finish finish
- * @param labelNode break label
- * @param targetNode node to break to
- * @param tryChain surrounding try chain
+ * @param source source code
+ * @param token token
+ * @param finish finish
+ * @param label label for break or null if none
*/
- public BreakNode(final Source source, final long token, final int finish, final LabelNode labelNode, final Node targetNode, final TryNode tryChain) {
- super(source, token, finish, labelNode, targetNode, tryChain);
- setHasGoto();
- }
-
- private BreakNode(final BreakNode breakNode, final CopyState cs) {
- super(breakNode, cs);
+ public BreakNode(final Source source, final long token, final int finish, final IdentNode label) {
+ super(source, token, finish);
+ this.label = label;
}
@Override
- protected Node copy(final CopyState cs) {
- return new BreakNode(this, cs);
+ public boolean hasGoto() {
+ return true;
}
/**
@@ -64,7 +61,7 @@ public class BreakNode extends LabeledNode {
*/
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterBreakNode(this) != null) {
+ if (visitor.enterBreakNode(this)) {
return visitor.leaveBreakNode(this);
}
@@ -72,26 +69,20 @@ public class BreakNode extends LabeledNode {
}
/**
- * Return the target label of this break node.
- * @return the target label.
+ * Get the label for this break node
+ * @return label, or null if none
*/
- public Label getTargetLabel() {
- if (targetNode instanceof BreakableNode) {
- return ((BreakableNode)targetNode).getBreakLabel();
- } else if (targetNode instanceof Block) {
- return ((Block)targetNode).getBreakLabel();
- }
-
- throw new AssertionError("Invalid break target " + targetNode.getClass());
+ public IdentNode getLabel() {
+ return label;
}
@Override
public void toString(final StringBuilder sb) {
sb.append("break");
- if (labelNode != null) {
+ if (label != null) {
sb.append(' ');
- labelNode.getLabel().toString(sb);
+ label.toString(sb);
}
}
}
diff --git a/src/jdk/nashorn/internal/ir/BreakableNode.java b/src/jdk/nashorn/internal/ir/BreakableNode.java
index d15b7564..3662bfa7 100644
--- a/src/jdk/nashorn/internal/ir/BreakableNode.java
+++ b/src/jdk/nashorn/internal/ir/BreakableNode.java
@@ -25,27 +25,34 @@
package jdk.nashorn.internal.ir;
+import java.util.Arrays;
+import java.util.List;
+
import jdk.nashorn.internal.codegen.Label;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.runtime.Source;
/**
* This class represents a node from which control flow can execute
* a {@code break} statement
*/
-public abstract class BreakableNode extends Node {
+@Immutable
+public abstract class BreakableNode extends LexicalContextNode {
/** break label. */
- protected Label breakLabel;
+ protected final Label breakLabel;
/**
* Constructor
*
- * @param source source code
- * @param token token
- * @param finish finish
+ * @param source source code
+ * @param token token
+ * @param finish finish
+ * @param breakLabel break label
*/
- public BreakableNode(final Source source, final long token, final int finish) {
+ protected BreakableNode(final Source source, final long token, final int finish, final Label breakLabel) {
super(source, token, finish);
+ this.breakLabel = breakLabel;
}
/**
@@ -55,6 +62,19 @@ public abstract class BreakableNode extends Node {
*/
protected BreakableNode(final BreakableNode breakableNode) {
super(breakableNode);
+ this.breakLabel = new Label(breakableNode.getBreakLabel());
+ }
+
+ @Override
+ public abstract Node ensureUniqueLabels(final LexicalContext lc);
+
+ /**
+ * Check whether this can be broken out from without using a label,
+ * e.g. everything but Blocks, basically
+ * @return true if breakable without label
+ */
+ protected boolean isBreakableWithoutLabel() {
+ return true;
}
/**
@@ -65,4 +85,14 @@ public abstract class BreakableNode extends Node {
return breakLabel;
}
+ /**
+ * Return the labels associated with this node. Breakable nodes that
+ * aren't LoopNodes only have a break label -> the location immediately
+ * afterwards the node in code
+ * @return list of labels representing locations around this node
+ */
+ public List<Label> getLabels() {
+ return Arrays.asList(breakLabel);
+ }
+
}
diff --git a/src/jdk/nashorn/internal/ir/CallNode.java b/src/jdk/nashorn/internal/ir/CallNode.java
index 3410709c..9a730aa8 100644
--- a/src/jdk/nashorn/internal/ir/CallNode.java
+++ b/src/jdk/nashorn/internal/ir/CallNode.java
@@ -25,49 +25,48 @@
package jdk.nashorn.internal.ir;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.annotations.Ignore;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
/**
* IR representation for a function call.
- *
*/
-public class CallNode extends Node implements TypeOverride<CallNode> {
+@Immutable
+public final class CallNode extends LexicalContextNode implements TypeOverride<CallNode> {
- private Type type;
+ private final Type type;
/** Function identifier or function body. */
- private Node function;
+ private final Node function;
/** Call arguments. */
- private List<Node> args;
+ private final List<Node> args;
- /** flag - is new expression */
- private boolean isNew;
+ /** Is this a "new" operation */
+ public static final int IS_NEW = 0x1;
- /** flag - is in with block */
- private boolean inWithBlock;
+ private final int flags;
/**
* Arguments to be passed to builtin {@code eval} function
*/
public static class EvalArgs {
/** evaluated code */
- private Node code;
+ private final Node code;
/** 'this' passed to evaluated code */
- private IdentNode evalThis;
+ private final IdentNode evalThis;
/** location string for the eval call */
- final private String location;
+ private final String location;
/** is this call from a strict context? */
- final private boolean strictMode;
+ private final boolean strictMode;
/**
* Constructor
@@ -92,12 +91,11 @@ public class CallNode extends Node implements TypeOverride<CallNode> {
return code;
}
- /**
- * Set the code that is to be eval.ed by this eval function
- * @param code the code as an AST node
- */
- public void setCode(final Node code) {
- this.code = code;
+ private EvalArgs setCode(final Node code) {
+ if (this.code == code) {
+ return this;
+ }
+ return new EvalArgs(code, evalThis, location, strictMode);
}
/**
@@ -108,12 +106,11 @@ public class CallNode extends Node implements TypeOverride<CallNode> {
return this.evalThis;
}
- /**
- * Set the {@code this} symbol used to invoke this eval call
- * @param evalThis the {@code this} symbol
- */
- public void setThis(final IdentNode evalThis) {
- this.evalThis = evalThis;
+ private EvalArgs setThis(final IdentNode evalThis) {
+ if (this.evalThis == evalThis) {
+ return this;
+ }
+ return new EvalArgs(code, evalThis, location, strictMode);
}
/**
@@ -135,7 +132,7 @@ public class CallNode extends Node implements TypeOverride<CallNode> {
/** arguments for 'eval' call. Non-null only if this call node is 'eval' */
@Ignore
- private EvalArgs evalArgs;
+ private final EvalArgs evalArgs;
/**
* Constructors
@@ -149,28 +146,22 @@ public class CallNode extends Node implements TypeOverride<CallNode> {
public CallNode(final Source source, final long token, final int finish, final Node function, final List<Node> args) {
super(source, token, finish);
- setStart(function.getStart());
-
- this.function = function;
- this.args = args;
+ this.function = function;
+ this.args = args;
+ this.flags = 0;
+ this.type = null;
+ this.evalArgs = null;
}
- private CallNode(final CallNode callNode, final CopyState cs) {
+ private CallNode(final CallNode callNode, final Node function, final List<Node> args, final int flags, final Type type, final EvalArgs evalArgs) {
super(callNode);
-
- final List<Node> newArgs = new ArrayList<>();
-
- for (final Node arg : callNode.args) {
- newArgs.add(cs.existingOrCopy(arg));
- }
-
- this.function = cs.existingOrCopy(callNode.function); //TODO existing or same?
- this.args = newArgs;
- this.isNew = callNode.isNew;
- this.inWithBlock = callNode.inWithBlock;
+ this.function = function;
+ this.args = args;
+ this.flags = flags;
+ this.type = type;
+ this.evalArgs = evalArgs;
}
-
@Override
public Type getType() {
if (hasCallSiteType()) {
@@ -181,8 +172,10 @@ public class CallNode extends Node implements TypeOverride<CallNode> {
@Override
public CallNode setType(final Type type) {
- this.type = type;
- return this;
+ if (this.type == type) {
+ return this;
+ }
+ return new CallNode(this, function, args, flags, type, evalArgs);
}
private boolean hasCallSiteType() {
@@ -194,11 +187,6 @@ public class CallNode extends Node implements TypeOverride<CallNode> {
return true;
}
- @Override
- protected Node copy(final CopyState cs) {
- return new CallNode(this, cs);
- }
-
/**
* Assist in IR navigation.
*
@@ -207,15 +195,22 @@ public class CallNode extends Node implements TypeOverride<CallNode> {
* @return node or replacement
*/
@Override
- public Node accept(final NodeVisitor visitor) {
- if (visitor.enterCallNode(this) != null) {
- function = function.accept(visitor);
-
- for (int i = 0, count = args.size(); i < count; i++) {
- args.set(i, args.get(i).accept(visitor));
+ public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
+ if (visitor.enterCallNode(this)) {
+ final CallNode newCallNode = (CallNode)visitor.leaveCallNode(
+ setFunction(function.accept(visitor)).
+ setArgs(Node.accept(visitor, Node.class, args)).
+ setFlags(flags).
+ setType(type).
+ setEvalArgs(evalArgs == null ?
+ null :
+ evalArgs.setCode(evalArgs.getCode().accept(visitor)).
+ setThis((IdentNode)evalArgs.getThis().accept(visitor))));
+ // Theoretically, we'd need to instead pass lc to every setter and do a replacement on each. In practice,
+ // setType from TypeOverride can't accept a lc, and we don't necessarily want to go there now.
+ if(this != newCallNode) {
+ return Node.replaceInLexicalContext(lc, this, newCallNode);
}
-
- return visitor.leaveCallNode(this);
}
return this;
@@ -226,7 +221,7 @@ public class CallNode extends Node implements TypeOverride<CallNode> {
if (hasCallSiteType()) {
sb.append('{');
final String desc = getType().getDescriptor();
- sb.append(desc.charAt(desc.length() - 1) == ';' ? "O" : getType().getDescriptor());
+ sb.append(desc.charAt(desc.length() - 1) == ';' ? 'O' : getType().getDescriptor());
sb.append('}');
}
@@ -261,8 +256,11 @@ public class CallNode extends Node implements TypeOverride<CallNode> {
* Reset the arguments for the call
* @param args new arguments list
*/
- public void setArgs(final List<Node> args) {
- this.args = args;
+ private CallNode setArgs(final List<Node> args) {
+ if (this.args == args) {
+ return this;
+ }
+ return new CallNode(this, function, args, flags, type, evalArgs);
}
/**
@@ -278,9 +276,13 @@ public class CallNode extends Node implements TypeOverride<CallNode> {
* {@code eval}
*
* @param evalArgs eval args
+ * @return same node or new one on state change
*/
- public void setEvalArgs(final EvalArgs evalArgs) {
- this.evalArgs = evalArgs;
+ public CallNode setEvalArgs(final EvalArgs evalArgs) {
+ if (this.evalArgs == evalArgs) {
+ return this;
+ }
+ return new CallNode(this, function, args, flags, type, evalArgs);
}
/**
@@ -301,10 +303,14 @@ public class CallNode extends Node implements TypeOverride<CallNode> {
/**
* Reset the function expression that this call invokes
- * @param node the function
+ * @param function the function
+ * @return same node or new one on state change
*/
- public void setFunction(final Node node) {
- function = node;
+ public CallNode setFunction(final Node function) {
+ if (this.function == function) {
+ return this;
+ }
+ return new CallNode(this, function, args, flags, type, evalArgs);
}
/**
@@ -312,28 +318,21 @@ public class CallNode extends Node implements TypeOverride<CallNode> {
* @return true if this a new operation
*/
public boolean isNew() {
- return isNew;
+ return (flags & IS_NEW) == IS_NEW;
}
/**
* Flag this call as a new operation
+ * @return same node or new one on state change
*/
- public void setIsNew() {
- this.isNew = true;
+ public CallNode setIsNew() {
+ return setFlags(IS_NEW);
}
- /**
- * Check if this call is inside a {@code with} block
- * @return true if the call is inside a {@code with} block
- */
- public boolean inWithBlock() {
- return inWithBlock;
- }
-
- /**
- * Flag this call to be inside a {@code with} block
- */
- public void setInWithBlock() {
- this.inWithBlock = true;
+ private CallNode setFlags(final int flags) {
+ if (this.flags == flags) {
+ return this;
+ }
+ return new CallNode(this, function, args, flags, type, evalArgs);
}
}
diff --git a/src/jdk/nashorn/internal/ir/CaseNode.java b/src/jdk/nashorn/internal/ir/CaseNode.java
index 61b89217..237536cc 100644
--- a/src/jdk/nashorn/internal/ir/CaseNode.java
+++ b/src/jdk/nashorn/internal/ir/CaseNode.java
@@ -26,19 +26,21 @@
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;
import jdk.nashorn.internal.runtime.Source;
/**
* IR representation of CASE clause.
- *
+ * Case nodes are not BreakableNodes, but the SwitchNode is
*/
-public class CaseNode extends BreakableNode {
+@Immutable
+public final class CaseNode extends Node {
/** Test expression. */
- private Node test;
+ private final Node test;
/** Statements. */
- private Block body;
+ private final Block body;
/** Case entry label. */
private final Label entry;
@@ -60,17 +62,17 @@ public class CaseNode extends BreakableNode {
this.entry = new Label("entry");
}
- private CaseNode(final CaseNode caseNode, final CopyState cs) {
+ CaseNode(final CaseNode caseNode, final Node test, final Block body) {
super(caseNode);
- this.test = cs.existingOrCopy(caseNode.test);
- this.body = (Block)cs.existingOrCopy(caseNode.body);
+ this.test = test;
+ this.body = body;
this.entry = new Label(caseNode.entry);
}
@Override
- protected Node copy(final CopyState cs) {
- return new CaseNode(this, cs);
+ public boolean isTerminal() {
+ return body.isTerminal();
}
/**
@@ -79,15 +81,11 @@ public class CaseNode extends BreakableNode {
*/
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterCaseNode(this) != null) {
- if (test != null) {
- test = test.accept(visitor);
- }
- if (body != null) {
- body = (Block)body.accept(visitor);
- }
-
- return visitor.leaveCaseNode(this);
+ if (visitor.enterCaseNode(this)) {
+ final Node newTest = test == null ? null : test.accept(visitor);
+ final Block newBody = body == null ? null : (Block)body.accept(visitor);
+
+ return visitor.leaveCaseNode(setTest(newTest).setBody(newBody));
}
return this;
@@ -131,8 +129,19 @@ public class CaseNode extends BreakableNode {
/**
* Reset the test expression for this case node
* @param test new test expression
+ * @return new or same CaseNode
*/
- public void setTest(final Node test) {
- this.test = test;
+ public CaseNode setTest(final Node test) {
+ if (this.test == test) {
+ return this;
+ }
+ return new CaseNode(this, test, body);
+ }
+
+ private CaseNode setBody(final Block body) {
+ if (this.body == body) {
+ return this;
+ }
+ return new CaseNode(this, test, body);
}
}
diff --git a/src/jdk/nashorn/internal/ir/CatchNode.java b/src/jdk/nashorn/internal/ir/CatchNode.java
index 005ffa8e..5e1b4111 100644
--- a/src/jdk/nashorn/internal/ir/CatchNode.java
+++ b/src/jdk/nashorn/internal/ir/CatchNode.java
@@ -25,26 +25,23 @@
package jdk.nashorn.internal.ir;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
/**
* IR representation of a catch clause.
- *
*/
-public class CatchNode extends Node {
+@Immutable
+public final class CatchNode extends Node {
/** Exception identifier. */
- private IdentNode exception;
+ private final IdentNode exception;
/** Exception condition. */
- private Node exceptionCondition;
+ private final Node exceptionCondition;
/** Catch body. */
- private Block body;
-
- /** Is rethrow - e.g. synthetic catch block for e.g. finallies, the parser case where
- * there has to be at least on catch for syntactic validity */
- private boolean isSyntheticRethrow;
+ private final Block body;
/**
* Constructors
@@ -64,18 +61,12 @@ public class CatchNode extends Node {
this.body = body;
}
- private CatchNode(final CatchNode catchNode, final CopyState cs) {
+ private CatchNode(final CatchNode catchNode, final IdentNode exception, final Node exceptionCondition, final Block body) {
super(catchNode);
- this.exception = (IdentNode)cs.existingOrCopy(catchNode.exception);
- this.exceptionCondition = cs.existingOrCopy(catchNode.exceptionCondition);
- this.body = (Block)cs.existingOrCopy(catchNode.body);
- this.isSyntheticRethrow = catchNode.isSyntheticRethrow;
- }
-
- @Override
- protected Node copy(final CopyState cs) {
- return new CatchNode(this, cs);
+ this.exception = exception;
+ this.exceptionCondition = exceptionCondition;
+ this.body = body;
}
/**
@@ -84,21 +75,22 @@ public class CatchNode extends Node {
*/
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterCatchNode(this) != null) {
- exception = (IdentNode)exception.accept(visitor);
-
- if (exceptionCondition != null) {
- exceptionCondition = exceptionCondition.accept(visitor);
- }
-
- body = (Block)body.accept(visitor);
- return visitor.leaveCatchNode(this);
+ if (visitor.enterCatchNode(this)) {
+ return visitor.leaveCatchNode(
+ setException((IdentNode)exception.accept(visitor)).
+ setExceptionCondition(exceptionCondition == null ? null : exceptionCondition.accept(visitor)).
+ setBody((Block)body.accept(visitor)));
}
return this;
}
@Override
+ public boolean isTerminal() {
+ return body.isTerminal();
+ }
+
+ @Override
public void toString(final StringBuilder sb) {
sb.append(" catch (");
exception.toString(sb);
@@ -111,23 +103,6 @@ public class CatchNode extends Node {
}
/**
- * Check if this catch is a synthetic rethrow
- * @return true if this is a synthetic rethrow
- */
- public boolean isSyntheticRethrow() {
- return isSyntheticRethrow;
- }
-
- /**
- * Flag this as deliberatly generated catch all that rethrows the
- * caught exception. This is used for example for generating finally
- * expressions
- */
- public void setIsSyntheticRethrow() {
- this.isSyntheticRethrow = true;
- }
-
- /**
* Get the identifier representing the exception thrown
* @return the exception identifier
*/
@@ -146,9 +121,13 @@ public class CatchNode extends Node {
/**
* Reset the exception condition for this catch block
* @param exceptionCondition the new exception condition
+ * @return new or same CatchNode
*/
- public void setExceptionCondition(final Node exceptionCondition) {
- this.exceptionCondition = exceptionCondition;
+ public CatchNode setExceptionCondition(final Node exceptionCondition) {
+ if (this.exceptionCondition == exceptionCondition) {
+ return this;
+ }
+ return new CatchNode(this, exception, exceptionCondition, body);
}
/**
@@ -158,4 +137,18 @@ public class CatchNode extends Node {
public Block getBody() {
return body;
}
+
+ private CatchNode setException(final IdentNode exception) {
+ if (this.exception == exception) {
+ return this;
+ }
+ return new CatchNode(this, exception, exceptionCondition, body);
+ }
+
+ private CatchNode setBody(final Block body) {
+ if (this.body == body) {
+ return this;
+ }
+ return new CatchNode(this, exception, exceptionCondition, body);
+ }
}
diff --git a/src/jdk/nashorn/internal/ir/ContinueNode.java b/src/jdk/nashorn/internal/ir/ContinueNode.java
index cbc7bff2..c8cc309d 100644
--- a/src/jdk/nashorn/internal/ir/ContinueNode.java
+++ b/src/jdk/nashorn/internal/ir/ContinueNode.java
@@ -25,43 +25,39 @@
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;
import jdk.nashorn.internal.runtime.Source;
/**
* IR representation for CONTINUE statements.
- *
*/
-public class ContinueNode extends LabeledNode {
+@Immutable
+public class ContinueNode extends Node {
+
+ private IdentNode label;
/**
* Constructor
*
- * @param source the source
- * @param token token
- * @param finish finish
- * @param labelNode the continue label
- * @param targetNode node to continue to
- * @param tryChain surrounding try chain
+ * @param source source code
+ * @param token token
+ * @param finish finish
+ * @param label label for break or null if none
*/
- public ContinueNode(final Source source, final long token, final int finish, final LabelNode labelNode, final Node targetNode, final TryNode tryChain) {
- super(source, token, finish, labelNode, targetNode, tryChain);
- setHasGoto();
- }
-
- private ContinueNode(final ContinueNode continueNode, final CopyState cs) {
- super(continueNode, cs);
+ public ContinueNode(final Source source, final long token, final int finish, final IdentNode label) {
+ super(source, token, finish);
+ this.label = label;
}
@Override
- protected Node copy(final CopyState cs) {
- return new ContinueNode(this, cs);
+ public boolean hasGoto() {
+ return true;
}
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterContinueNode(this) != null) {
+ if (visitor.enterContinueNode(this)) {
return visitor.leaveContinueNode(this);
}
@@ -69,21 +65,20 @@ public class ContinueNode extends LabeledNode {
}
/**
- * Return the target label of this continue node.
- * @return the target label.
+ * Get the label for this break node
+ * @return label, or null if none
*/
- public Label getTargetLabel() {
- assert targetNode instanceof WhileNode : "continue target must be a while node";
- return ((WhileNode)targetNode).getContinueLabel();
+ public IdentNode getLabel() {
+ return label;
}
@Override
public void toString(final StringBuilder sb) {
sb.append("continue");
- if (labelNode != null) {
+ if (label != null) {
sb.append(' ');
- labelNode.getLabel().toString(sb);
+ label.toString(sb);
}
}
}
diff --git a/src/jdk/nashorn/internal/ir/EmptyNode.java b/src/jdk/nashorn/internal/ir/EmptyNode.java
index 15330a37..67516127 100644
--- a/src/jdk/nashorn/internal/ir/EmptyNode.java
+++ b/src/jdk/nashorn/internal/ir/EmptyNode.java
@@ -25,14 +25,15 @@
package jdk.nashorn.internal.ir;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
/**
* IR representation for an empty statement.
- *
*/
-public class EmptyNode extends Node {
+@Immutable
+public final class EmptyNode extends Node {
/**
* Constructor
@@ -57,7 +58,7 @@ public class EmptyNode extends Node {
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterEmptyNode(this) != null) {
+ if (visitor.enterEmptyNode(this)) {
return visitor.leaveEmptyNode(this);
}
return this;
diff --git a/src/jdk/nashorn/internal/ir/ExecuteNode.java b/src/jdk/nashorn/internal/ir/ExecuteNode.java
index 8ae7d556..f6dd7d1b 100644
--- a/src/jdk/nashorn/internal/ir/ExecuteNode.java
+++ b/src/jdk/nashorn/internal/ir/ExecuteNode.java
@@ -25,6 +25,7 @@
package jdk.nashorn.internal.ir;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
@@ -33,9 +34,10 @@ import jdk.nashorn.internal.runtime.Source;
* node means "this code will be executed" and evaluating it results in
* statements being added to the IR
*/
-public class ExecuteNode extends Node {
+@Immutable
+public final class ExecuteNode extends Node {
/** Expression to execute. */
- private Node expression;
+ private final Node expression;
/**
* Constructor
@@ -50,6 +52,11 @@ public class ExecuteNode extends Node {
this.expression = expression;
}
+ private ExecuteNode(final ExecuteNode executeNode, final Node expression) {
+ super(executeNode);
+ this.expression = expression;
+ }
+
/**
* Constructor
*
@@ -60,34 +67,15 @@ public class ExecuteNode extends Node {
this.expression = expression;
}
- private ExecuteNode(final ExecuteNode executeNode, final CopyState cs) {
- super(executeNode);
- this.expression = cs.existingOrCopy(executeNode.expression);
- }
-
- @Override
- protected Node copy(final CopyState cs) {
- return new ExecuteNode(this, cs);
- }
-
- @Override
- public boolean equals(final Object other) {
- if (!super.equals(other)) {
- return false;
- }
- return expression.equals(((ExecuteNode)other).getExpression());
- }
-
@Override
- public int hashCode() {
- return super.hashCode() ^ expression.hashCode();
+ public boolean isTerminal() {
+ return expression.isTerminal();
}
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterExecuteNode(this) != null) {
- setExpression(expression.accept(visitor));
- return visitor.leaveExecuteNode(this);
+ if (visitor.enterExecuteNode(this)) {
+ return visitor.leaveExecuteNode(setExpression(expression.accept(visitor)));
}
return this;
@@ -109,8 +97,12 @@ public class ExecuteNode extends Node {
/**
* Reset the expression to be executed
* @param expression the expression
+ * @return new or same execute node
*/
- public void setExpression(final Node expression) {
- this.expression = expression;
+ public ExecuteNode setExpression(final Node expression) {
+ if (this.expression == expression) {
+ return this;
+ }
+ return new ExecuteNode(this, expression);
}
}
diff --git a/src/jdk/nashorn/internal/ir/Flags.java b/src/jdk/nashorn/internal/ir/Flags.java
new file mode 100644
index 00000000..94a21098
--- /dev/null
+++ b/src/jdk/nashorn/internal/ir/Flags.java
@@ -0,0 +1,69 @@
+/*
+ * 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.ir;
+
+/**
+ * Interface implemented by all nodes that have flags in
+ * a lexical context. This is needed as we sometimes have to save
+ * the setting of flags in the lexical context until a block
+ * is completely finished and its final version (after multiple
+ * copy on writes) is placed in the lexical context
+ *
+ * @param <T> lexical context node that can have flags set during code generation
+ */
+public interface Flags<T extends LexicalContextNode> {
+
+ /**
+ * Check if a flag is set in a lexical context node
+ * @param flag flag to check
+ * @return flags
+ */
+ public boolean getFlag(int flag);
+
+ /**
+ * Clear a flag of a LexicalContextNode
+ * @param lc lexical context
+ * @param flag flag to clear
+ * @return the new LexicalContext node if flags were changed, same otherwise
+ */
+ public T clearFlag(final LexicalContext lc, int flag);
+
+ /**
+ * Set a flag of a LexicalContextNode
+ * @param lc lexical context
+ * @param flag flag to set
+ * @return the new LexicalContext node if flags were changed, same otherwise
+ */
+ public T setFlag(final LexicalContext lc, int flag);
+
+ /**
+ * Set all flags of a LexicalContextNode, overwriting previous flags
+ * @param lc lexical context
+ * @param flags new flags value
+ * @return the new LexicalContext node if flags were changed, same otherwise
+ */
+ public T setFlags(final LexicalContext lc, int flags);
+}
diff --git a/src/jdk/nashorn/internal/ir/ForNode.java b/src/jdk/nashorn/internal/ir/ForNode.java
index e55054dd..057b8464 100644
--- a/src/jdk/nashorn/internal/ir/ForNode.java
+++ b/src/jdk/nashorn/internal/ir/ForNode.java
@@ -25,73 +25,75 @@
package jdk.nashorn.internal.ir;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
/**
* IR representing a FOR statement.
- *
*/
-public class ForNode extends WhileNode {
+@Immutable
+public final class ForNode extends LoopNode {
/** Initialize expression. */
- private Node init;
+ private final Node init;
/** Test expression. */
- private Node modify;
+ private final Node modify;
/** Iterator symbol. */
private Symbol iterator;
- /** is for in */
- private boolean isForIn;
+ /** Is this a normal for loop? */
+ public static final int IS_FOR = 1 << 0;
+
+ /** Is this a normal for in loop? */
+ public static final int IS_FOR_IN = 1 << 1;
+
+ /** Is this a normal for each in loop? */
+ public static final int IS_FOR_EACH = 1 << 2;
- /** is for each */
- private boolean isForEach;
+ private final int flags;
/**
* Constructor
*
- * @param source the source
- * @param token token
- * @param finish finish
+ * @param source the source
+ * @param token token
+ * @param finish finish
+ * @param init init
+ * @param test test
+ * @param body body
+ * @param modify modify
+ * @param flags flags
*/
- public ForNode(final Source source, final long token, final int finish) {
- super(source, token, finish);
+ public ForNode(final Source source, final long token, final int finish, final Node init, final Node test, final Block body, final Node modify, final int flags) {
+ super(source, token, finish, test, body, false);
+ this.init = init;
+ this.modify = modify;
+ this.flags = flags;
}
- private ForNode(final ForNode forNode, final CopyState cs) {
- super(forNode, cs);
-
- this.init = cs.existingOrCopy(forNode.init);
- this.modify = cs.existingOrCopy(forNode.modify);
- this.iterator = forNode.iterator;
- this.isForIn = forNode.isForIn;
- this.isForEach = forNode.isForEach;
+ private ForNode(final ForNode forNode, final Node init, final Node test, final Block body, final Node modify, final int flags, final boolean controlFlowEscapes) {
+ super(forNode, test, body, controlFlowEscapes);
+ this.init = init;
+ this.modify = modify;
+ this.flags = flags;
+ this.iterator = forNode.iterator; //TODO is this acceptable? symbols are never cloned, just copied as references
}
@Override
- protected Node copy(final CopyState cs) {
- return new ForNode(this, cs);
+ public Node ensureUniqueLabels(LexicalContext lc) {
+ return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes));
}
@Override
- public Node accept(final NodeVisitor visitor) {
- if (visitor.enterForNode(this) != null) {
- if (init != null) {
- init = init.accept(visitor);
- }
-
- if (test != null) {
- test = test.accept(visitor);
- }
-
- if (modify != null) {
- modify = modify.accept(visitor);
- }
-
- body = (Block)body.accept(visitor);
-
- return visitor.leaveForNode(this);
+ protected Node accept(final LexicalContext lc, final NodeVisitor visitor) {
+ if (visitor.enterForNode(this)) {
+ return visitor.leaveForNode(
+ setInit(lc, init == null ? null : init.accept(visitor)).
+ setTest(lc, test == null ? null : test.accept(visitor)).
+ setModify(lc, modify == null ? null : modify.accept(visitor)).
+ setBody(lc, (Block)body.accept(visitor)));
}
return this;
@@ -122,6 +124,19 @@ public class ForNode extends WhileNode {
sb.append(')');
}
+ @Override
+ public boolean hasGoto() {
+ return !isForIn() && test == null;
+ }
+
+ @Override
+ public boolean mustEnter() {
+ if (isForIn()) {
+ return false; //may be an empty set to iterate over, then we skip the loop
+ }
+ return test == null;
+ }
+
/**
* Get the initialization expression for this for loop
* @return the initialization expression
@@ -132,10 +147,15 @@ public class ForNode extends WhileNode {
/**
* Reset the initialization expression for this for loop
+ * @param lc lexical context
* @param init new initialization expression
+ * @return new for node if changed or existing if not
*/
- public void setInit(final Node init) {
- this.init = init;
+ public ForNode setInit(final LexicalContext lc, final Node init) {
+ if (this.init == init) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes));
}
/**
@@ -143,14 +163,16 @@ public class ForNode extends WhileNode {
* @return true if this is a for in constructor
*/
public boolean isForIn() {
- return isForIn;
+ return (flags & IS_FOR_IN) != 0;
}
/**
* Flag this to be a for in construct
+ * @param lc lexical context
+ * @return new for node if changed or existing if not
*/
- public void setIsForIn() {
- this.isForIn = true;
+ public ForNode setIsForIn(final LexicalContext lc) {
+ return setFlags(lc, flags | IS_FOR_IN);
}
/**
@@ -159,14 +181,16 @@ public class ForNode extends WhileNode {
* @return true if this is a for each construct
*/
public boolean isForEach() {
- return isForEach;
+ return (flags & IS_FOR_EACH) != 0;
}
/**
* Flag this to be a for each construct
+ * @param lc lexical context
+ * @return new for node if changed or existing if not
*/
- public void setIsForEach() {
- this.isForEach = true;
+ public ForNode setIsForEach(final LexicalContext lc) {
+ return setFlags(lc, flags | IS_FOR_EACH);
}
/**
@@ -195,10 +219,15 @@ public class ForNode extends WhileNode {
/**
* Reset the modification expression for this ForNode
+ * @param lc lexical context
* @param modify new modification expression
+ * @return new for node if changed or existing if not
*/
- public void setModify(final Node modify) {
- this.modify = modify;
+ public ForNode setModify(final LexicalContext lc, final Node modify) {
+ if (this.modify == modify) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes));
}
@Override
@@ -207,7 +236,39 @@ public class ForNode extends WhileNode {
}
@Override
- public void setTest(final Node test) {
- this.test = test;
+ public ForNode setTest(final LexicalContext lc, final Node test) {
+ if (this.test == test) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes));
}
+
+ @Override
+ public Block getBody() {
+ return body;
+ }
+
+ @Override
+ public ForNode setBody(final LexicalContext lc, final Block body) {
+ if (this.body == body) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes));
+ }
+
+ @Override
+ public ForNode setControlFlowEscapes(final LexicalContext lc, final boolean controlFlowEscapes) {
+ if (this.controlFlowEscapes == controlFlowEscapes) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes));
+ }
+
+ private ForNode setFlags(final LexicalContext lc, final int flags) {
+ if (this.flags == flags) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes));
+ }
+
}
diff --git a/src/jdk/nashorn/internal/ir/FunctionNode.java b/src/jdk/nashorn/internal/ir/FunctionNode.java
index 2b0e109b..60fe64ff 100644
--- a/src/jdk/nashorn/internal/ir/FunctionNode.java
+++ b/src/jdk/nashorn/internal/ir/FunctionNode.java
@@ -30,23 +30,19 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.TEMP_PREFIX;
import static jdk.nashorn.internal.ir.Symbol.IS_CONSTANT;
import static jdk.nashorn.internal.ir.Symbol.IS_TEMP;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.Iterator;
+import java.util.HashSet;
import java.util.List;
-import java.util.Map;
-import java.util.Stack;
+import java.util.Set;
import jdk.nashorn.internal.codegen.CompileUnit;
import jdk.nashorn.internal.codegen.Compiler;
-import jdk.nashorn.internal.codegen.Frame;
-import jdk.nashorn.internal.codegen.MethodEmitter;
+import jdk.nashorn.internal.codegen.CompilerConstants;
import jdk.nashorn.internal.codegen.Namespace;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.annotations.Ignore;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
-import jdk.nashorn.internal.parser.Parser;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.Source;
import jdk.nashorn.internal.runtime.UserAccessorProperty;
@@ -55,9 +51,11 @@ import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
/**
* IR representation for function (or script.)
*/
-public class FunctionNode extends Block {
+@Immutable
+public final class FunctionNode extends LexicalContextNode implements Flags<FunctionNode> {
- private static final Type FUNCTION_TYPE = Type.typeFor(ScriptFunction.class);
+ /** Type used for all FunctionNodes */
+ public static final Type FUNCTION_TYPE = Type.typeFor(ScriptFunction.class);
/** Function kinds */
public enum Kind {
@@ -90,134 +88,106 @@ public class FunctionNode extends Block {
/** method has had its types finalized */
FINALIZED,
/** method has been emitted to bytecode */
- EMITTED,
- /** code installed in a class loader */
- INSTALLED
+ EMITTED
}
/** External function identifier. */
@Ignore
- private IdentNode ident;
+ private final IdentNode ident;
+
+ /** The body of the function node */
+ private final Block body;
/** Internal function name. */
- private String name;
+ private final String name;
/** Compilation unit. */
- private CompileUnit compileUnit;
-
- /** Method emitter for current method. */
- private MethodEmitter method;
+ private final CompileUnit compileUnit;
/** Function kind. */
- private Kind kind;
+ private final Kind kind;
/** List of parameters. */
- private List<IdentNode> parameters;
+ private final List<IdentNode> parameters;
/** First token of function. **/
- private long firstToken;
+ private final long firstToken;
/** Last token of function. **/
- private long lastToken;
+ private final long lastToken;
- /** Variable frames. */
- private Frame frames;
+ /** Declared symbols in this function node */
+ @Ignore
+ private final Set<Symbol> declaredSymbols;
/** Method's namespace. */
private final Namespace namespace;
- /** Node representing current this. */
- @Ignore
- private IdentNode thisNode;
-
- /** Node representing current scope. */
- @Ignore
- private IdentNode scopeNode;
-
- /** Node representing return value. */
- @Ignore
- private IdentNode resultNode;
-
- /** Node representing current arguments. */
- @Ignore
- private IdentNode argumentsNode;
-
- /** Node representing callee */
- @Ignore
- private IdentNode calleeNode;
-
- /** Node representing varargs */
- @Ignore
- private IdentNode varArgsNode;
-
- /** Pending label list. */
- private final Stack<LabelNode> labelStack;
-
- /** Pending control list. */
- private final Stack<Node> controlStack;
-
- /** VarNode for this function statement */
- @Ignore //this is explicit code anyway and should not be traversed after lower
- private VarNode funcVarNode;
-
- /** Line number for function declaration */
- @Ignore
- private LineNumberNode funcVarLineNumberNode;
-
- /** Initializer var func = __callee__, where applicable */
- @Ignore
- private Node selfSymbolInit;
-
/** Current compilation state */
@Ignore
private final EnumSet<CompilationState> compilationState;
- /** Type hints, e.g based on parameters at call site */
- private final Map<IdentNode, Type> specializedTypes;
-
/** Function flags. */
- private int flags;
+ private final int flags;
/** Is anonymous function flag. */
- private static final int IS_ANONYMOUS = 1 << 0;
+ public static final int IS_ANONYMOUS = 1 << 0;
+
/** Is the function created in a function declaration (as opposed to a function expression) */
- private static final int IS_DECLARED = 1 << 1;
+ public static final int IS_DECLARED = 1 << 1;
+
/** is this a strict mode function? */
- private static final int IS_STRICT_MODE = 1 << 2;
+ public static final int IS_STRICT = 1 << 2;
+
/** Does the function use the "arguments" identifier ? */
- private static final int USES_ARGUMENTS = 1 << 3;
- /** Are we lowered ? */
- private static final int IS_LOWERED = 1 << 4;
+ public static final int USES_ARGUMENTS = 1 << 3;
+
/** Has this node been split because it was too large? */
- private static final int IS_SPLIT = 1 << 5;
- /** Does the function call eval? */
- private static final int HAS_EVAL = 1 << 6;
- /** Does the function contain a with block ? */
- private static final int HAS_WITH = 1 << 7;
- /** Does a descendant function contain a with or eval? */
- private static final int HAS_DESCENDANT_WITH_OR_EVAL = 1 << 8;
- /** Does the function define "arguments" identifier as a parameter of nested function name? */
- private static final int DEFINES_ARGUMENTS = 1 << 9;
- /** Does the function need a self symbol? */
- private static final int NEEDS_SELF_SYMBOL = 1 << 10;
+ public static final int IS_SPLIT = 1 << 4;
+
+ /** Does the function call eval? If it does, then all variables in this function might be get/set by it and it can
+ * introduce new variables into this function's scope too.*/
+ public static final int HAS_EVAL = 1 << 5;
+
+ /** Does a nested function contain eval? If it does, then all variables in this function might be get/set by it. */
+ public static final int HAS_NESTED_EVAL = 1 << 6;
+
+ /**
+ * Flag this function as one that defines the identifier "arguments" as a function parameter or nested function
+ * name. This precludes it from needing to have an Arguments object defined as "arguments" local variable. Note that
+ * defining a local variable named "arguments" still requires construction of the Arguments object (see
+ * ECMAScript 5.1 Chapter 10.5).
+ * @see #needsArguments()
+ */
+ public static final int DEFINES_ARGUMENTS = 1 << 8;
+
/** Does this function or any of its descendants use variables from an ancestor function's scope (incl. globals)? */
- private static final int USES_ANCESTOR_SCOPE = 1 << 11;
+ public static final int USES_ANCESTOR_SCOPE = 1 << 9;
+
/** Is this function lazily compiled? */
- private static final int IS_LAZY = 1 << 12;
+ public static final int IS_LAZY = 1 << 10;
+
/** Does this function have lazy, yet uncompiled children */
- private static final int HAS_LAZY_CHILDREN = 1 << 13;
+ public static final int HAS_LAZY_CHILDREN = 1 << 11;
+
/** Does this function have lazy, yet uncompiled children */
- private static final int IS_PROGRAM = 1 << 14;
+ public static final int IS_PROGRAM = 1 << 12;
+
+ /** Does this function have nested declarations? */
+ public static final int HAS_FUNCTION_DECLARATIONS = 1 << 13;
+
+ /** Does this function or any nested functions contain an eval? */
+ private static final int HAS_DEEP_EVAL = HAS_EVAL | HAS_NESTED_EVAL;
- /** Does this function or any nested functions contain a with or an eval? */
- private static final int HAS_DEEP_WITH_OR_EVAL = HAS_EVAL | HAS_WITH | HAS_DESCENDANT_WITH_OR_EVAL;
/** Does this function need to store all its variables in scope? */
- private static final int HAS_ALL_VARS_IN_SCOPE = HAS_DEEP_WITH_OR_EVAL | IS_SPLIT | HAS_LAZY_CHILDREN;
+ private static final int HAS_ALL_VARS_IN_SCOPE = HAS_DEEP_EVAL | IS_SPLIT | HAS_LAZY_CHILDREN;
+
/** Does this function potentially need "arguments"? Note that this is not a full test, as further negative check of REDEFINES_ARGS is needed. */
private static final int MAYBE_NEEDS_ARGUMENTS = USES_ARGUMENTS | HAS_EVAL;
- /** Does this function need the parent scope? It needs it if either it or its descendants use variables from it, or have a deep with or eval.
+
+ /** 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.
* We also pessimistically need a parent scope if we have lazy children that have not yet been compiled */
- private static final int NEEDS_PARENT_SCOPE = USES_ANCESTOR_SCOPE | HAS_DEEP_WITH_OR_EVAL | HAS_LAZY_CHILDREN;
+ private static final int NEEDS_PARENT_SCOPE = USES_ANCESTOR_SCOPE | HAS_DEEP_EVAL | HAS_LAZY_CHILDREN;
/** What is the return type of this function? */
private Type returnType = Type.UNKNOWN;
@@ -225,107 +195,77 @@ public class FunctionNode extends Block {
/**
* Constructor
*
- * @param source the source
- * @param token token
- * @param finish finish
- * @param namespace the namespace
- * @param ident the identifier
- * @param name the name of the function
- */
- public FunctionNode(final Source source, final long token, final int finish, final Namespace namespace, final IdentNode ident, final String name) {
+ * @param source the source
+ * @param token token
+ * @param finish finish
+ * @param firstToken first token of the funtion node (including the function declaration)
+ * @param namespace the namespace
+ * @param ident the identifier
+ * @param name the name of the function
+ * @param parameters parameter list
+ * @param kind kind of function as in {@link FunctionNode.Kind}
+ * @param flags initial flags
+ */
+ public FunctionNode(
+ final Source source,
+ final long token,
+ final int finish,
+ final long firstToken,
+ final Namespace namespace,
+ final IdentNode ident,
+ final String name,
+ final List<IdentNode> parameters,
+ final FunctionNode.Kind kind,
+ final int flags) {
super(source, token, finish);
this.ident = ident;
this.name = name;
- this.kind = Kind.NORMAL;
- this.parameters = new ArrayList<>();
- this.firstToken = token;
+ this.kind = kind;
+ this.parameters = parameters;
+ this.firstToken = firstToken;
this.lastToken = token;
this.namespace = namespace;
- this.labelStack = new Stack<>();
- this.controlStack = new Stack<>();
this.compilationState = EnumSet.of(CompilationState.INITIALIZED);
- this.specializedTypes = new HashMap<>();
+ this.declaredSymbols = new HashSet<>();
+ this.flags = flags;
+ this.compileUnit = null;
+ this.body = null;
}
- private FunctionNode(final FunctionNode functionNode, final CopyState cs) {
- super(functionNode, cs);
-
- this.ident = (IdentNode)cs.existingOrCopy(functionNode.ident);
- this.name = functionNode.name;
+ private FunctionNode(final FunctionNode functionNode, final long lastToken, final int flags, final Type returnType, final CompileUnit compileUnit, final EnumSet<CompilationState> compilationState, final Block body) {
+ super(functionNode);
+ this.flags = flags;
+ this.returnType = returnType;
+ this.compileUnit = compileUnit;
+ this.lastToken = lastToken;
+ this.compilationState = compilationState;
+ this.body = body;
+
+ // the fields below never change - they are final and assigned in constructor
+ this.name = functionNode.name;
+ this.ident = functionNode.ident;
+ this.namespace = functionNode.namespace;
+ this.declaredSymbols = functionNode.declaredSymbols;
this.kind = functionNode.kind;
-
- this.parameters = new ArrayList<>();
- for (final IdentNode param : functionNode.getParameters()) {
- this.parameters.add((IdentNode)cs.existingOrCopy(param));
- }
-
- this.firstToken = functionNode.firstToken;
- this.lastToken = functionNode.lastToken;
- this.namespace = functionNode.getNamespace();
- this.thisNode = (IdentNode)cs.existingOrCopy(functionNode.thisNode);
- this.scopeNode = (IdentNode)cs.existingOrCopy(functionNode.scopeNode);
- this.resultNode = (IdentNode)cs.existingOrCopy(functionNode.resultNode);
- this.argumentsNode = (IdentNode)cs.existingOrCopy(functionNode.argumentsNode);
- this.varArgsNode = (IdentNode)cs.existingOrCopy(functionNode.varArgsNode);
- this.calleeNode = (IdentNode)cs.existingOrCopy(functionNode.calleeNode);
- this.labelStack = new Stack<>();
- this.controlStack = new Stack<>();
-
- this.flags = functionNode.flags;
-
- this.funcVarNode = (VarNode)cs.existingOrCopy(functionNode.funcVarNode);
- /** VarNode for this function statement */
-
- this.compilationState = EnumSet.copyOf(functionNode.compilationState);
- this.specializedTypes = new HashMap<>();
+ this.parameters = functionNode.parameters;
+ this.firstToken = functionNode.firstToken;
}
@Override
- protected Node copy(final CopyState cs) {
- // deep clone all parent blocks
- return new FunctionNode(this, cs);
- }
-
- @Override
- public Node accept(final NodeVisitor visitor) {
- final FunctionNode saveFunctionNode = visitor.getCurrentFunctionNode();
- final Block saveBlock = visitor.getCurrentBlock();
- final MethodEmitter saveMethodEmitter = visitor.getCurrentMethodEmitter();
- final CompileUnit saveCompileUnit = visitor.getCurrentCompileUnit();
-
- visitor.setCurrentFunctionNode(this);
- visitor.setCurrentBlock(this);
-
- try {
- if (visitor.enterFunctionNode(this) != null) {
- if (ident != null) {
- ident = (IdentNode)ident.accept(visitor);
- }
-
- for (int i = 0, count = parameters.size(); i < count; i++) {
- parameters.set(i, (IdentNode)parameters.get(i).accept(visitor));
- }
-
- for (int i = 0, count = statements.size(); i < count; i++) {
- statements.set(i, statements.get(i).accept(visitor));
- }
-
- return visitor.leaveFunctionNode(this);
- }
- } finally {
- visitor.setCurrentBlock(saveBlock);
- visitor.setCurrentFunctionNode(saveFunctionNode);
- visitor.setCurrentCompileUnit(saveCompileUnit);
- visitor.setCurrentMethodEmitter(saveMethodEmitter);
+ public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
+ if (visitor.enterFunctionNode(this)) {
+ return visitor.leaveFunctionNode(setBody(lc, (Block)body.accept(visitor)));
}
-
return this;
}
- @Override
- public boolean needsScope() {
- return super.needsScope() || isProgram();
+ /**
+ * Get the compilation state of this function
+ * @return the compilation state
+ */
+ public EnumSet<CompilationState> getState() {
+ return compilationState;
}
/**
@@ -357,62 +297,17 @@ public class FunctionNode extends Block {
* FunctionNode has been lowered, the compiler will add
* {@code CompilationState#LOWERED} to the state vector
*
+ * @param lc lexical context
* @param state {@link CompilationState} to add
+ * @return function node or a new one if state was changed
*/
- public void setState(final CompilationState state) {
- compilationState.add(state);
- }
-
- /*
- * Frame management.
- */
-
- /**
- * Push a new block frame.
- *
- * @return the new frame
- */
- public final Frame pushFrame() {
- frames = new Frame(frames);
- return frames;
- }
-
- /**
- * Pop a block frame.
- */
- public final void popFrame() {
- frames = frames.getPrevious();
- }
-
- /**
- * Create a temporary variable to the current frame.
- *
- * @param currentFrame Frame to add to - defaults to current function frame
- * @param type Strong type of symbol.
- * @param node Primary node to use symbol.
- *
- * @return Symbol used.
- */
- public Symbol newTemporary(final Frame currentFrame, final Type type, final Node node) {
- assert currentFrame != null;
- Symbol symbol = node.getSymbol();
-
- // If no symbol already present.
- if (symbol == null) {
- final String uname = uniqueName(TEMP_PREFIX.tag());
- symbol = new Symbol(uname, IS_TEMP, type);
- symbol.setNode(node);
- }
-
- // Assign a slot if it doesn't have one.
- if (!symbol.hasSlot()) {
- currentFrame.addSymbol(symbol);
+ public FunctionNode setState(final LexicalContext lc, final CompilationState state) {
+ if (this.compilationState.equals(state)) {
+ return this;
}
-
- // Set symbol to node.
- node.setSymbol(symbol);
-
- return symbol;
+ final EnumSet<CompilationState> newState = EnumSet.copyOf(this.compilationState);
+ newState.add(state);
+ return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, newState, body));
}
/**
@@ -425,18 +320,6 @@ public class FunctionNode extends Block {
}
/**
- * Add a new temporary variable to the current frame
- *
- * @param type Strong type of symbol
- * @param node Primary node to use symbol
- *
- * @return symbol used
- */
- public Symbol newTemporary(final Type type, final Node node) {
- return newTemporary(frames, type, node);
- }
-
- /**
* Create a virtual symbol for a literal.
*
* @param literalNode Primary node to use symbol.
@@ -444,9 +327,8 @@ public class FunctionNode extends Block {
* @return Symbol used.
*/
public Symbol newLiteral(final LiteralNode<?> literalNode) {
- final String uname = uniqueName(LITERAL_PREFIX.tag());
+ final String uname = uniqueName(LITERAL_PREFIX.symbolName());
final Symbol symbol = new Symbol(uname, IS_CONSTANT, literalNode.getType());
- symbol.setNode(literalNode);
literalNode.setSymbol(symbol);
return symbol;
@@ -482,84 +364,43 @@ public class FunctionNode extends Block {
sb.append(')');
}
- /**
- * Returns true if the function is the top-level program.
- * @return True if this function node represents the top-level program.
- */
- public boolean isProgram() {
- return (flags & IS_PROGRAM) != 0;
- }
-
- /**
- * Marks the function as representing the top-level program.
- */
- public void setProgram() {
- flags |= IS_PROGRAM;
+ @Override
+ public boolean getFlag(final int flag) {
+ return (flags & flag) != 0;
}
- /**
- * Get the control stack. Used when parsing to establish nesting depths of
- * different control structures
- *
- * @return the control stack
- */
- public Stack<Node> getControlStack() {
- return controlStack;
+ @Override
+ public FunctionNode setFlags(final LexicalContext lc, int flags) {
+ if (this.flags == flags) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body));
}
- /**
- * Should this function node be lazily code generated, i.e. first at link time
- * @return true if lazy
- */
- public boolean isLazy() {
- return (flags & IS_LAZY) != 0;
+ @Override
+ public FunctionNode clearFlag(final LexicalContext lc, final int flag) {
+ return setFlags(lc, flags & ~flag);
}
- /**
- * Set if this function should be lazily generated
- * @param isLazy is lazy
- */
- public void setIsLazy(final boolean isLazy) {
- this.flags = isLazy ? flags | IS_LAZY : flags & ~IS_LAZY;
+ @Override
+ public FunctionNode setFlag(final LexicalContext lc, final int flag) {
+ return setFlags(lc, flags | flag);
}
/**
- * Check if the {@code with} keyword is used in this function
- *
- * @return true if {@code with} is used
+ * Returns true if the function is the top-level program.
+ * @return True if this function node represents the top-level program.
*/
- public boolean hasWith() {
- return (flags & HAS_WITH) != 0;
+ public boolean isProgram() {
+ return getFlag(IS_PROGRAM);
}
/**
- * Flag this function as using the {@code with} keyword
- * @param ancestors the iterator over functions in this functions's containing lexical context
+ * Should this function node be lazily code generated, i.e. first at link time
+ * @return true if lazy
*/
- public void setHasWith(final Iterator<FunctionNode> ancestors) {
- if(!hasWith()) {
- this.flags |= HAS_WITH;
- // with requires scope in parents.
- // TODO: refine this. with should not force all variables in parents to be in scope, only those that are
- // actually referenced as identifiers by name
- markParentForWithOrEval(ancestors);
- }
- }
-
- private void markParentForWithOrEval(final Iterator<FunctionNode> ancestors) {
- // If this is invoked, then either us or a descendant uses with or eval, meaning we must have our own scope.
- setNeedsScope();
-
- if(ancestors.hasNext()) {
- ancestors.next().setDescendantHasWithOrEval(ancestors);
- }
- }
-
- private void setDescendantHasWithOrEval(final Iterator<FunctionNode> ancestors) {
- if((flags & HAS_DESCENDANT_WITH_OR_EVAL) == 0) {
- flags |= HAS_DESCENDANT_WITH_OR_EVAL;
- markParentForWithOrEval(ancestors);
- }
+ public boolean isLazy() {
+ return getFlag(IS_LAZY);
}
/**
@@ -568,30 +409,7 @@ public class FunctionNode extends Block {
* @return true if {@code eval} is used
*/
public boolean hasEval() {
- return (flags & HAS_EVAL) != 0;
- }
-
- /**
- * Flag this function as calling the {@code eval} function
- * @param ancestors the iterator over functions in this functions's containing lexical context
- */
- public void setHasEval(final Iterator<FunctionNode> ancestors) {
- if(!hasEval()) {
- this.flags |= HAS_EVAL;
- markParentForWithOrEval(ancestors);
- }
- }
-
- /**
- * Test whether this function or any of its nested functions contains a <tt>with</tt> statement
- * or an <tt>eval</tt> call.
- *
- * @see #hasWith()
- * @see #hasEval()
- * @return true if this or a nested function contains with or eval
- */
- public boolean hasDeepWithOrEval() {
- return (flags & HAS_DEEP_WITH_OR_EVAL) != 0;
+ return getFlag(HAS_EVAL);
}
/**
@@ -603,90 +421,11 @@ public class FunctionNode extends Block {
}
/**
- * Set the first token for this function
- * @param firstToken the first token
- */
- public void setFirstToken(final long firstToken) {
- this.firstToken = firstToken;
- }
-
- /**
- * Returns a list of functions declared by this function. Only includes declared functions, and does not include any
- * function expressions that might occur in its body.
- * @return a list of functions declared by this function.
- */
- public List<FunctionNode> getDeclaredFunctions() {
- // Note that the function does not have a dedicated list of declared functions, but rather relies on the
- // invariant that all function declarations are at the beginning of the statement list as VarNode with a
- // FunctionNode marked as statement with its variable initializer. Every VarNode is also preceded by a
- // LineNumberNode. This invariant is established by the parser and has to be preserved in visitors.
- final List<FunctionNode> fns = new ArrayList<>();
- for (final Node stmt : statements) {
- if(stmt instanceof LineNumberNode) {
- continue;
- } else if(stmt instanceof VarNode) {
- final Node init = ((VarNode)stmt).getInit();
- if(init instanceof FunctionNode) {
- final FunctionNode fn = (FunctionNode)init;
- if(fn.isDeclared()) {
- fns.add(fn);
- continue;
- }
- }
- }
- // Node is neither a LineNumberNode, nor a function declaration VarNode. Since all function declarations are
- // at the start of the function, we've reached the end of function declarations.
- break;
- }
- return fns;
- }
-
- /**
- * Get the label stack. This is used by the parser to establish
- * label nesting depth
- *
- * @return the label stack
- */
- public Stack<LabelNode> getLabelStack() {
- return labelStack;
- }
-
- /**
- * If this function needs to use var args, return the identifier to the node used
- * for the var args structure
- *
- * @return IdentNode representing the var args structure
- */
- public IdentNode getVarArgsNode() {
- return varArgsNode;
- }
-
- /**
- * Set the identifier to the node used for the var args structure
- *
- * @param varArgsNode IdentNode representing the var args
- */
- public void setVarArgsNode(final IdentNode varArgsNode) {
- this.varArgsNode = varArgsNode;
- }
-
- /**
- * If this function uses the {@code callee} variable, return the node used
- * as this variable
- *
- * @return an IdentNode representing the {@code callee} variable
+ * Check whether this function has nested function declarations
+ * @return true if nested function declarations exist
*/
- public IdentNode getCalleeNode() {
- return calleeNode;
- }
-
- /**
- * If this function uses the {@code callee} variable, set the node representing the
- * callee
- * @param calleeNode an IdentNode representing the callee
- */
- public void setCalleeNode(final IdentNode calleeNode) {
- this.calleeNode = calleeNode;
+ public boolean hasDeclaredFunctions() {
+ return getFlag(HAS_FUNCTION_DECLARATIONS);
}
/**
@@ -697,42 +436,54 @@ public class FunctionNode extends Block {
* @return true if the function's generated Java method needs a {@code callee} parameter.
*/
public boolean needsCallee() {
- return needsParentScope() || needsSelfSymbol() || (needsArguments() && !isStrictMode());
+ return needsParentScope() || needsSelfSymbol() || (needsArguments() && !isStrict());
}
/**
- * If this is a function where {@code arguments} is used, return the node used as the {@code arguments}
- * variable
- * @return an IdentNode representing {@code arguments}
+ * Get the identifier for this function
+ * @return the identifier as an IdentityNode
*/
- public IdentNode getArgumentsNode() {
- return argumentsNode;
+ public IdentNode getIdent() {
+ return ident;
}
/**
- * If this is a Function where {@code arguments} is used, an identifier to the node representing
- * the {@code arguments} value has to be supplied by the compiler
- *
- * @param argumentsNode IdentNode that represents {@code arguments}
+ * Return a set of symbols declared in this function node. This
+ * is only relevant after Attr, otherwise it will be an empty
+ * set as no symbols have been introduced
+ * @return set of declared symbols in function
*/
- public void setArgumentsNode(final IdentNode argumentsNode) {
- this.argumentsNode = argumentsNode;
+ public Set<Symbol> getDeclaredSymbols() {
+ return Collections.unmodifiableSet(declaredSymbols);
}
/**
- * Get the identifier for this function
- * @return the identifier as an IdentityNode
+ * Add a declared symbol to this function node
+ * @param symbol symbol that is declared
*/
- public IdentNode getIdent() {
- return ident;
+ public void addDeclaredSymbol(final Symbol symbol) {
+ declaredSymbols.add(symbol);
+ }
+
+ /**
+ * Get the function body
+ * @return the function body
+ */
+ public Block getBody() {
+ return body;
}
/**
- * Reset the identifier for this function
- * @param ident IdentNode for new identifier
+ * Reset the function body
+ * @param lc lexical context
+ * @param body new body
+ * @return new function node if body changed, same if not
*/
- public void setIdent(final IdentNode ident) {
- this.ident = ident;
+ public FunctionNode setBody(final LexicalContext lc, final Block body) {
+ if(this.body == body) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body));
}
/**
@@ -748,17 +499,6 @@ public class FunctionNode extends Block {
}
/**
- * Flag this function as one that defines the identifier "arguments" as a function parameter or nested function
- * name. This precludes it from needing to have an Arguments object defined as "arguments" local variable. Note that
- * defining a local variable named "arguments" still requires construction of the Arguments object (see
- * ECMAScript 5.1 Chapter 10.5).
- * @see #needsArguments()
- */
- public void setDefinesArguments() {
- this.flags |= DEFINES_ARGUMENTS;
- }
-
- /**
* Returns true if this function needs to have an Arguments object defined as a local variable named "arguments".
* Functions that use "arguments" as identifier and don't define it as a name of a parameter or a nested function
* (see ECMAScript 5.1 Chapter 10.5), as well as any function that uses eval or with, or has a nested function that
@@ -770,15 +510,7 @@ public class FunctionNode extends Block {
public boolean needsArguments() {
// uses "arguments" or calls eval, but it does not redefine "arguments", and finally, it's not a script, since
// for top-level script, "arguments" is picked up from Context by Global.init() instead.
- return (flags & MAYBE_NEEDS_ARGUMENTS) != 0 && (flags & DEFINES_ARGUMENTS) == 0 && !isProgram();
- }
-
- /**
- * Flags this function as one that uses the "arguments" identifier.
- * @see #needsArguments()
- */
- public void setUsesArguments() {
- flags |= USES_ARGUMENTS;
+ return getFlag(MAYBE_NEEDS_ARGUMENTS) && !getFlag(DEFINES_ARGUMENTS) && !isProgram();
}
/**
@@ -789,7 +521,7 @@ public class FunctionNode extends Block {
* @return true if the function needs parent scope.
*/
public boolean needsParentScope() {
- return (flags & NEEDS_PARENT_SCOPE) != 0 || isProgram();
+ return getFlag(NEEDS_PARENT_SCOPE) || isProgram();
}
/**
@@ -802,15 +534,6 @@ public class FunctionNode extends Block {
}
/**
- * Set the kind of this function
- * @see FunctionNode.Kind
- * @param kind the kind
- */
- public void setKind(final Kind kind) {
- this.kind = kind;
- }
-
- /**
* Return the last token for this function's code
* @return last token
*/
@@ -820,10 +543,15 @@ public class FunctionNode extends Block {
/**
* Set the last token for this function's code
+ * @param lc lexical context
* @param lastToken the last token
+ * @return function node or a new one if state was changed
*/
- public void setLastToken(final long lastToken) {
- this.lastToken = lastToken;
+ public FunctionNode setLastToken(final LexicalContext lc, final long lastToken) {
+ if (this.lastToken == lastToken) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body));
}
/**
@@ -835,21 +563,13 @@ public class FunctionNode extends Block {
}
/**
- * Set the name of this function
- * @param name the name
- */
- public void setName(final String name) {
- this.name = name;
- }
-
- /**
* Check if this function should have all its variables in its own scope. Scripts, split sub-functions, and
* functions having with and/or eval blocks are such.
*
* @return true if all variables should be in scope
*/
public boolean allVarsInScope() {
- return isProgram() || (flags & HAS_ALL_VARS_IN_SCOPE) != 0;
+ return isProgram() || getFlag(HAS_ALL_VARS_IN_SCOPE);
}
/**
@@ -858,15 +578,7 @@ public class FunctionNode extends Block {
* @return true if this function is split from a larger one
*/
public boolean isSplit() {
- return (flags & IS_SPLIT) != 0;
- }
-
- /**
- * Flag this function node as being a sub-function generated by the splitter
- */
- public void setIsSplit() {
- this.flags |= IS_SPLIT;
- setNeedsScope();
+ return getFlag(IS_SPLIT);
}
/**
@@ -875,15 +587,7 @@ public class FunctionNode extends Block {
* @return true if there are lazy child functions
*/
public boolean hasLazyChildren() {
- return (flags & HAS_LAZY_CHILDREN) != 0;
- }
-
- /**
- * Flag this function node as having yet-to-be-generated child functions
- */
- public void setHasLazyChildren() {
- this.flags |= HAS_LAZY_CHILDREN;
- setNeedsScope();
+ return getFlag(HAS_LAZY_CHILDREN);
}
/**
@@ -895,66 +599,13 @@ public class FunctionNode extends Block {
}
/**
- * Set the paremeters to this function
- * @param parameters a list of IdentNodes representing parameters in left to right order
- */
- public void setParameters(final List<IdentNode> parameters) {
- this.parameters = parameters;
- }
-
- /**
* Get a specialized type for an identity, if one exists
* @param node node to check specialized type for
* @return null if no specialization exists, otherwise type
*/
+ @SuppressWarnings("static-method")
public Type getSpecializedType(final IdentNode node) {
- return specializedTypes.get(node);
- }
-
- /**
- * Set parameter type hints for specialization.
- * @param types types array of length equal to parameter list size
- */
- public void setParameterTypes(final Class<?>[] types) {
- assert types.length == parameters.size() : "Type vector length doesn't correspond to parameter types";
- //diff - skip the callee and this etc, they are not explicit params in the parse tree
- for (int i = 0; i < types.length ; i++) {
- specializedTypes.put(parameters.get(i), Type.typeFor(types[i]));
- }
- }
-
- /**
- * Get the identifier for the variable in which the function return value
- * should be stored
- * @return an IdentNode representing the return value
- */
- public IdentNode getResultNode() {
- return resultNode;
- }
-
- /**
- * Set the identifier representing the variable in which the function return
- * value should be stored
- * @param resultNode an IdentNode representing the return value
- */
- public void setResultNode(final IdentNode resultNode) {
- this.resultNode = resultNode;
- }
-
- /**
- * Get the identifier representing this function's scope
- * @return an IdentNode representing this function's scope
- */
- public IdentNode getScopeNode() {
- return scopeNode;
- }
-
- /**
- * Set the identifier representing this function's scope
- * @param scopeNode an IdentNode representing this function's scope
- */
- public void setScopeNode(final IdentNode scopeNode) {
- this.scopeNode = scopeNode;
+ return null; //TODO implement specialized types later
}
/**
@@ -962,15 +613,7 @@ public class FunctionNode extends Block {
* @return true if function is declared.
*/
public boolean isDeclared() {
- return (flags & IS_DECLARED) != 0;
- }
-
- /**
- * Flag this function as being created as a function declaration (as opposed to a function expression).
- * @see Parser
- */
- public void setIsDeclared() {
- this.flags |= IS_DECLARED;
+ return getFlag(IS_DECLARED);
}
/**
@@ -978,15 +621,7 @@ public class FunctionNode extends Block {
* @return true if function is anonymous
*/
public boolean isAnonymous() {
- return (flags & IS_ANONYMOUS) != 0;
- }
-
- /**
- * Flag this function as an anonymous function.
- * @see Parser
- */
- public void setIsAnonymous() {
- this.flags |= IS_ANONYMOUS;
+ return getFlag(IS_ANONYMOUS);
}
/**
@@ -995,109 +630,7 @@ public class FunctionNode extends Block {
* @return true if function needs a symbol for self
*/
public boolean needsSelfSymbol() {
- return (flags & NEEDS_SELF_SYMBOL) != 0;
- }
-
- /**
- * Get the initializer statement for the __callee__ variable, where applicable
- * for self references
- * @return initialization
- */
- public Node getSelfSymbolInit() {
- return this.selfSymbolInit;
- }
-
- /**
- * Flag the function as needing a self symbol. This is needed only for
- * self referring functions
- * @param selfSymbolInit initialization expression for self symbol
- */
- public void setNeedsSelfSymbol(final Node selfSymbolInit) {
- this.flags |= NEEDS_SELF_SYMBOL;
- this.selfSymbolInit = selfSymbolInit;
- }
-
- /**
- * Marks this function as using any of its ancestors' scopes.
- */
- public void setUsesAncestorScope() {
- this.flags |= USES_ANCESTOR_SCOPE;
- }
-
- @Override
- void setUsesParentScopeSymbol(Symbol symbol, Iterator<Block> ancestors) {
- setUsesAncestorScope();
- super.setUsesParentScopeSymbol(symbol, ancestors);
- }
-
- /**
- * Return the node representing {@code this} in this function
- * @return IdentNode representing {@code this}
- */
- public IdentNode getThisNode() {
- return thisNode;
- }
-
- /**
- * Set the node representing {@code this} in this function
- * @param thisNode identifier representing {@code this}
- */
- public void setThisNode(final IdentNode thisNode) {
- this.thisNode = thisNode;
- }
-
- /**
- * Every function declared as {@code function x()} is internally hoisted
- * and represented as {@code var x = function() ... }. This getter returns
- * the VarNode representing this virtual assignment
- *
- * @return the var node emitted for setting this function symbol
- */
- public VarNode getFunctionVarNode() {
- return funcVarNode;
- }
-
- /**
- * Set the virtual VarNode assignment for this function.
- * @see FunctionNode#getFunctionVarNode()
- *
- * @param varNode the virtual var node assignment
- */
- public void setFunctionVarNode(final VarNode varNode) {
- funcVarNode = varNode;
- }
-
- /**
- * The line number information where the function was declared must be propagated
- * to the virtual {@code var x = function() ... } assignment described in
- * {@link FunctionNode#getFunctionVarNode()}
- * This maintains the line number of the declaration
- *
- * @return a line number node representing the line this function was declared
- */
- public LineNumberNode getFunctionVarLineNumberNode() {
- return funcVarLineNumberNode;
- }
-
- /**
- * Set the virtual VarNode assignment for this function, along with
- * a line number node for tracking the original start line of the function
- * declaration
- *
- * @param varNode the virtual var node assignment
- * @param lineNumber the line number node for the function declaration
- */
- public void setFunctionVarNode(final VarNode varNode, final LineNumberNode lineNumber) {
- funcVarNode = varNode;
- funcVarLineNumberNode = lineNumber;
- }
-
- /**
- * Get the namespace this function uses for its symbols
- * @return the namespace
- */
- public Namespace getNamespace() {
- return namespace;
+ return body.getFlag(Block.NEEDS_SELF_SYMBOL);
}
@Override
@@ -1118,47 +651,38 @@ public class FunctionNode extends Block {
/**
* Set the function return type
- *
+ * @param lc lexical context
* @param returnType new return type
+ * @return function node or a new one if state was changed
*/
- public void setReturnType(final Type returnType) {
+ public FunctionNode setReturnType(final LexicalContext lc, final Type returnType) {
//we never bother with object types narrower than objects, that will lead to byte code verification errors
//as for instance even if we know we are returning a string from a method, the code generator will always
//treat it as an object, at least for now
- this.returnType = Type.widest(this.returnType, returnType.isObject() ? Type.OBJECT : returnType);
- }
-
- /**
- * Set strict mode on or off for this function
- *
- * @param isStrictMode true if strict mode should be enabled
- */
- public void setStrictMode(final boolean isStrictMode) {
- flags = isStrictMode ? flags | IS_STRICT_MODE : flags & ~IS_STRICT_MODE;
+ if (this.returnType == returnType) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(
+ lc,
+ this,
+ new FunctionNode(
+ this,
+ lastToken,
+ flags,
+ Type.widest(this.returnType, returnType.isObject() ?
+ Type.OBJECT :
+ returnType),
+ compileUnit,
+ compilationState,
+ body));
}
/**
* Check if the function is generated in strict mode
* @return true if strict mode enabled for function
*/
- public boolean isStrictMode() {
- return (flags & IS_STRICT_MODE) != 0;
- }
-
- /**
- * Set the lowered state
- */
- public void setIsLowered() {
- flags |= IS_LOWERED;
- }
-
- /**
- * Get the lowered state
- *
- * @return true if function is lowered
- */
- public boolean isLowered() {
- return (flags & IS_LOWERED) != 0;
+ public boolean isStrict() {
+ return getFlag(IS_STRICT);
}
/**
@@ -1173,25 +697,47 @@ public class FunctionNode extends Block {
/**
* Reset the compile unit used to compile this function
* @see Compiler
+ * @param lc lexical context
* @param compileUnit the compile unit
+ * @return function node or a new one if state was changed
*/
- public void setCompileUnit(final CompileUnit compileUnit) {
- this.compileUnit = compileUnit;
+ public FunctionNode setCompileUnit(final LexicalContext lc, final CompileUnit compileUnit) {
+ if (this.compileUnit == compileUnit) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body));
}
/**
- * Return the method emitter used to write bytecode for this function
- * @return the method emitter
+ * Create a temporary variable to the current frame.
+ *
+ * @param block that needs the temporary
+ * @param type Strong type of symbol.
+ * @param node Primary node to use symbol.
+ *
+ * @return Symbol used.
*/
- public MethodEmitter getMethodEmitter() {
- return method;
+ public Symbol ensureSymbol(final Block block, final Type type, final Node node) {
+ Symbol symbol = node.getSymbol();
+
+ // If no symbol already present.
+ if (symbol == null) {
+ final String uname = uniqueName(TEMP_PREFIX.symbolName());
+ symbol = new Symbol(uname, IS_TEMP, type);
+ block.putSymbol(uname, symbol);
+ node.setSymbol(symbol);
+ }
+
+ return symbol;
}
/**
- * Set the method emitter that is to be used to write bytecode for this function
- * @param method a method emitter
+ * Get the symbol for a compiler constant, or null if not available (yet)
+ * @param cc compiler constant
+ * @return symbol for compiler constant, or null if not defined yet (for example in Lower)
*/
- public void setMethodEmitter(final MethodEmitter method) {
- this.method = method;
+ public Symbol compilerConstant(final CompilerConstants cc) {
+ return body.getExistingSymbol(cc.symbolName());
}
+
}
diff --git a/src/jdk/nashorn/internal/ir/IdentNode.java b/src/jdk/nashorn/internal/ir/IdentNode.java
index 889a8700..daf79ee3 100644
--- a/src/jdk/nashorn/internal/ir/IdentNode.java
+++ b/src/jdk/nashorn/internal/ir/IdentNode.java
@@ -32,13 +32,15 @@ import static jdk.nashorn.internal.codegen.ObjectClassGenerator.DEBUG_FIELDS;
import jdk.nashorn.internal.codegen.ObjectClassGenerator;
import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
/**
* IR representation for an identifier.
*/
-public class IdentNode extends Node implements PropertyKey, TypeOverride<IdentNode>, FunctionCall {
+@Immutable
+public final class IdentNode extends Node implements PropertyKey, TypeOverride<IdentNode>, FunctionCall {
private static final int PROPERTY_NAME = 1 << 0;
private static final int INITIALIZED_HERE = 1 << 1;
private static final int FUNCTION = 1 << 2;
@@ -47,9 +49,9 @@ public class IdentNode extends Node implements PropertyKey, TypeOverride<IdentNo
private final String name;
/** Type for a callsite, e.g. X in a get()X or a set(X)V */
- private Type callSiteType;
+ private final Type callSiteType;
- private byte flags;
+ private final int flags;
/**
* Constructor
@@ -62,6 +64,15 @@ public class IdentNode extends Node implements PropertyKey, TypeOverride<IdentNo
public IdentNode(final Source source, final long token, final int finish, final String name) {
super(source, token, finish);
this.name = name;
+ this.callSiteType = null;
+ this.flags = 0;
+ }
+
+ private IdentNode(final IdentNode identNode, final String name, final Type callSiteType, final int flags) {
+ super(identNode);
+ this.name = name;
+ this.callSiteType = callSiteType;
+ this.flags = flags;
}
/**
@@ -71,8 +82,9 @@ public class IdentNode extends Node implements PropertyKey, TypeOverride<IdentNo
*/
public IdentNode(final IdentNode identNode) {
super(identNode);
- this.name = identNode.getName();
- this.flags = identNode.flags;
+ this.name = identNode.getName();
+ this.callSiteType = null;
+ this.flags = identNode.flags;
}
@Override
@@ -92,40 +104,15 @@ public class IdentNode extends Node implements PropertyKey, TypeOverride<IdentNo
@Override
public IdentNode setType(final Type type) {
- if (DEBUG_FIELDS && getSymbol() != null && !Type.areEquivalent(getSymbol().getSymbolType(), type)) {
- ObjectClassGenerator.LOG.info(getClass().getName() + " " + this + " => " + type + " instead of " + getType());
- }
// do NOT, repeat NOT touch the symbol here. it might be a local variable or whatever. This is the override if it isn't
- if(this.callSiteType == type) {
+ if (this.callSiteType == type) {
return this;
}
- final IdentNode n = (IdentNode)clone();
- n.callSiteType = type;
- return n;
- }
-
- @Override
- protected Node copy(final CopyState cs) {
- return new IdentNode(this);
- }
-
- /**
- * Test to see if two IdentNode are the same.
- *
- * @param other Other ident.
- * @return true if the idents are the same.
- */
- @Override
- public boolean equals(final Object other) {
- if (other instanceof IdentNode) {
- return name.equals(((IdentNode)other).name);
+ if (DEBUG_FIELDS && getSymbol() != null && !Type.areEquivalent(getSymbol().getSymbolType(), type)) {
+ ObjectClassGenerator.LOG.info(getClass().getName(), " ", this, " => ", type, " instead of ", getType());
}
- return false;
- }
- @Override
- public int hashCode() {
- return name.hashCode();
+ return new IdentNode(this, name, type, flags);
}
/**
@@ -135,7 +122,7 @@ public class IdentNode extends Node implements PropertyKey, TypeOverride<IdentNo
*/
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterIdentNode(this) != null) {
+ if (visitor.enterIdentNode(this)) {
return visitor.leaveIdentNode(this);
}
@@ -147,7 +134,7 @@ public class IdentNode extends Node implements PropertyKey, TypeOverride<IdentNo
if (hasCallSiteType()) {
sb.append('{');
final String desc = getType().getDescriptor();
- sb.append(desc.charAt(desc.length() - 1) == ';' ? "O" : getType().getDescriptor());
+ sb.append(desc.charAt(desc.length() - 1) == ';' ? 'O' : getType().getDescriptor());
sb.append('}');
}
@@ -191,10 +178,10 @@ public class IdentNode extends Node implements PropertyKey, TypeOverride<IdentNo
* @return a node equivalent to this one except for the requested change.
*/
public IdentNode setIsPropertyName() {
- if(isPropertyName()) return this;
- final IdentNode n = (IdentNode)clone();
- n.flags |= PROPERTY_NAME;
- return n;
+ if (isPropertyName()) {
+ return this;
+ }
+ return new IdentNode(this, name, callSiteType, flags | PROPERTY_NAME);
}
/**
@@ -210,10 +197,10 @@ public class IdentNode extends Node implements PropertyKey, TypeOverride<IdentNo
* @return a node equivalent to this one except for the requested change.
*/
public IdentNode setIsInitializedHere() {
- if(isInitializedHere()) return this;
- final IdentNode n = (IdentNode)clone();
- n.flags |= INITIALIZED_HERE;
- return n;
+ if (isInitializedHere()) {
+ return this;
+ }
+ return new IdentNode(this, name, callSiteType, flags | INITIALIZED_HERE);
}
/**
@@ -223,7 +210,7 @@ public class IdentNode extends Node implements PropertyKey, TypeOverride<IdentNo
* @return true if this IdentNode is special
*/
public boolean isSpecialIdentity() {
- return name.equals(__DIR__.tag()) || name.equals(__FILE__.tag()) || name.equals(__LINE__.tag());
+ return name.equals(__DIR__.symbolName()) || name.equals(__FILE__.symbolName()) || name.equals(__LINE__.symbolName());
}
@Override
@@ -236,9 +223,9 @@ public class IdentNode extends Node implements PropertyKey, TypeOverride<IdentNo
* @return an ident node identical to this one in all aspects except with its function flag set.
*/
public IdentNode setIsFunction() {
- if(isFunction()) return this;
- final IdentNode n = (IdentNode)clone();
- n.flags |= FUNCTION;
- return n;
+ if (isFunction()) {
+ return this;
+ }
+ return new IdentNode(this, name, callSiteType, flags | FUNCTION);
}
}
diff --git a/src/jdk/nashorn/internal/ir/IfNode.java b/src/jdk/nashorn/internal/ir/IfNode.java
index 3ddcf1dc..027e1a84 100644
--- a/src/jdk/nashorn/internal/ir/IfNode.java
+++ b/src/jdk/nashorn/internal/ir/IfNode.java
@@ -25,22 +25,23 @@
package jdk.nashorn.internal.ir;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
/**
* IR representation for an IF statement.
- *
*/
-public class IfNode extends Node {
+@Immutable
+public final class IfNode extends Node {
/** Test expression. */
- private Node test;
+ private final Node test;
/** Pass statements. */
- private Block pass;
+ private final Block pass;
/** Fail statements. */
- private Block fail;
+ private final Block fail;
/**
* Constructor
@@ -54,37 +55,30 @@ public class IfNode extends Node {
*/
public IfNode(final Source source, final long token, final int finish, final Node test, final Block pass, final Block fail) {
super(source, token, finish);
-
this.test = test;
this.pass = pass;
this.fail = fail;
}
- private IfNode(final IfNode ifNode, final CopyState cs) {
+ private IfNode(final IfNode ifNode, final Node test, final Block pass, final Block fail) {
super(ifNode);
-
- this.test = cs.existingOrCopy(ifNode.test);
- this.pass = (Block)cs.existingOrCopy(ifNode.pass);
- this.fail = (Block)cs.existingOrCopy(ifNode.fail);
+ this.test = test;
+ this.pass = pass;
+ this.fail = fail;
}
@Override
- protected Node copy(final CopyState cs) {
- return new IfNode(this, cs);
+ public boolean isTerminal() {
+ return pass.isTerminal() && fail != null && fail.isTerminal();
}
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterIfNode(this) != null) {
- test = test.accept(visitor);
-
- pass = (Block)pass.accept(visitor);
-
- if (fail != null) {
- fail = (Block)fail.accept(visitor);
- }
-
- return visitor.leaveIfNode(this);
+ if (visitor.enterIfNode(this)) {
+ return visitor.leaveIfNode(
+ setTest(test.accept(visitor)).
+ setPass((Block)pass.accept(visitor)).
+ setFail(fail == null ? null : (Block)fail.accept(visitor)));
}
return this;
@@ -105,6 +99,13 @@ public class IfNode extends Node {
return fail;
}
+ private IfNode setFail(final Block fail) {
+ if (this.fail == fail) {
+ return this;
+ }
+ return new IfNode(this, test, pass, fail);
+ }
+
/**
* Get the then block for this IfNode
* @return the then block
@@ -113,6 +114,13 @@ public class IfNode extends Node {
return pass;
}
+ private IfNode setPass(final Block pass) {
+ if (this.pass == pass) {
+ return this;
+ }
+ return new IfNode(this, test, pass, fail);
+ }
+
/**
* Get the test expression for this IfNode
* @return the test expression
@@ -124,8 +132,12 @@ public class IfNode extends Node {
/**
* Reset the test expression for this IfNode
* @param test a new test expression
+ * @return new or same IfNode
*/
- public void setTest(final Node test) {
- this.test = test;
+ public IfNode setTest(final Node test) {
+ if (this.test == test) {
+ return this;
+ }
+ return new IfNode(this, test, pass, fail);
}
}
diff --git a/src/jdk/nashorn/internal/ir/IndexNode.java b/src/jdk/nashorn/internal/ir/IndexNode.java
index 4745bf64..764ee38c 100644
--- a/src/jdk/nashorn/internal/ir/IndexNode.java
+++ b/src/jdk/nashorn/internal/ir/IndexNode.java
@@ -25,22 +25,18 @@
package jdk.nashorn.internal.ir;
-import static jdk.nashorn.internal.codegen.ObjectClassGenerator.DEBUG_FIELDS;
-
-import jdk.nashorn.internal.codegen.ObjectClassGenerator;
import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
/**
* IR representation of an indexed access (brackets operator.)
- *
*/
-public class IndexNode extends BaseNode implements TypeOverride<IndexNode> {
- /** Property ident. */
- private Node index;
-
- private boolean hasCallSiteType;
+@Immutable
+public final class IndexNode extends BaseNode {
+ /** Property index. */
+ private final Node index;
/**
* Constructors
@@ -52,50 +48,27 @@ public class IndexNode extends BaseNode implements TypeOverride<IndexNode> {
* @param index index for access
*/
public IndexNode(final Source source, final long token, final int finish, final Node base, final Node index) {
- super(source, token, finish, base);
-
+ super(source, token, finish, base, false, false);
this.index = index;
}
- /**
- * Copy constructor
- *
- * @param indexNode source node
- */
- public IndexNode(final IndexNode indexNode) {
- this(indexNode, new CopyState());
- }
-
- private IndexNode(final IndexNode indexNode, final CopyState cs) {
- super(indexNode, cs);
-
- index = cs.existingOrCopy(indexNode.index);
- }
-
- @Override
- protected Node copy(final CopyState cs) {
- return new IndexNode(this, cs);
- }
-
- @Override
- public boolean equals(final Object other) {
- if (!super.equals(other)) {
- return false;
- }
- return index.equals(((IndexNode)other).getIndex());
- }
-
- @Override
- public int hashCode() {
- return super.hashCode() ^ getIndex().hashCode();
+ private IndexNode(final IndexNode indexNode, final Node base, final Node index, final boolean isFunction, final boolean hasCallSiteType) {
+ super(indexNode, base, isFunction, hasCallSiteType);
+ this.index = index;
}
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterIndexNode(this) != null) {
- base = base.accept(visitor);
- index = index.accept(visitor);
- return visitor.leaveIndexNode(this);
+ if (visitor.enterIndexNode(this)) {
+ final Node newBase = base.accept(visitor);
+ final Node newIndex = index.accept(visitor);
+ final IndexNode newNode;
+ if (newBase != base || newIndex != index) {
+ newNode = new IndexNode(this, newBase, newIndex, isFunction(), hasCallSiteType());
+ } else {
+ newNode = this;
+ }
+ return visitor.leaveIndexNode(newNode);
}
return this;
@@ -105,7 +78,7 @@ public class IndexNode extends BaseNode implements TypeOverride<IndexNode> {
public void toString(final StringBuilder sb) {
final boolean needsParen = tokenType().needsParens(base.tokenType(), true);
- if (hasCallSiteType) {
+ if (hasCallSiteType()) {
sb.append('{');
final String desc = getType().getDescriptor();
sb.append(desc.charAt(desc.length() - 1) == ';' ? "O" : getType().getDescriptor());
@@ -135,27 +108,19 @@ public class IndexNode extends BaseNode implements TypeOverride<IndexNode> {
return index;
}
- /**
- * Reset the index expression for this IndexNode
- * @param index a new index expression
- */
- public void setIndex(final Node index) {
- this.index = index;
- }
-
@Override
- public IndexNode setType(final Type type) {
- if (DEBUG_FIELDS && !Type.areEquivalent(getSymbol().getSymbolType(), type)) {
- ObjectClassGenerator.LOG.info(getClass().getName() + " " + this + " => " + type + " instead of " + getType());
+ public BaseNode setIsFunction() {
+ if (isFunction()) {
+ return this;
}
- hasCallSiteType = true;
- getSymbol().setTypeOverride(type);
- return this;
+ return new IndexNode(this, base, index, true, hasCallSiteType());
}
@Override
- public boolean canHaveCallSiteType() {
- return true; //carried by the symbol and always the same nodetype==symboltype
+ public IndexNode setType(final Type type) {
+ logTypeChange(type);
+ getSymbol().setTypeOverride(type); //always a temp so this is fine.
+ return new IndexNode(this, base, index, isFunction(), true);
}
}
diff --git a/src/jdk/nashorn/internal/ir/LabelNode.java b/src/jdk/nashorn/internal/ir/LabelNode.java
index 756ea2df..bf932db8 100644
--- a/src/jdk/nashorn/internal/ir/LabelNode.java
+++ b/src/jdk/nashorn/internal/ir/LabelNode.java
@@ -25,29 +25,20 @@
package jdk.nashorn.internal.ir;
-import jdk.nashorn.internal.ir.annotations.Ignore;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
/**
* IR representation for a labeled statement.
- *
*/
-
-public class LabelNode extends Node {
+@Immutable
+public final class LabelNode extends LexicalContextNode {
/** Label ident. */
- private IdentNode label;
+ private final IdentNode label;
/** Statements. */
- private Block body;
-
- /** Node to break from. */
- @Ignore
- private Node breakNode;
-
- /** Node to continue. */
- @Ignore
- private Node continueNode;
+ private final Block body;
/**
* Constructor
@@ -65,26 +56,23 @@ public class LabelNode extends Node {
this.body = body;
}
- private LabelNode(final LabelNode labelNode, final CopyState cs) {
+ private LabelNode(final LabelNode labelNode, final IdentNode label, final Block body) {
super(labelNode);
-
- this.label = (IdentNode)cs.existingOrCopy(labelNode.label);
- this.body = (Block)cs.existingOrCopy(labelNode.body);
- this.breakNode = cs.existingOrSame(labelNode.breakNode);
- this.continueNode = cs.existingOrSame(labelNode.continueNode);
+ this.label = label;
+ this.body = body;
}
@Override
- protected Node copy(final CopyState cs) {
- return new LabelNode(this, cs);
+ public boolean isTerminal() {
+ return body.isTerminal();
}
@Override
- public Node accept(final NodeVisitor visitor) {
- if (visitor.enterLabelNode(this) != null) {
- label = (IdentNode)label.accept(visitor);
- body = (Block)body.accept(visitor);
- return visitor.leaveLabelNode(this);
+ public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
+ if (visitor.enterLabelNode(this)) {
+ return visitor.leaveLabelNode(
+ setLabel(visitor.getLexicalContext(), (IdentNode)label.accept(visitor)).
+ setBody(visitor.getLexicalContext(), (Block)body.accept(visitor)));
}
return this;
@@ -106,44 +94,15 @@ public class LabelNode extends Node {
/**
* Reset the body of the node
+ * @param lc lexical context
* @param body new body
+ * @return new for node if changed or existing if not
*/
- public void setBody(final Block body) {
- this.body = body;
- }
-
- /**
- * Get the break node for this node
- * @return the break node
- */
- public Node getBreakNode() {
- return breakNode;
- }
-
- /**
- * Reset the break node for this node
- * @param breakNode the break node
- */
- public void setBreakNode(final Node breakNode) {
- assert breakNode instanceof BreakableNode || breakNode instanceof Block : "Invalid break node: " + breakNode;
- this.breakNode = breakNode;
- }
-
- /**
- * Get the continue node for this node
- * @return the continue node
- */
- public Node getContinueNode() {
- return continueNode;
- }
-
- /**
- * Reset the continue node for this node
- * @param continueNode the continue node
- */
- public void setContinueNode(final Node continueNode) {
- assert continueNode instanceof WhileNode : "invalid continue node: " + continueNode;
- this.continueNode = continueNode;
+ public LabelNode setBody(final LexicalContext lc, final Block body) {
+ if (this.body == body) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new LabelNode(this, label, body));
}
/**
@@ -154,4 +113,11 @@ public class LabelNode extends Node {
return label;
}
+ private LabelNode setLabel(final LexicalContext lc, final IdentNode label) {
+ if (this.label == label) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new LabelNode(this, label, body));
+ }
+
}
diff --git a/src/jdk/nashorn/internal/ir/LabeledNode.java b/src/jdk/nashorn/internal/ir/LabeledNode.java
deleted file mode 100644
index a3783e92..00000000
--- a/src/jdk/nashorn/internal/ir/LabeledNode.java
+++ /dev/null
@@ -1,123 +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.ir;
-
-import jdk.nashorn.internal.ir.annotations.Ignore;
-import jdk.nashorn.internal.runtime.Source;
-
-/**
- * IR base class for break and continue.
- *
- */
-public abstract class LabeledNode extends Node {
- /** Optional label. */
- @Ignore
- protected final LabelNode labelNode;
-
- /** Target control node. */
- @Ignore
- protected final Node targetNode;
-
- /** Try chain. */
- @Ignore
- protected final TryNode tryChain;
-
- /** scope nesting level */
- protected int scopeNestingLevel;
-
- /**
- * Constructor
- *
- * @param source the source
- * @param token token
- * @param finish finish
- * @param labelNode the label node
- * @param targetNode the place to break to
- * @param tryChain the try chain
- */
- public LabeledNode(final Source source, final long token, final int finish, final LabelNode labelNode, final Node targetNode, final TryNode tryChain) {
- super(source, token, finish);
-
- this.labelNode = labelNode;
- this.targetNode = targetNode;
- this.tryChain = tryChain;
- }
-
- /**
- * Copy constructor
- *
- * @param labeledNode source node
- * @param cs copy state
- */
- protected LabeledNode(final LabeledNode labeledNode, final CopyState cs) {
- super(labeledNode);
-
- this.labelNode = (LabelNode)cs.existingOrCopy(labeledNode.labelNode);
- this.targetNode = cs.existingOrSame(labeledNode.targetNode);
- this.tryChain = (TryNode)cs.existingOrSame(labeledNode.tryChain);
- this.scopeNestingLevel = labeledNode.scopeNestingLevel;
- }
-
- /**
- * Get the label
- * @return the label
- */
- public LabelNode getLabel() {
- return labelNode;
- }
-
- /**
- * Get the target node
- * @return the target node
- */
- public Node getTargetNode() {
- return targetNode;
- }
-
- /**
- * Get the surrounding try chain
- * @return the try chain
- */
- public TryNode getTryChain() {
- return tryChain;
- }
-
- /**
- * Get the scope nesting level
- * @return nesting level
- */
- public int getScopeNestingLevel() {
- return scopeNestingLevel;
- }
-
- /**
- * Set scope nesting level
- * @param level the new level
- */
- public void setScopeNestingLevel(final int level) {
- scopeNestingLevel = level;
- }
-}
diff --git a/src/jdk/nashorn/internal/ir/LexicalContext.java b/src/jdk/nashorn/internal/ir/LexicalContext.java
index 2db1f796..c3f096f5 100644
--- a/src/jdk/nashorn/internal/ir/LexicalContext.java
+++ b/src/jdk/nashorn/internal/ir/LexicalContext.java
@@ -1,96 +1,250 @@
+/*
+ * 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.ir;
-import java.util.ArrayDeque;
-import java.util.Deque;
+import java.io.File;
import java.util.Iterator;
import java.util.NoSuchElementException;
+import jdk.nashorn.internal.codegen.Label;
+import jdk.nashorn.internal.runtime.Debug;
+import jdk.nashorn.internal.runtime.Source;
/**
* A class that tracks the current lexical context of node visitation as a stack of {@link Block} nodes. Has special
* methods to retrieve useful subsets of the context.
+ *
+ * This is implemented with a primitive array and a stack pointer, because it really makes a difference
+ * performance wise. None of the collection classes were optimal
*/
-public class LexicalContext implements Cloneable {
- private final Deque<Block> lexicalContext;
+public class LexicalContext {
+ private LexicalContextNode[] stack;
+
+ private int[] flags;
+ private int sp;
/**
* Creates a new empty lexical context.
*/
public LexicalContext() {
- lexicalContext = new ArrayDeque<>();
+ stack = new LexicalContextNode[16];
+ flags = new int[16];
}
/**
- * Pushes a new block on top of the context, making it the innermost open block.
- * @param block the new block
+ * Set the flags for a lexical context node on the stack. Does not
+ * replace the flags, but rather adds to them
+ *
+ * @param node node
+ * @param flag new flag to set
*/
- public void push(Block block) {
- //new Exception(block.toString()).printStackTrace();
- lexicalContext.push(block);
+ public void setFlag(final LexicalContextNode node, final int flag) {
+ if (flag != 0) {
+ for (int i = sp - 1; i >= 0; i--) {
+ if (stack[i] == node) {
+ flags[i] |= flag;
+ //System.err.println("Setting flag " + node + " " + flag);
+ return;
+ }
+ }
+ }
+ assert false;
}
/**
- * Pops the innermost block off the context.
- * @param the block expected to be popped, used to detect unbalanced pushes/pops
+ * Get the flags for a lexical context node on the stack
+ * @param node node
+ * @return the flags for the node
*/
- public void pop(Block block) {
- final Block popped = lexicalContext.pop();
- assert popped == block;
+ public int getFlags(final LexicalContextNode node) {
+ for (int i = sp - 1; i >= 0; i--) {
+ if (stack[i] == node) {
+ return flags[i];
+ }
+ }
+ throw new AssertionError("flag node not on context stack");
}
/**
- * Returns an iterator over all blocks in the context, with the top block (innermost lexical context) first.
- * @return an iterator over all blocks in the context.
+ * Get the function body of a function node on the lexical context
+ * stack. This will trigger an assertion if node isn't present
+ * @param functionNode function node
+ * @return body of function node
*/
- public Iterator<Block> getBlocks() {
- return lexicalContext.iterator();
+ public Block getFunctionBody(final FunctionNode functionNode) {
+ for (int i = sp - 1; i >= 0 ; i--) {
+ if (stack[i] == functionNode) {
+ return (Block)stack[i + 1];
+ }
+ }
+ throw new AssertionError(functionNode.getName() + " not on context stack");
}
/**
- * Returns an iterator over all functions in the context, with the top (innermost open) function first.
- * @return an iterator over all functions in the context.
+ * Return all nodes in the LexicalContext
+ * @return all nodes
*/
- public Iterator<FunctionNode> getFunctions() {
- return new FunctionIterator(getBlocks());
+ public Iterator<LexicalContextNode> getAllNodes() {
+ return new NodeIterator<>(LexicalContextNode.class);
+ }
+
+ /**
+ * Returns the outermost function in this context. It is either the program, or a lazily compiled function.
+ * @return the outermost function in this context.
+ */
+ public FunctionNode getOutermostFunction() {
+ return (FunctionNode)stack[0];
}
- private static final class FunctionIterator implements Iterator<FunctionNode> {
- private final Iterator<Block> it;
- private FunctionNode next;
- FunctionIterator(Iterator<Block> it) {
- this.it = it;
- next = findNext();
+
+ /**
+ * Pushes a new block on top of the context, making it the innermost open block.
+ * @param node the new node
+ * @return the node that was pushed
+ */
+ public <T extends LexicalContextNode> T push(final T node) {
+ if (sp == stack.length) {
+ final LexicalContextNode[] newStack = new LexicalContextNode[sp * 2];
+ System.arraycopy(stack, 0, newStack, 0, sp);
+ stack = newStack;
+
+ final int[] newFlags = new int[sp * 2];
+ System.arraycopy(flags, 0, newFlags, 0, sp);
+ flags = newFlags;
+
}
+ stack[sp] = node;
+ flags[sp] = 0;
- @Override
- public boolean hasNext() {
- return next != null;
+ sp++;
+
+ return node;
+ }
+
+ /**
+ * Is the context empty?
+ * @return true if empty
+ */
+ public boolean isEmpty() {
+ return sp == 0;
+ }
+
+ /**
+ * The depth of the lexical context
+ * @return depth
+ */
+ public int size() {
+ return sp;
+ }
+
+ /**
+ * Pops the innermost block off the context and all nodes that has been contributed
+ * since it was put there
+ *
+ * @param node the node expected to be popped, used to detect unbalanced pushes/pops
+ * @return the node that was popped
+ */
+ @SuppressWarnings("unchecked")
+ public <T extends LexicalContextNode> T pop(final T node) {
+ --sp;
+ final LexicalContextNode popped = stack[sp];
+ stack[sp] = null;
+ if (popped instanceof Flags) {
+ return (T)((Flags<?>)popped).setFlag(this, flags[sp]);
}
- @Override
- public FunctionNode next() {
- if(next == null) {
- throw new NoSuchElementException();
+ return (T)popped;
+ }
+
+
+ /**
+ * Return the top element in the context
+ * @return the node that was pushed last
+ */
+ public LexicalContextNode peek() {
+ return stack[sp - 1];
+ }
+
+ /**
+ * Check if a node is in the lexical context
+ * @param node node to check for
+ * @return true if in the context
+ */
+ public boolean contains(final LexicalContextNode node) {
+ for (int i = 0; i < sp; i++) {
+ if (stack[i] == node) {
+ return true;
}
- FunctionNode lnext = next;
- next = findNext();
- return lnext;
}
+ return false;
+ }
- private FunctionNode findNext() {
- while(it.hasNext()) {
- final Block block = it.next();
- if(block instanceof FunctionNode) {
- return ((FunctionNode)block);
- }
+ /**
+ * Replace a node on the lexical context with a new one. Normally
+ * you should try to engineer IR traversals so this isn't needed
+ *
+ * @param oldNode old node
+ * @param newNode new node
+ * @return the new node
+ */
+ public LexicalContextNode replace(final LexicalContextNode oldNode, final LexicalContextNode newNode) {
+ //System.err.println("REPLACE old=" + Debug.id(oldNode) + " new=" + Debug.id(newNode));
+ for (int i = sp - 1; i >= 0; i--) {
+ if (stack[i] == oldNode) {
+ assert i == (sp - 1) : "violation of contract - we always expect to find the replacement node on top of the lexical context stack: " + newNode + " has " + stack[i + 1].getClass() + " above it";
+ stack[i] = newNode;
+ break;
}
- return null;
- }
+ }
+ return newNode;
+ }
- @Override
- public void remove() {
- throw new UnsupportedOperationException();
- }
+ /**
+ * Returns an iterator over all blocks in the context, with the top block (innermost lexical context) first.
+ * @return an iterator over all blocks in the context.
+ */
+ public Iterator<Block> getBlocks() {
+ return new NodeIterator<>(Block.class);
+ }
+
+ /**
+ * Returns an iterator over all functions in the context, with the top (innermost open) function first.
+ * @return an iterator over all functions in the context.
+ */
+ public Iterator<FunctionNode> getFunctions() {
+ return new NodeIterator<>(FunctionNode.class);
+ }
+
+ /**
+ * Get the parent block for the current lexical context block
+ * @return parent block
+ */
+ public Block getParentBlock() {
+ final Iterator<Block> iter = new NodeIterator<>(Block.class, getCurrentFunction());
+ iter.next();
+ return iter.hasNext() ? iter.next() : null;
}
/**
@@ -98,12 +252,12 @@ public class LexicalContext implements Cloneable {
* @param block the block whose ancestors are returned
* @return an iterator over all ancestors block of the given block.
*/
- public Iterator<Block> getAncestorBlocks(Block block) {
- final Iterator<Block> it = getBlocks();
- while(it.hasNext()) {
- final Block b = it.next();
- if(block == b) {
- return it;
+ public Iterator<Block> getAncestorBlocks(final Block block) {
+ final Iterator<Block> iter = getBlocks();
+ while (iter.hasNext()) {
+ final Block b = iter.next();
+ if (block == b) {
+ return iter;
}
}
throw new AssertionError("Block is not on the current lexical context stack");
@@ -115,17 +269,17 @@ public class LexicalContext implements Cloneable {
* @return an iterator over a block and all its ancestors.
*/
public Iterator<Block> getBlocks(final Block block) {
- final Iterator<Block> it = getAncestorBlocks(block);
+ final Iterator<Block> iter = getAncestorBlocks(block);
return new Iterator<Block>() {
boolean blockReturned = false;
@Override
public boolean hasNext() {
- return it.hasNext() || !blockReturned;
+ return iter.hasNext() || !blockReturned;
}
@Override
public Block next() {
- if(blockReturned) {
- return it.next();
+ if (blockReturned) {
+ return iter.next();
}
blockReturned = true;
return block;
@@ -138,61 +292,319 @@ public class LexicalContext implements Cloneable {
}
/**
- * Returns the closest function node to the block. If the block is itself a function, it is returned.
- * @param block the block
- * @return the function closest to the block.
- * @see #getParentFunction(Block)
+ * Get the function for this block. If the block is itself a function
+ * this returns identity
+ * @param block block for which to get function
+ * @return function for block
*/
- public FunctionNode getFunction(Block block) {
- if(block instanceof FunctionNode) {
- return (FunctionNode)block;
+ public FunctionNode getFunction(final Block block) {
+ final Iterator<LexicalContextNode> iter = new NodeIterator<>(LexicalContextNode.class);
+ while (iter.hasNext()) {
+ final LexicalContextNode next = iter.next();
+ if (next == block) {
+ while (iter.hasNext()) {
+ final LexicalContextNode next2 = iter.next();
+ if (next2 instanceof FunctionNode) {
+ return (FunctionNode)next2;
+ }
+ }
+ }
}
- return getParentFunction(block);
+ assert false;
+ return null;
}
/**
- * Returns the closest function node to the block and all its ancestor functions. If the block is itself a function,
- * it is returned too.
- * @param block the block
- * @return the closest function node to the block and all its ancestor functions.
+ * Returns the innermost block in the context.
+ * @return the innermost block in the context.
*/
- public Iterator<FunctionNode> getFunctions(final Block block) {
- return new FunctionIterator(getBlocks(block));
+ public Block getCurrentBlock() {
+ return getBlocks().next();
}
/**
- * Returns the containing function of the block. If the block is itself a function, its parent function is returned.
- * @param block the block
- * @return the containing function of the block.
- * @see #getFunction(Block)
+ * Returns the innermost function in the context.
+ * @return the innermost function in the context.
*/
- public FunctionNode getParentFunction(Block block) {
- return getFirstFunction(getAncestorBlocks(block));
+ public FunctionNode getCurrentFunction() {
+ if (isEmpty()) {
+ return null;
+ }
+ return new NodeIterator<>(FunctionNode.class).next();
}
- private static FunctionNode getFirstFunction(Iterator<Block> it) {
- while(it.hasNext()) {
- final Block ancestor = it.next();
- if(ancestor instanceof FunctionNode) {
- return (FunctionNode)ancestor;
+ /**
+ * Get the block in which a symbol is defined
+ * @param symbol symbol
+ * @return block in which the symbol is defined, assert if no such block in context
+ */
+ public Block getDefiningBlock(final Symbol symbol) {
+ if (symbol.isTemp()) {
+ return null;
+ }
+ final String name = symbol.getName();
+ for (final Iterator<Block> it = getBlocks(); it.hasNext();) {
+ final Block next = it.next();
+ if (next.getExistingSymbol(name) == symbol) {
+ return next;
}
}
+ throw new AssertionError("Couldn't find symbol " + name + " in the context");
+ }
+
+ /**
+ * Get the function in which a symbol is defined
+ * @param symbol symbol
+ * @return function node in which this symbol is defined, assert if no such symbol exists in context
+ */
+ public FunctionNode getDefiningFunction(Symbol symbol) {
+ if (symbol.isTemp()) {
+ return null;
+ }
+ final String name = symbol.getName();
+ for (final Iterator<LexicalContextNode> iter = new NodeIterator<>(LexicalContextNode.class); iter.hasNext();) {
+ final LexicalContextNode next = iter.next();
+ if (next instanceof Block && ((Block)next).getExistingSymbol(name) == symbol) {
+ while (iter.hasNext()) {
+ final LexicalContextNode next2 = iter.next();
+ if (next2 instanceof FunctionNode) {
+ return ((FunctionNode)next2);
+ }
+ }
+ throw new AssertionError("Defining block for symbol " + name + " has no function in the context");
+ }
+ }
+ throw new AssertionError("Couldn't find symbol " + name + " in the context");
+ }
+
+ /**
+ * Is the topmost lexical context element a function body?
+ * @return true if function body
+ */
+ public boolean isFunctionBody() {
+ return getParentBlock() == null;
+ }
+
+ /**
+ * Returns true if the expression defining the function is a callee of a CallNode that should be the second
+ * element on the stack, e.g. <code>(function(){})()</code>. That is, if the stack ends with
+ * {@code [..., CallNode, FunctionNode]} then {@code callNode.getFunction()} should be equal to
+ * {@code functionNode}, and the top of the stack should itself be a variant of {@code functionNode}.
+ * @param functionNode the function node being tested
+ * @return true if the expression defining the current function is a callee of a call expression.
+ */
+ public boolean isFunctionDefinedInCurrentCall(FunctionNode functionNode) {
+ final LexicalContextNode parent = stack[sp - 2];
+ if(parent instanceof CallNode && ((CallNode)parent).getFunction() == functionNode) {
+ assert functionNode.getSource() == peek().getSource();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * 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)
+ */
+ public FunctionNode getParentFunction(final FunctionNode functionNode) {
+ final Iterator<FunctionNode> iter = new NodeIterator<>(FunctionNode.class);
+ while (iter.hasNext()) {
+ final FunctionNode next = iter.next();
+ if (next == functionNode) {
+ return iter.hasNext() ? iter.next() : null;
+ }
+ }
+ assert false;
return null;
}
/**
- * Returns the innermost block in the context.
- * @return the innermost block in the context.
+ * Count the number of with scopes until a given node
+ * @param until node to stop counting at, or null if all nodes should be counted
+ * @return number of with scopes encountered in the context
*/
- public Block getCurrentBlock() {
- return lexicalContext.element();
+ public int getScopeNestingLevelTo(final LexicalContextNode until) {
+ //count the number of with nodes until "until" is hit
+ int n = 0;
+ for (final Iterator<WithNode> iter = new NodeIterator<>(WithNode.class, until); iter.hasNext(); iter.next()) {
+ n++;
+ }
+ return n;
+ }
+
+ private BreakableNode getBreakable() {
+ for (final NodeIterator<BreakableNode> iter = new NodeIterator<>(BreakableNode.class, getCurrentFunction()); iter.hasNext(); ) {
+ final BreakableNode next = iter.next();
+ if (next.isBreakableWithoutLabel()) {
+ return next;
+ }
+ }
+ return null;
}
/**
- * Returns the innermost function in the context.
- * @return the innermost function in the context.
+ * Find the breakable node corresponding to this label.
+ * @param label label to search for, if null the closest breakable node will be returned unconditionally, e.g. a while loop with no label
+ * @return closest breakable node
*/
- public FunctionNode getCurrentFunction() {
- return getFirstFunction(getBlocks());
+ public BreakableNode getBreakable(final IdentNode label) {
+ if (label != null) {
+ final LabelNode foundLabel = findLabel(label.getName());
+ if (foundLabel != null) {
+ // iterate to the nearest breakable to the foundLabel
+ BreakableNode breakable = null;
+ for (final NodeIterator<BreakableNode> iter = new NodeIterator<>(BreakableNode.class, foundLabel); iter.hasNext(); ) {
+ breakable = iter.next();
+ }
+ return breakable;
+ }
+ return null;
+ }
+ return getBreakable();
+ }
+
+ private LoopNode getContinueTo() {
+ final Iterator<LoopNode> iter = new NodeIterator<>(LoopNode.class, getCurrentFunction());
+ return iter.hasNext() ? iter.next() : null;
+ }
+
+ /**
+ * Find the continue target node corresponding to this label.
+ * @param label label to search for, if null the closest loop node will be returned unconditionally, e.g. a while loop with no label
+ * @return closest continue target node
+ */
+ public LoopNode getContinueTo(final IdentNode label) {
+ if (label != null) {
+ final LabelNode foundLabel = findLabel(label.getName());
+ if (foundLabel != null) {
+ // iterate to the nearest loop to the foundLabel
+ LoopNode loop = null;
+ for (final NodeIterator<LoopNode> iter = new NodeIterator<>(LoopNode.class, foundLabel); iter.hasNext(); ) {
+ loop = iter.next();
+ }
+ return loop;
+ }
+ return null;
+ }
+ return getContinueTo();
+ }
+
+ /**
+ * Check the lexical context for a given label node by name
+ * @param name name of the label
+ * @return LabelNode if found, null otherwise
+ */
+ public LabelNode findLabel(final String name) {
+ for (final Iterator<LabelNode> iter = new NodeIterator<>(LabelNode.class, getCurrentFunction()); iter.hasNext(); ) {
+ final LabelNode next = iter.next();
+ if (next.getLabel().getName().equals(name)) {
+ return next;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Checks whether a given label is a jump destination that lies outside a given
+ * split node
+ * @param splitNode the split node
+ * @param label the label
+ * @return true if label resides outside the split node
+ */
+ public boolean isExternalTarget(final SplitNode splitNode, final Label label) {
+ boolean targetFound = false;
+ for (int i = sp - 1; i >= 0; i--) {
+ final LexicalContextNode next = stack[i];
+ if (next == splitNode) {
+ return !targetFound;
+ }
+
+ if (next instanceof BreakableNode) {
+ for (final Label l : ((BreakableNode)next).getLabels()) {
+ if (l == label) {
+ targetFound = true;
+ break;
+ }
+ }
+ }
+ }
+ assert false : label + " was expected in lexical context " + LexicalContext.this + " but wasn't";
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuffer sb = new StringBuffer();
+ sb.append("[ ");
+ for (int i = 0; i < sp; i++) {
+ final Node node = stack[i];
+ sb.append(node.getClass().getSimpleName());
+ sb.append('@');
+ sb.append(Debug.id(node));
+ sb.append(':');
+ final Source source = node.getSource();
+ String src = source.toString();
+ if (src.indexOf(File.pathSeparator) != -1) {
+ src = src.substring(src.lastIndexOf(File.pathSeparator));
+ }
+ src += ' ';
+ src += source.getLine(node.getStart());
+ sb.append(' ');
+ }
+ sb.append(" ==> ]");
+ return sb.toString();
+ }
+
+ private class NodeIterator <T extends LexicalContextNode> implements Iterator<T> {
+ private int index;
+ private T next;
+ private final Class<T> clazz;
+ private LexicalContextNode until;
+
+ NodeIterator(final Class<T> clazz) {
+ this(clazz, null);
+ }
+
+ NodeIterator(final Class<T> clazz, final LexicalContextNode until) {
+ this.index = sp - 1;
+ this.clazz = clazz;
+ this.until = until;
+ this.next = findNext();
+ }
+
+ @Override
+ public boolean hasNext() {
+ return next != null;
+ }
+
+ @Override
+ public T next() {
+ if (next == null) {
+ throw new NoSuchElementException();
+ }
+ T lnext = next;
+ next = findNext();
+ return lnext;
+ }
+
+ private T findNext() {
+ for (int i = index; i >= 0; i--) {
+ final Node node = stack[i];
+ if (node == until) {
+ return null;
+ }
+ if (clazz.isAssignableFrom(node.getClass())) {
+ index = i - 1;
+ return clazz.cast(node);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
}
}
diff --git a/src/jdk/nashorn/internal/ir/DoWhileNode.java b/src/jdk/nashorn/internal/ir/LexicalContextNode.java
index 3939795e..e48c6e0b 100644
--- a/src/jdk/nashorn/internal/ir/DoWhileNode.java
+++ b/src/jdk/nashorn/internal/ir/LexicalContextNode.java
@@ -22,61 +22,52 @@
* 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;
import jdk.nashorn.internal.runtime.Source;
/**
- * Loop representing do while loops. This is mostly split from WhileNode
- * because of the different order of the Phi Traversals
- *
+ * Superclass for nodes that can be part of the lexical context
+ * @see LexicalContext
*/
-public class DoWhileNode extends WhileNode {
-
+public abstract class LexicalContextNode extends Node {
/**
* Constructor
*
- * @param source the source
- * @param token token
- * @param finish finish
+ * @param source source
+ * @param token token
+ * @param finish finish
*/
- public DoWhileNode(final Source source, final long token, final int finish) {
+ protected LexicalContextNode(final Source source, final long token, final int finish) {
super(source, token, finish);
}
/**
* Copy constructor
*
- * @param doWhileNode source node
- * @param cs copy state
+ * @param node source node
*/
- protected DoWhileNode(final DoWhileNode doWhileNode, final CopyState cs) {
- super(doWhileNode, cs);
+ protected LexicalContextNode(final LexicalContextNode node) {
+ super(node);
}
- @Override
- protected Node copy(final CopyState cs) {
- return new DoWhileNode(this, cs);
- }
+ /**
+ * Accept function for the node given a lexical context. It must be prepared
+ * to replace itself if present in the lexical context
+ *
+ * @param lc lexical context
+ * @param visitor node visitor
+ *
+ * @return new node or same node depending on state change
+ */
+ protected abstract Node accept(final LexicalContext lc, final NodeVisitor visitor);
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterDoWhileNode(this) != null) {
- body = (Block)body.accept(visitor);
- test = test.accept(visitor);
-
- return visitor.leaveDoWhileNode(this);
- }
-
- return this;
- }
-
- @Override
- public void toString(final StringBuilder sb) {
- sb.append("while (");
- test.toString(sb);
- sb.append(')');
+ final LexicalContext lc = visitor.getLexicalContext();
+ lc.push(this);
+ final LexicalContextNode newNode = (LexicalContextNode)accept(lc, visitor);
+ return lc.pop(newNode);
}
}
diff --git a/src/jdk/nashorn/internal/ir/LineNumberNode.java b/src/jdk/nashorn/internal/ir/LineNumberNode.java
index c7912ff0..63f04594 100644
--- a/src/jdk/nashorn/internal/ir/LineNumberNode.java
+++ b/src/jdk/nashorn/internal/ir/LineNumberNode.java
@@ -25,6 +25,7 @@
package jdk.nashorn.internal.ir;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.Token;
import jdk.nashorn.internal.runtime.Source;
@@ -32,8 +33,8 @@ import jdk.nashorn.internal.runtime.Source;
/**
* IR Node representing a line number
*/
-
-public class LineNumberNode extends Node {
+@Immutable
+public final class LineNumberNode extends Node {
/** Line number */
private final int lineNumber;
@@ -46,24 +47,17 @@ public class LineNumberNode extends Node {
*/
public LineNumberNode(final Source source, final long token, final int lineNumber) {
super(source, token, Token.descPosition(token));
-
this.lineNumber = lineNumber;
}
- private LineNumberNode(final LineNumberNode lineNumberNode) {
+ private LineNumberNode(final LineNumberNode lineNumberNode) {
super(lineNumberNode);
-
this.lineNumber = lineNumberNode.getLineNumber();
}
@Override
- protected Node copy(final CopyState cs) {
- return new LineNumberNode(this);
- }
-
- @Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterLineNumberNode(this) != null) {
+ if (visitor.enterLineNumberNode(this)) {
return visitor.leaveLineNumberNode(this);
}
diff --git a/src/jdk/nashorn/internal/ir/LiteralNode.java b/src/jdk/nashorn/internal/ir/LiteralNode.java
index cc424b7a..ae80214c 100644
--- a/src/jdk/nashorn/internal/ir/LiteralNode.java
+++ b/src/jdk/nashorn/internal/ir/LiteralNode.java
@@ -30,6 +30,7 @@ import java.util.Collections;
import java.util.List;
import jdk.nashorn.internal.codegen.CompileUnit;
import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.Lexer.LexerToken;
import jdk.nashorn.internal.parser.Token;
@@ -44,6 +45,7 @@ import jdk.nashorn.internal.runtime.Undefined;
*
* @param <T> the literal type
*/
+@Immutable
public abstract class LiteralNode<T> extends Node implements PropertyKey {
/** Literal value */
protected final T value;
@@ -93,23 +95,6 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
return value == null;
}
- @Override
- public int hashCode() {
- return value == null ? 0 : value.hashCode();
- }
-
- @Override
- public boolean equals(final Object other) {
- if (!(other instanceof LiteralNode<?>)) {
- return false;
- }
- final LiteralNode<?> otherNode = (LiteralNode<?>)other;
- if (otherNode.isNull()) {
- return isNull();
- }
- return ((LiteralNode<?>)other).getValue().equals(value);
- }
-
/**
* Check if the literal value is boolean true
* @return true if literal value is boolean true
@@ -226,7 +211,7 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
*/
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterLiteralNode(this) != null) {
+ if (visitor.enterLiteralNode(this)) {
return visitor.leaveLiteralNode(this);
}
@@ -274,7 +259,8 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
return new NodeLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish());
}
- private static class BooleanLiteralNode extends LiteralNode<Boolean> {
+ @Immutable
+ private static final class BooleanLiteralNode extends LiteralNode<Boolean> {
private BooleanLiteralNode(final Source source, final long token, final int finish, final boolean value) {
super(source, Token.recast(token, value ? TokenType.TRUE : TokenType.FALSE), finish, value);
@@ -285,11 +271,6 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
}
@Override
- protected Node copy(final CopyState cs) {
- return new BooleanLiteralNode(this);
- }
-
- @Override
public boolean isTrue() {
return value;
}
@@ -331,7 +312,8 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
return new BooleanLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish(), value);
}
- private static class NumberLiteralNode extends LiteralNode<Number> {
+ @Immutable
+ private static final class NumberLiteralNode extends LiteralNode<Number> {
private final Type type = numberGetType(value);
@@ -358,11 +340,6 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
}
@Override
- protected Node copy(final CopyState cs) {
- return new NumberLiteralNode(this);
- }
-
- @Override
public Type getType() {
return type;
}
@@ -407,11 +384,6 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
private UndefinedLiteralNode(final UndefinedLiteralNode literalNode) {
super(literalNode);
}
-
- @Override
- protected Node copy(final CopyState cs) {
- return new UndefinedLiteralNode(this);
- }
}
/**
@@ -440,6 +412,7 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
return new UndefinedLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish());
}
+ @Immutable
private static class StringLiteralNode extends LiteralNode<String> {
private StringLiteralNode(final Source source, final long token, final int finish, final String value) {
super(source, Token.recast(token, TokenType.STRING), finish, value);
@@ -450,11 +423,6 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
}
@Override
- protected Node copy(final CopyState cs) {
- return new StringLiteralNode(this);
- }
-
- @Override
public void toString(final StringBuilder sb) {
sb.append('\"');
sb.append(value);
@@ -488,6 +456,7 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
return new StringLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish(), value);
}
+ @Immutable
private static class LexerTokenLiteralNode extends LiteralNode<LexerToken> {
private LexerTokenLiteralNode(final Source source, final long token, final int finish, final LexerToken value) {
super(source, Token.recast(token, TokenType.STRING), finish, value); //TODO is string the correct token type here?
@@ -498,11 +467,6 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
}
@Override
- protected Node copy(final CopyState cs) {
- return new LexerTokenLiteralNode(this);
- }
-
- @Override
public Type getType() {
return Type.OBJECT;
}
@@ -539,7 +503,7 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
return new LexerTokenLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish(), value);
}
- private static class NodeLiteralNode extends LiteralNode<Node> {
+ private static final class NodeLiteralNode extends LiteralNode<Node> {
private NodeLiteralNode(final Source source, final long token, final int finish) {
this(source, token, finish, null);
@@ -558,13 +522,8 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
}
@Override
- protected Node copy(final CopyState cs) {
- return new NodeLiteralNode(this);
- }
-
- @Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterLiteralNode(this) != null) {
+ if (visitor.enterLiteralNode(this)) {
if (value != null) {
final Node newValue = value.accept(visitor);
if(value != newValue) {
@@ -617,7 +576,7 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
/**
* Array literal node class.
*/
- public static class ArrayLiteralNode extends LiteralNode<Node[]> {
+ public static final class ArrayLiteralNode extends LiteralNode<Node[]> {
private static class PostsetMarker {
//empty
}
@@ -705,11 +664,6 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
this.elementType = node.elementType;
}
- @Override
- protected Node copy(final CopyState cs) {
- return new ArrayLiteralNode(this);
- }
-
/**
* Compute things like widest element type needed. Internal use from compiler only
*/
@@ -894,7 +848,7 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterLiteralNode(this) != null) {
+ if (visitor.enterLiteralNode(this)) {
for (int i = 0; i < value.length; i++) {
final Node element = value[i];
if (element != null) {
diff --git a/src/jdk/nashorn/internal/ir/Location.java b/src/jdk/nashorn/internal/ir/Location.java
index c8e01dd3..cd9edb5b 100644
--- a/src/jdk/nashorn/internal/ir/Location.java
+++ b/src/jdk/nashorn/internal/ir/Location.java
@@ -25,16 +25,13 @@
package jdk.nashorn.internal.ir;
-import java.util.Objects;
import jdk.nashorn.internal.parser.Token;
import jdk.nashorn.internal.parser.TokenType;
import jdk.nashorn.internal.runtime.Source;
/**
* Used to locate an entity back to it's source file.
- *
*/
-
public class Location implements Cloneable {
/** Source of entity. */
private final Source source;
@@ -73,22 +70,13 @@ public class Location implements Cloneable {
}
@Override
- public boolean equals(final Object other) {
- if (other == null) {
- return false;
- }
-
- if (other.getClass() != this.getClass()) {
- return false;
- }
-
- final Location loc = (Location)other;
- return token == loc.token && Objects.equals(source, loc.source);
+ public final boolean equals(final Object other) {
+ return super.equals(other);
}
@Override
- public int hashCode() {
- return Token.hashCode(token) ^ Objects.hashCode(source);
+ public final int hashCode() {
+ return super.hashCode();
}
/**
diff --git a/src/jdk/nashorn/internal/ir/LoopNode.java b/src/jdk/nashorn/internal/ir/LoopNode.java
new file mode 100644
index 00000000..b3909dc2
--- /dev/null
+++ b/src/jdk/nashorn/internal/ir/LoopNode.java
@@ -0,0 +1,176 @@
+/*
+ * 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.ir;
+
+import java.util.Arrays;
+import java.util.List;
+
+import jdk.nashorn.internal.codegen.Label;
+import jdk.nashorn.internal.runtime.Source;
+
+/**
+ * A loop node, for example a while node, do while node or for node
+ */
+public abstract class LoopNode extends BreakableNode {
+ /** loop continue label. */
+ protected final Label continueLabel;
+
+ /** Loop test node, null if infinite */
+ protected final Node test;
+
+ /** Loop body */
+ protected final Block body;
+
+ /** Can control flow escape from loop, e.g. through breaks or continues to outer loops? */
+ protected final boolean controlFlowEscapes;
+
+ /**
+ * Constructor
+ *
+ * @param source source
+ * @param token token
+ * @param finish finish
+ * @param test test, or null if infinite loop
+ * @param body loop body
+ * @param controlFlowEscapes controlFlowEscapes
+ */
+ protected LoopNode(final Source source, final long token, final int finish, final Node test, final Block body, final boolean controlFlowEscapes) {
+ super(source, token, finish, new Label("while_break"));
+ this.continueLabel = new Label("while_continue");
+ this.test = test;
+ this.body = body;
+ this.controlFlowEscapes = controlFlowEscapes;
+ }
+
+ /**
+ * Constructor
+ *
+ * @param loopNode loop node
+ * @param test new test
+ * @param body new body
+ * @param controlFlowEscapes controlFlowEscapes
+ */
+ protected LoopNode(final LoopNode loopNode, final Node test, final Block body, final boolean controlFlowEscapes) {
+ super(loopNode);
+ this.continueLabel = new Label(loopNode.continueLabel);
+ this.test = test;
+ this.body = body;
+ this.controlFlowEscapes = controlFlowEscapes;
+ }
+
+ @Override
+ public abstract Node ensureUniqueLabels(final LexicalContext lc);
+
+ /**
+ * Does the control flow escape from this loop, i.e. through breaks or
+ * continues to outer loops?
+ * @return true if control flow escapes
+ */
+ public boolean controlFlowEscapes() {
+ return controlFlowEscapes;
+ }
+
+
+ @Override
+ public boolean isTerminal() {
+ if (!mustEnter()) {
+ return false;
+ }
+ //must enter but control flow may escape - then not terminal
+ if (controlFlowEscapes) {
+ return false;
+ }
+ //must enter, but body ends with return - then terminal
+ if (body.isTerminal()) {
+ return true;
+ }
+ //no breaks or returns, it is still terminal if we can never exit
+ return test == null;
+ }
+
+ /**
+ * Conservative check: does this loop have to be entered?
+ * @return true if body will execute at least once
+ */
+ public abstract boolean mustEnter();
+
+ /**
+ * Get the continue label for this while node, i.e. location to go to on continue
+ * @return continue label
+ */
+ public Label getContinueLabel() {
+ return continueLabel;
+ }
+
+ @Override
+ public List<Label> getLabels() {
+ return Arrays.asList(breakLabel, continueLabel);
+ }
+
+ @Override
+ public boolean isLoop() {
+ return true;
+ }
+
+ /**
+ * Get the body for this for node
+ * @return the body
+ */
+ public abstract Block getBody();
+
+ /**
+ * @param lc lexical context
+ * @param body new body
+ * @return new for node if changed or existing if not
+ */
+ public abstract LoopNode setBody(final LexicalContext lc, final Block body);
+
+ /**
+ * Get the test for this for node
+ * @return the test
+ */
+ public abstract Node getTest();
+
+ /**
+ * Set the test for this for node
+ *
+ * @param lc lexical context
+ * @param test new test
+ * @return same or new node depending on if test was changed
+ */
+ public abstract LoopNode setTest(final LexicalContext lc, final Node test);
+
+ /**
+ * Set the control flow escapes flag for this node.
+ * TODO - integrate this with Lowering in a better way
+ *
+ * @param lc lexical context
+ * @param controlFlowEscapes control flow escapes value
+ * @return new loop node if changed otherwise the same
+ */
+ public abstract LoopNode setControlFlowEscapes(final LexicalContext lc, final boolean controlFlowEscapes);
+
+}
diff --git a/src/jdk/nashorn/internal/ir/Node.java b/src/jdk/nashorn/internal/ir/Node.java
index c5f01337..dfed903d 100644
--- a/src/jdk/nashorn/internal/ir/Node.java
+++ b/src/jdk/nashorn/internal/ir/Node.java
@@ -25,7 +25,9 @@
package jdk.nashorn.internal.ir;
-import java.util.IdentityHashMap;
+import java.util.ArrayList;
+import java.util.List;
+
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.Token;
@@ -33,30 +35,17 @@ import jdk.nashorn.internal.runtime.Source;
/**
* Nodes are used to compose Abstract Syntax Trees.
- *
*/
public abstract class Node extends Location {
/** Node symbol. */
private Symbol symbol;
/** Start of source range. */
- protected int start;
+ protected final int start;
/** End of source range. */
protected int finish;
- /** Has this node been resolved - i.e. emitted code already */
- private boolean isResolved;
-
- /** Is this node terminal */
- private boolean isTerminal;
-
- /** Is this a goto node */
- private boolean hasGoto;
-
- /** Is this a discard */
- private boolean shouldDiscard;
-
/**
* Constructor
*
@@ -72,6 +61,21 @@ public abstract class Node extends Location {
}
/**
+ * Constructor
+ *
+ * @param source source
+ * @param token token
+ * @param start start
+ * @param finish finish
+ */
+ protected Node(final Source source, final long token, final int start, final int finish) {
+ super(source, token);
+
+ this.start = start;
+ this.finish = finish;
+ }
+
+ /**
* Copy constructor
*
* @param node source node
@@ -79,13 +83,9 @@ public abstract class Node extends Location {
protected Node(final Node node) {
super(node);
- this.symbol = node.symbol;
- this.isResolved = node.isResolved;
- this.isTerminal = node.isTerminal;
- this.hasGoto = node.hasGoto;
- this.shouldDiscard = node.shouldDiscard;
- this.start = node.start;
- this.finish = node.finish;
+ this.symbol = node.symbol;
+ this.start = node.start;
+ this.finish = node.finish;
}
/**
@@ -156,28 +156,6 @@ public abstract class Node extends Location {
}
/**
- * Test to see if code been generated for this node. Set isResolved if not.
- *
- * @return True if node has already been resolved.
- */
- public boolean testResolved() {
- if (isResolved()) {
- return true;
- }
-
- setIsResolved(true);
-
- return false;
- }
-
- /**
- * Reset the resolved flag.
- */
- public void resetResolved() {
- setIsResolved(false);
- }
-
- /**
* Is this a debug info node like LineNumberNode etc?
*
* @return true if this is a debug node
@@ -187,72 +165,13 @@ public abstract class Node extends Location {
}
/**
- * Helper class used for node cloning
- */
- public static final class CopyState {
- private final IdentityHashMap<Node, Node> cloneMap = new IdentityHashMap<>();
-
- /**
- * Find existing or create new copy of the node.
- *
- * @param node Node to copy.
- *
- * @return New object.
- */
- public Node existingOrCopy(final Node node) {
- if (node != null) {
- Node copy = cloneMap.get(node);
-
- if (copy == null) {
- copy = node.copy(this);
- cloneMap.put(node, copy);
- }
-
- return copy;
- }
-
- return node;
- }
-
- /**
- * Find existing or use old copy of the node.
- *
- * @param node Node to copy.
- *
- * @return new object.
- */
- public Node existingOrSame(final Node node) {
- if (node != null) {
- Node copy = cloneMap.get(node);
-
- if (copy == null) {
- copy = node;
- }
-
- return copy;
- }
-
- return node;
- }
- }
-
- /**
- * Deep copy the node.
- *
- * @return Deep copy of the Node.
- */
- public final Node copy() {
- return copy(new CopyState());
- }
-
- /**
- * Deep copy the node.
- *
- * @param cs CopyState passed around to re-use certain nodes.
- * @return Deep copy of the Node.
+ * For reference copies - ensure that labels in the copy node are unique
+ * using an appropriate copy constructor
+ * @param lc lexical context
+ * @return new node or same of no labels
*/
- protected Node copy(final CopyState cs) {
- return cs.existingOrCopy(this);
+ public Node ensureUniqueLabels(final LexicalContext lc) {
+ return this;
}
/**
@@ -283,35 +202,7 @@ public abstract class Node extends Location {
* @return true if terminal
*/
public boolean hasTerminalFlags() {
- return isTerminal || hasGoto;
- }
-
- /**
- * Copy the terminal flags state of a node to another node
- *
- * @param other source node
- */
- public void copyTerminalFlags(final Node other) {
- isTerminal = other.isTerminal;
- hasGoto = other.hasGoto;
- }
-
- /**
- * Check if the return value of this expression should be discarded
- * @return true if return value is discarded
- */
- public boolean shouldDiscard() {
- return shouldDiscard;
- }
-
- /**
- * Setter that determines whether this node's return value should be discarded
- * or not
- *
- * @param shouldDiscard true if return value is discarded, false otherwise
- */
- public void setDiscard(final boolean shouldDiscard) {
- this.shouldDiscard = shouldDiscard;
+ return isTerminal() || hasGoto();
}
/**
@@ -336,29 +227,7 @@ public abstract class Node extends Location {
* @return true if node has goto semantics
*/
public boolean hasGoto() {
- return hasGoto;
- }
-
- /**
- * Flag this node as having goto semantics as described in {@link Node#hasGoto()}
- */
- public void setHasGoto() {
- this.hasGoto = true;
- }
-
- /**
- * Check whether this node is resolved, i.e. code has been generated for it
- * @return true if node is resolved
- */
- public boolean isResolved() {
- return isResolved;
- }
-
- /**
- * Flag this node as resolved or not, i.e. code has been generated for it
- */
- private void setIsResolved(boolean isResolved) {
- this.isResolved = isResolved;
+ return false;
}
/**
@@ -370,14 +239,6 @@ public abstract class Node extends Location {
}
/**
- * Set start position for node
- * @param start start position
- */
- public void setStart(final int start) {
- this.start = start;
- }
-
- /**
* Return the Symbol the compiler has assigned to this Node. The symbol
* is the place where it's expression value is stored after evaluation
*
@@ -404,17 +265,29 @@ public abstract class Node extends Location {
* @return true if this node is terminal
*/
public boolean isTerminal() {
- return isTerminal;
+ return false;
}
- /**
- * Set this to be a terminal node, i.e. it terminates control flow as described
- * in {@link Node#isTerminal()}
- *
- * @param isTerminal true if this is a terminal node, false otherwise
- */
- public void setIsTerminal(final boolean isTerminal) {
- this.isTerminal = isTerminal;
+ //on change, we have to replace the entire list, that's we can't simple do ListIterator.set
+ static <T extends Node> List<T> accept(final NodeVisitor visitor, final Class<T> clazz, final List<T> list) {
+ boolean changed = false;
+ final List<T> newList = new ArrayList<>();
+
+ for (final Node node : list) {
+ final T newNode = clazz.cast(node.accept(visitor));
+ if (newNode != node) {
+ changed = true;
+ }
+ newList.add(newNode);
+ }
+
+ return changed ? newList : list;
}
+ static <T extends LexicalContextNode> T replaceInLexicalContext(final LexicalContext lc, final T oldNode, final T newNode) {
+ if (lc != null) {
+ lc.replace(oldNode, newNode);
+ }
+ return newNode;
+ }
}
diff --git a/src/jdk/nashorn/internal/ir/ObjectNode.java b/src/jdk/nashorn/internal/ir/ObjectNode.java
index f6724a62..744a44d7 100644
--- a/src/jdk/nashorn/internal/ir/ObjectNode.java
+++ b/src/jdk/nashorn/internal/ir/ObjectNode.java
@@ -25,16 +25,18 @@
package jdk.nashorn.internal.ir;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
/**
* IR representation of an object literal.
*/
-public class ObjectNode extends Node {
+@Immutable
+public final class ObjectNode extends Node {
/** Literal elements. */
private final List<Node> elements;
@@ -49,35 +51,18 @@ public class ObjectNode extends Node {
*/
public ObjectNode(final Source source, final long token, final int finish, final List<Node> elements) {
super(source, token, finish);
-
this.elements = elements;
}
- private ObjectNode(final ObjectNode objectNode, final CopyState cs) {
+ private ObjectNode(final ObjectNode objectNode, final List<Node> elements) {
super(objectNode);
-
- final List<Node> newElements = new ArrayList<>();
-
- for (final Node element : objectNode.elements) {
- newElements.add(cs.existingOrCopy(element));
- }
-
- this.elements = newElements;
- }
-
- @Override
- protected Node copy(final CopyState cs) {
- return new ObjectNode(this, cs);
+ this.elements = elements;
}
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterObjectNode(this) != null) {
- for (int i = 0, count = elements.size(); i < count; i++) {
- elements.set(i, elements.get(i).accept(visitor));
- }
-
- return visitor.leaveObjectNode(this);
+ if (visitor.enterObjectNode(this)) {
+ return visitor.leaveObjectNode(setElements(Node.accept(visitor, Node.class, elements)));
}
return this;
@@ -112,4 +97,11 @@ public class ObjectNode extends Node {
public List<Node> getElements() {
return Collections.unmodifiableList(elements);
}
+
+ private ObjectNode setElements(final List<Node> elements) {
+ if (this.elements == elements) {
+ return this;
+ }
+ return new ObjectNode(this, elements);
+ }
}
diff --git a/src/jdk/nashorn/internal/ir/PropertyNode.java b/src/jdk/nashorn/internal/ir/PropertyNode.java
index a6bc49de..635e1aa3 100644
--- a/src/jdk/nashorn/internal/ir/PropertyNode.java
+++ b/src/jdk/nashorn/internal/ir/PropertyNode.java
@@ -25,28 +25,27 @@
package jdk.nashorn.internal.ir;
-import jdk.nashorn.internal.ir.annotations.Reference;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
/**
* IR representation of an object literal property.
*/
-public class PropertyNode extends Node {
+@Immutable
+public final class PropertyNode extends Node {
/** Property key. */
- private PropertyKey key;
+ private final PropertyKey key;
/** Property value. */
- private Node value;
+ private final Node value;
/** Property getter. */
- @Reference
- private Node getter;
+ private final FunctionNode getter;
/** Property getter. */
- @Reference
- private Node setter;
+ private final FunctionNode setter;
/**
* Constructor
@@ -56,26 +55,23 @@ public class PropertyNode extends Node {
* @param finish finish
* @param key the key of this property
* @param value the value of this property
+ * @param getter getter function body
+ * @param setter setter function body
*/
- public PropertyNode(final Source source, final long token, final int finish, final PropertyKey key, final Node value) {
+ public PropertyNode(final Source source, final long token, final int finish, final PropertyKey key, final Node value, final FunctionNode getter, final FunctionNode setter) {
super(source, token, finish);
-
this.key = key;
this.value = value;
+ this.getter = getter;
+ this.setter = setter;
}
- private PropertyNode(final PropertyNode propertyNode, final CopyState cs) {
+ private PropertyNode(final PropertyNode propertyNode, final PropertyKey key, final Node value, final FunctionNode getter, final FunctionNode setter) {
super(propertyNode);
-
- this.key = (PropertyKey)cs.existingOrCopy((Node)propertyNode.key);
- this.value = cs.existingOrCopy(propertyNode.value);
- this.getter = cs.existingOrSame(propertyNode.getter);
- this.setter = cs.existingOrSame(propertyNode.setter);
- }
-
- @Override
- protected Node copy(final CopyState cs) {
- return new PropertyNode(this, cs);
+ this.key = key;
+ this.value = value;
+ this.getter = getter;
+ this.setter = setter;
}
/**
@@ -88,22 +84,12 @@ public class PropertyNode extends Node {
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterPropertyNode(this) != null) {
- key = (PropertyKey)((Node)key).accept(visitor);
-
- if (value != null) {
- value = value.accept(visitor);
- }
-
- if (getter != null) {
- getter = getter.accept(visitor);
- }
-
- if (setter != null) {
- setter = setter.accept(visitor);
- }
-
- return visitor.leavePropertyNode(this);
+ if (visitor.enterPropertyNode(this)) {
+ return visitor.leavePropertyNode(
+ setKey((PropertyKey)((Node)key).accept(visitor)).
+ setValue(value == null ? null : value.accept(visitor)).
+ setGetter(getter == null ? null : (FunctionNode)getter.accept(visitor)).
+ setSetter(setter == null ? null : (FunctionNode)setter.accept(visitor)));
}
return this;
@@ -136,16 +122,20 @@ public class PropertyNode extends Node {
* Get the getter for this property
* @return getter or null if none exists
*/
- public Node getGetter() {
+ public FunctionNode getGetter() {
return getter;
}
/**
* Set the getter of this property, null if none
* @param getter getter
+ * @return same node or new node if state changed
*/
- public void setGetter(final Node getter) {
- this.getter = getter;
+ public PropertyNode setGetter(final FunctionNode getter) {
+ if (this.getter == getter) {
+ return this;
+ }
+ return new PropertyNode(this, key, value, getter, setter);
}
/**
@@ -156,20 +146,31 @@ public class PropertyNode extends Node {
return (Node)key;
}
+ private PropertyNode setKey(final PropertyKey key) {
+ if (this.key == key) {
+ return this;
+ }
+ return new PropertyNode(this, key, value, getter, setter);
+ }
+
/**
* Get the setter for this property
* @return setter or null if none exists
*/
- public Node getSetter() {
+ public FunctionNode getSetter() {
return setter;
}
/**
* Set the setter for this property, null if none
* @param setter setter
+ * @return same node or new node if state changed
*/
- public void setSetter(final Node setter) {
- this.setter = setter;
+ public PropertyNode setSetter(final FunctionNode setter) {
+ if (this.setter == setter) {
+ return this;
+ }
+ return new PropertyNode(this, key, value, getter, setter);
}
/**
@@ -183,8 +184,12 @@ public class PropertyNode extends Node {
/**
* Set the value of this property
* @param value new value
+ * @return same node or new node if state changed
*/
- public void setValue(final Node value) {
- this.value = value;
- }
+ public PropertyNode setValue(final Node value) {
+ if (this.value == value) {
+ return this;
+ }
+ return new PropertyNode(this, key, value, getter, setter);
+ }
}
diff --git a/src/jdk/nashorn/internal/ir/ReturnNode.java b/src/jdk/nashorn/internal/ir/ReturnNode.java
index 1400f395..dafc956d 100644
--- a/src/jdk/nashorn/internal/ir/ReturnNode.java
+++ b/src/jdk/nashorn/internal/ir/ReturnNode.java
@@ -27,22 +27,17 @@ package jdk.nashorn.internal.ir;
import static jdk.nashorn.internal.parser.TokenType.RETURN;
import static jdk.nashorn.internal.parser.TokenType.YIELD;
-
-import jdk.nashorn.internal.ir.annotations.Ignore;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
/**
* IR representation for RETURN or YIELD statements.
- *
*/
+@Immutable
public class ReturnNode extends Node {
/** Optional expression. */
- private Node expression;
-
- /** Try chain. */
- @Ignore
- private final TryNode tryChain;
+ private final Node expression;
/**
* Constructor
@@ -51,27 +46,20 @@ public class ReturnNode extends Node {
* @param token token
* @param finish finish
* @param expression expression to return
- * @param tryChain surrounding try chain.
*/
- public ReturnNode(final Source source, final long token, final int finish, final Node expression, final TryNode tryChain) {
+ public ReturnNode(final Source source, final long token, final int finish, final Node expression) {
super(source, token, finish);
-
this.expression = expression;
- this.tryChain = tryChain;
-
- setIsTerminal(true);
}
- private ReturnNode(final ReturnNode returnNode, final CopyState cs) {
+ private ReturnNode(final ReturnNode returnNode, final Node expression) {
super(returnNode);
-
- this.expression = cs.existingOrCopy(returnNode.expression);
- this.tryChain = (TryNode)cs.existingOrSame(returnNode.tryChain);
+ this.expression = expression;
}
@Override
- protected Node copy(final CopyState cs) {
- return new ReturnNode(this, cs);
+ public boolean isTerminal() {
+ return true;
}
/**
@@ -100,11 +88,10 @@ public class ReturnNode extends Node {
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterReturnNode(this) != null) {
+ if (visitor.enterReturnNode(this)) {
if (expression != null) {
- expression = expression.accept(visitor);
+ return visitor.leaveReturnNode(setExpression(expression.accept(visitor)));
}
-
return visitor.leaveReturnNode(this);
}
@@ -121,25 +108,6 @@ public class ReturnNode extends Node {
}
}
- @Override
- public boolean equals(final Object other) {
- if (other instanceof ReturnNode) {
- final ReturnNode otherReturn = (ReturnNode)other;
- if (hasExpression() != otherReturn.hasExpression()) {
- return false;
- } else if (hasExpression()) {
- return otherReturn.getExpression().equals(getExpression());
- }
- return true;
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return 0x4711_17 ^ (expression == null ? 0 : expression.hashCode());
- }
-
/**
* Get the expression this node returns
* @return return expression, or null if void return
@@ -151,16 +119,13 @@ public class ReturnNode extends Node {
/**
* Reset the expression this node returns
* @param expression new expression, or null if void return
+ * @return new or same return node
*/
- public void setExpression(final Node expression) {
- this.expression = expression;
+ public ReturnNode setExpression(final Node expression) {
+ if (this.expression == expression) {
+ return this;
+ }
+ return new ReturnNode(this, expression);
}
- /**
- * Get the surrounding try chain for this return node
- * @return try chain
- */
- public TryNode getTryChain() {
- return tryChain;
- }
}
diff --git a/src/jdk/nashorn/internal/ir/RuntimeNode.java b/src/jdk/nashorn/internal/ir/RuntimeNode.java
index 461007cd..7bdb6c0a 100644
--- a/src/jdk/nashorn/internal/ir/RuntimeNode.java
+++ b/src/jdk/nashorn/internal/ir/RuntimeNode.java
@@ -30,14 +30,15 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.TokenType;
import jdk.nashorn.internal.runtime.Source;
/**
* IR representation for a runtime call.
- *
*/
+@Immutable
public class RuntimeNode extends Node implements TypeOverride<RuntimeNode> {
/**
@@ -271,10 +272,10 @@ public class RuntimeNode extends Node implements TypeOverride<RuntimeNode> {
private final List<Node> args;
/** Call site override - e.g. we know that a ScriptRuntime.ADD will return an int */
- private Type callSiteType;
+ private final Type callSiteType;
/** is final - i.e. may not be removed again, lower in the code pipeline */
- private boolean isFinal;
+ private final boolean isFinal;
/**
* Constructor
@@ -290,6 +291,17 @@ public class RuntimeNode extends Node implements TypeOverride<RuntimeNode> {
this.request = request;
this.args = args;
+ this.callSiteType = null;
+ this.isFinal = false;
+ }
+
+ private RuntimeNode(final RuntimeNode runtimeNode, final Request request, final Type callSiteType, final boolean isFinal, final List<Node> args) {
+ super(runtimeNode);
+
+ this.request = request;
+ this.args = args;
+ this.callSiteType = callSiteType;
+ this.isFinal = isFinal;
}
/**
@@ -326,8 +338,10 @@ public class RuntimeNode extends Node implements TypeOverride<RuntimeNode> {
public RuntimeNode(final Node parent, final Request request, final List<Node> args) {
super(parent);
- this.request = request;
- this.args = args;
+ this.request = request;
+ this.args = args;
+ this.callSiteType = null;
+ this.isFinal = false;
}
/**
@@ -350,20 +364,6 @@ public class RuntimeNode extends Node implements TypeOverride<RuntimeNode> {
this(parent, request, parent.lhs(), parent.rhs());
}
- private RuntimeNode(final RuntimeNode runtimeNode, final CopyState cs) {
- super(runtimeNode);
-
- final List<Node> newArgs = new ArrayList<>();
-
- for (final Node arg : runtimeNode.args) {
- newArgs.add(cs.existingOrCopy(arg));
- }
-
- this.request = runtimeNode.request;
- this.args = newArgs;
- this.callSiteType = runtimeNode.callSiteType;
- }
-
/**
* Is this node final - i.e. it can never be replaced with other nodes again
* @return true if final
@@ -374,14 +374,14 @@ public class RuntimeNode extends Node implements TypeOverride<RuntimeNode> {
/**
* Flag this node as final - i.e it may never be replaced with other nodes again
+ * @param isFinal is the node final, i.e. can not be removed and replaced by a less generic one later in codegen
+ * @return same runtime node if already final, otherwise a new one
*/
- public void setIsFinal() {
- this.isFinal = true;
- }
-
- @Override
- protected Node copy(final CopyState cs) {
- return new RuntimeNode(this, cs);
+ public RuntimeNode setIsFinal(final boolean isFinal) {
+ if (this.isFinal == isFinal) {
+ return this;
+ }
+ return new RuntimeNode(this, request, callSiteType, isFinal, args);
}
/**
@@ -394,8 +394,10 @@ public class RuntimeNode extends Node implements TypeOverride<RuntimeNode> {
@Override
public RuntimeNode setType(final Type type) {
- this.callSiteType = type;
- return this;
+ if (this.callSiteType == type) {
+ return this;
+ }
+ return new RuntimeNode(this, request, type, isFinal, args);
}
@Override
@@ -409,12 +411,12 @@ public class RuntimeNode extends Node implements TypeOverride<RuntimeNode> {
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterRuntimeNode(this) != null) {
- for (int i = 0, count = args.size(); i < count; i++) {
- args.set(i, args.get(i).accept(visitor));
+ if (visitor.enterRuntimeNode(this)) {
+ final List<Node> newArgs = new ArrayList<>();
+ for (final Node arg : args) {
+ newArgs.add(arg.accept(visitor));
}
-
- return visitor.leaveRuntimeNode(this);
+ return visitor.leaveRuntimeNode(setArgs(newArgs));
}
return this;
@@ -449,6 +451,13 @@ public class RuntimeNode extends Node implements TypeOverride<RuntimeNode> {
return Collections.unmodifiableList(args);
}
+ private RuntimeNode setArgs(final List<Node> args) {
+ if (this.args == args) {
+ return this;
+ }
+ return new RuntimeNode(this, request, callSiteType, isFinal, args);
+ }
+
/**
* Get the request that this runtime node implements
* @return the request
diff --git a/src/jdk/nashorn/internal/ir/SplitNode.java b/src/jdk/nashorn/internal/ir/SplitNode.java
index b751cdcb..49c4092f 100644
--- a/src/jdk/nashorn/internal/ir/SplitNode.java
+++ b/src/jdk/nashorn/internal/ir/SplitNode.java
@@ -25,99 +25,65 @@
package jdk.nashorn.internal.ir;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
import jdk.nashorn.internal.codegen.CompileUnit;
-import jdk.nashorn.internal.codegen.Label;
-import jdk.nashorn.internal.codegen.MethodEmitter;
-import jdk.nashorn.internal.ir.annotations.Ignore;
-import jdk.nashorn.internal.ir.annotations.Reference;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
-
/**
* Node indicating code is split across classes.
*/
-public class SplitNode extends Node {
+@Immutable
+public class SplitNode extends LexicalContextNode {
/** Split node method name. */
private final String name;
/** Compilation unit. */
- private CompileUnit compileUnit;
-
- /** Method emitter for current method. */
- private MethodEmitter method;
-
- /** Method emitter for caller method. */
- private MethodEmitter caller;
-
- /** Containing function. */
- @Reference
- private final FunctionNode functionNode;
-
- /** A list of target labels in parent methods this split node may encounter. */
- @Ignore
- private final List<Label> externalTargets;
-
- /** True if this split node or any of its children contain a return statement. */
- private boolean hasReturn;
+ private final CompileUnit compileUnit;
/** Body of split code. */
- @Ignore
- private Node body;
+ private final Node body;
/**
* Constructor
*
- * @param name name of split node
- * @param functionNode function node to split in
- * @param body body of split code
+ * @param name name of split node
+ * @param body body of split code
+ * @param compileUnit compile unit to use for the body
*/
- public SplitNode(final String name, final FunctionNode functionNode, final Node body) {
+ public SplitNode(final String name, final Node body, final CompileUnit compileUnit) {
super(body.getSource(), body.getToken(), body.getFinish());
-
- this.name = name;
- this.functionNode = functionNode;
- this.body = body;
- this.externalTargets = new ArrayList<>();
+ this.name = name;
+ this.body = body;
+ this.compileUnit = compileUnit;
}
- private SplitNode(final SplitNode splitNode, final CopyState cs) {
+ private SplitNode(final SplitNode splitNode, final Node body) {
super(splitNode);
+ this.name = splitNode.name;
+ this.body = body;
+ this.compileUnit = splitNode.compileUnit;
+ }
- this.name = splitNode.name;
- this.functionNode = (FunctionNode)cs.existingOrSame(splitNode.functionNode);
- this.body = cs.existingOrCopy(splitNode.body);
- this.externalTargets = new ArrayList<>();
+ /**
+ * Get the body for this split node - i.e. the actual code it encloses
+ * @return body for split node
+ */
+ public Node getBody() {
+ return body;
}
- @Override
- protected Node copy(final CopyState cs) {
- return new SplitNode(this, cs);
+ private SplitNode setBody(final LexicalContext lc, final Node body) {
+ if (this.body == body) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new SplitNode(this, body));
}
@Override
- public Node accept(final NodeVisitor visitor) {
- final CompileUnit saveCompileUnit = visitor.getCurrentCompileUnit();
- final MethodEmitter saveMethod = visitor.getCurrentMethodEmitter();
-
- setCaller(saveMethod);
-
- visitor.setCurrentCompileUnit(getCompileUnit());
- visitor.setCurrentMethodEmitter(getMethodEmitter());
-
- try {
- if (visitor.enterSplitNode(this) != null) {
- body = body.accept(visitor);
-
- return visitor.leaveSplitNode(this);
- }
- } finally {
- visitor.setCurrentCompileUnit(saveCompileUnit);
- visitor.setCurrentMethodEmitter(saveMethod);
+ public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
+ if (visitor.enterSplitNode(this)) {
+ return visitor.leaveSplitNode(setBody(lc, body.accept(visitor)));
}
-
return this;
}
@@ -130,22 +96,6 @@ public class SplitNode extends Node {
}
/**
- * Get the method emitter of the caller for this split node
- * @return caller method emitter
- */
- public MethodEmitter getCaller() {
- return caller;
- }
-
- /**
- * Set the caller method emitter for this split node
- * @param caller method emitter
- */
- public void setCaller(final MethodEmitter caller) {
- this.caller = caller;
- }
-
- /**
* Get the name for this split node
* @return name
*/
@@ -161,67 +111,4 @@ public class SplitNode extends Node {
return compileUnit;
}
- /**
- * Set the compile unit for this split node
- * @param compileUnit compile unit
- */
- public void setCompileUnit(final CompileUnit compileUnit) {
- this.compileUnit = compileUnit;
- }
-
- /**
- * Get the method emitter for this split node
- * @return method emitter
- */
- public MethodEmitter getMethodEmitter() {
- return method;
- }
-
- /**
- * Set the method emitter for this split node
- * @param method method emitter
- */
- public void setMethodEmitter(final MethodEmitter method) {
- this.method = method;
- }
-
- /**
- * Get the function node this SplitNode splits
- * @return function node reference
- */
- public FunctionNode getFunctionNode() {
- return functionNode;
- }
-
- /**
- * Get the external targets for this SplitNode
- * @return list of external targets
- */
- public List<Label> getExternalTargets() {
- return Collections.unmodifiableList(externalTargets);
- }
-
- /**
- * Add an external target for this SplitNode
- * @param targetLabel target label
- */
- public void addExternalTarget(final Label targetLabel) {
- externalTargets.add(targetLabel);
- }
-
- /**
- * Check whether this SplitNode returns a value
- * @return true if return
- */
- public boolean hasReturn() {
- return hasReturn;
- }
-
- /**
- * Set whether this SplitNode returns a value or not
- * @param hasReturn true if return exists, false otherwise
- */
- public void setHasReturn(final boolean hasReturn) {
- this.hasReturn = hasReturn;
- }
}
diff --git a/src/jdk/nashorn/internal/ir/SwitchNode.java b/src/jdk/nashorn/internal/ir/SwitchNode.java
index 23d9c7ea..7864a10d 100644
--- a/src/jdk/nashorn/internal/ir/SwitchNode.java
+++ b/src/jdk/nashorn/internal/ir/SwitchNode.java
@@ -28,73 +28,84 @@ package jdk.nashorn.internal.ir;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+
import jdk.nashorn.internal.codegen.Label;
-import jdk.nashorn.internal.ir.annotations.Ignore;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
/**
* IR representation of a SWITCH statement.
*/
-public class SwitchNode extends BreakableNode {
+@Immutable
+public final class SwitchNode extends BreakableNode {
/** Switch expression. */
- private Node expression;
-
- /** Tag symbol. */
- private Symbol tag;
+ private final Node expression;
/** Switch cases. */
- private List<CaseNode> cases;
+ private final List<CaseNode> cases;
- /** Switch default. */
- @Ignore //points to one of the members in the list above, don't traverse twice
- private CaseNode defaultCase;
+ /** Switch default index. */
+ private final int defaultCaseIndex;
+
+ /** Tag symbol. */
+ private Symbol tag;
/**
* Constructor
*
- * @param source the source
- * @param token token
- * @param finish finish
+ * @param source the source
+ * @param token token
+ * @param finish finish
+ * @param expression switch expression
+ * @param cases cases
+ * @param defaultCase the default case node - null if none, otherwise has to be present in cases list
*/
- public SwitchNode(final Source source, final long token, final int finish) {
- super(source, token, finish);
- this.breakLabel = new Label("switch_break");
+ public SwitchNode(final Source source, final long token, final int finish, final Node expression, final List<CaseNode> cases, final CaseNode defaultCase) {
+ super(source, token, finish, new Label("switch_break"));
+ this.expression = expression;
+ this.cases = cases;
+ this.defaultCaseIndex = defaultCase == null ? -1 : cases.indexOf(defaultCase);
}
- private SwitchNode(final SwitchNode switchNode, final CopyState cs) {
+ private SwitchNode(final SwitchNode switchNode, final Node expression, final List<CaseNode> cases, final int defaultCase) {
super(switchNode);
-
- final List<CaseNode> newCases = new ArrayList<>();
-
- for (final CaseNode caseNode : switchNode.getCases()) {
- newCases.add((CaseNode)cs.existingOrCopy(caseNode));
- }
-
- this.expression = cs.existingOrCopy(switchNode.getExpression());
- this.tag = switchNode.getTag();
- this.cases = newCases;
- this.defaultCase = (CaseNode)cs.existingOrCopy(switchNode.getDefaultCase());
- this.breakLabel = new Label(switchNode.getBreakLabel());
+ this.expression = expression;
+ this.cases = cases;
+ this.defaultCaseIndex = defaultCase;
+ this.tag = switchNode.getTag(); //TODO are symbols inhereted as references?
}
@Override
- protected Node copy(final CopyState cs) {
- return new SwitchNode(this, cs);
+ public Node ensureUniqueLabels(final LexicalContext lc) {
+ final List<CaseNode> newCases = new ArrayList<>();
+ for (final CaseNode caseNode : cases) {
+ newCases.add(new CaseNode(caseNode, caseNode.getTest(), caseNode.getBody()));
+ }
+ return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, newCases, defaultCaseIndex));
}
@Override
- public Node accept(final NodeVisitor visitor) {
- if (visitor.enterSwitchNode(this) != null) {
- expression = expression.accept(visitor);
-
- for (int i = 0, count = cases.size(); i < count; i++) {
- cases.set(i, (CaseNode)cases.get(i).accept(visitor));
+ public boolean isTerminal() {
+ //there must be a default case, and that including all other cases must terminate
+ if (!cases.isEmpty() && defaultCaseIndex != -1) {
+ for (final CaseNode caseNode : cases) {
+ if (!caseNode.isTerminal()) {
+ return false;
+ }
}
+ return true;
+ }
+ return false;
- //the default case is in the cases list and should not be explicitly traversed!
+ }
- return visitor.leaveSwitchNode(this);
+ @Override
+ public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
+ if (visitor.enterSwitchNode(this)) {
+ return visitor.leaveSwitchNode(
+ setExpression(visitor.getLexicalContext(), expression.accept(visitor)).
+ setCases(visitor.getLexicalContext(), Node.accept(visitor, CaseNode.class, cases), defaultCaseIndex));
}
return this;
@@ -108,6 +119,14 @@ public class SwitchNode extends BreakableNode {
}
/**
+ * Return the case node that is default case
+ * @return default case or null if none
+ */
+ public CaseNode getDefaultCase() {
+ return defaultCaseIndex == -1 ? null : cases.get(defaultCaseIndex);
+ }
+
+ /**
* Get the cases in this switch
* @return a list of case nodes
*/
@@ -116,27 +135,33 @@ public class SwitchNode extends BreakableNode {
}
/**
- * Set or reset the list of cases in this switch
- * @param cases a list of cases, case nodes
+ * Replace case nodes with new list. the cases have to be the same
+ * and the default case index the same. This is typically used
+ * by NodeVisitors who perform operations on every case node
+ * @param lc lexical context
+ * @param cases list of cases
+ * @return new switcy node or same if no state was changed
*/
- public void setCases(final List<CaseNode> cases) {
- this.cases = cases;
+ public SwitchNode setCases(final LexicalContext lc, final List<CaseNode> cases) {
+ return setCases(lc, cases, defaultCaseIndex);
}
- /**
- * Get the default case for this switch
- * @return default case node
- */
- public CaseNode getDefaultCase() {
- return defaultCase;
+ private SwitchNode setCases(final LexicalContext lc, final List<CaseNode> cases, final int defaultCaseIndex) {
+ if (this.cases == cases) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex));
}
/**
- * Set the default case for this switch
- * @param defaultCase default case node
+ * Set or reset the list of cases in this switch
+ * @param lc lexical context
+ * @param cases a list of cases, case nodes
+ * @param defaultCase a case in the list that is the default - must be in the list or class will assert
+ * @return new switch node or same if no state was changed
*/
- public void setDefaultCase(final CaseNode defaultCase) {
- this.defaultCase = defaultCase;
+ public SwitchNode setCases(final LexicalContext lc, final List<CaseNode> cases, final CaseNode defaultCase) {
+ return setCases(lc, cases, defaultCase == null ? -1 : cases.indexOf(defaultCase));
}
/**
@@ -149,10 +174,15 @@ public class SwitchNode extends BreakableNode {
/**
* Set or reset the expression to switch on
+ * @param lc lexical context
* @param expression switch expression
+ * @return new switch node or same if no state was changed
*/
- public void setExpression(final Node expression) {
- this.expression = expression;
+ public SwitchNode setExpression(final LexicalContext lc, final Node expression) {
+ if (this.expression == expression) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex));
}
/**
diff --git a/src/jdk/nashorn/internal/ir/Symbol.java b/src/jdk/nashorn/internal/ir/Symbol.java
index 603b8b08..da22f64d 100644
--- a/src/jdk/nashorn/internal/ir/Symbol.java
+++ b/src/jdk/nashorn/internal/ir/Symbol.java
@@ -29,8 +29,10 @@ import java.io.PrintWriter;
import java.util.HashSet;
import java.util.Set;
import java.util.StringTokenizer;
+
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.runtime.Context;
+import jdk.nashorn.internal.runtime.Debug;
import jdk.nashorn.internal.runtime.options.Options;
/**
@@ -63,6 +65,8 @@ public final class Symbol implements Comparable<Symbol> {
public static final int IS_LET = 1 << 8;
/** Is this an internal symbol, never represented explicitly in source code */
public static final int IS_INTERNAL = 1 << 9;
+ /** Is this a function self-reference symbol */
+ public static final int IS_FUNCTION_SELF = 1 << 10;
/** Null or name identifying symbol. */
private final String name;
@@ -70,12 +74,6 @@ public final class Symbol implements Comparable<Symbol> {
/** Symbol flags. */
private int flags;
- /** Defining node. */
- private Node node;
-
- /** Definition block. */
- private final Block block;
-
/** Type of symbol. */
private Type type;
@@ -121,16 +119,12 @@ public final class Symbol implements Comparable<Symbol> {
*
* @param name name of symbol
* @param flags symbol flags
- * @param node node this symbol is in
- * @param block block this symbol is in
* @param type type of this symbol
* @param slot bytecode slot for this symbol
*/
- protected Symbol(final String name, final int flags, final Node node, final Block block, final Type type, final int slot) {
+ protected Symbol(final String name, final int flags, final Type type, final int slot) {
this.name = name;
this.flags = flags;
- this.node = node;
- this.block = block;
this.type = type;
this.slot = slot;
this.fieldIndex = -1;
@@ -142,11 +136,9 @@ public final class Symbol implements Comparable<Symbol> {
*
* @param name name of symbol
* @param flags symbol flags
- * @param node node this symbol is in
- * @param block block this symbol is in
*/
- public Symbol(final String name, final int flags, final Node node, final Block block) {
- this(name, flags, node, block, Type.UNKNOWN, -1);
+ public Symbol(final String name, final int flags) {
+ this(name, flags, Type.UNKNOWN, -1);
}
/**
@@ -157,7 +149,7 @@ public final class Symbol implements Comparable<Symbol> {
* @param type type of this symbol
*/
public Symbol(final String name, final int flags, final Type type) {
- this(name, flags, null, null, type, -1);
+ this(name, flags, type, -1);
}
private static String align(final String string, final int max) {
@@ -269,20 +261,6 @@ public final class Symbol implements Comparable<Symbol> {
return type.isCategory2() ? 2 : 1;
}
- @Override
- public boolean equals(final Object other) {
- if (!(other instanceof Symbol)) {
- return false;
- }
- final Symbol symbol = (Symbol) other;
- return name.equals(symbol.name) && block.equals(symbol.block);
- }
-
- @Override
- public int hashCode() {
- return name.hashCode() ^ block.hashCode();
- }
-
private static String type(final String desc) {
switch (desc.charAt(desc.length() - 1)) {
case ';':
@@ -371,14 +349,14 @@ public final class Symbol implements Comparable<Symbol> {
/**
* Flag this symbol as scope as described in {@link Symbol#isScope()}
*/
- public void setIsScope() {
+ /**
+ * Flag this symbol as scope as described in {@link Symbol#isScope()}
+ */
+ public void setIsScope() {
if (!isScope()) {
trace("SET IS SCOPE");
}
flags |= IS_SCOPE;
- if(!isGlobal()) {
- getBlock().setNeedsScope();
- }
}
/**
@@ -478,11 +456,11 @@ public final class Symbol implements Comparable<Symbol> {
}
/**
- * Get the block in which the symbol is defined
- * @return a block
+ * Flag this symbol as a function's self-referencing symbol.
+ * @return true if this symbol as a function's self-referencing symbol.
*/
- public Block getBlock() {
- return block;
+ public boolean isFunctionSelf() {
+ return (flags & IS_FUNCTION_SELF) == IS_FUNCTION_SELF;
}
/**
@@ -492,7 +470,7 @@ public final class Symbol implements Comparable<Symbol> {
* @return field index
*/
public int getFieldIndex() {
- assert fieldIndex != -1 : "fieldIndex must be initialized";
+ assert fieldIndex != -1 : "fieldIndex must be initialized " + fieldIndex;
return fieldIndex;
}
@@ -503,7 +481,6 @@ public final class Symbol implements Comparable<Symbol> {
* @param fieldIndex field index - a positive integer
*/
public void setFieldIndex(final int fieldIndex) {
- assert this.fieldIndex == -1 : "fieldIndex must be initialized only once";
this.fieldIndex = fieldIndex;
}
@@ -524,22 +501,6 @@ public final class Symbol implements Comparable<Symbol> {
}
/**
- * Get the node this symbol stores the result for
- * @return node
- */
- public Node getNode() {
- return node;
- }
-
- /**
- * Set the node this symbol stores the result for
- * @param node node
- */
- public void setNode(final Node node) {
- this.node = node;
- }
-
- /**
* Get the name of this symbol
* @return symbol name
*/
@@ -616,18 +577,25 @@ public final class Symbol implements Comparable<Symbol> {
}
/**
- * Check if this symbol is in the global scope, i.e. it is on the outermost level
- * in the script
- * @return true if this this is a global scope symbol
+ * From a lexical context, set this symbol as needing scope, which
+ * will set flags for the defining block that will be written when
+ * block is popped from the lexical context stack, used by codegen
+ * when flags need to be tagged, but block is in the
+ * middle of evaluation and cannot be modified.
+ *
+ * @param lc lexical context
+ * @param symbol symbol
*/
- public boolean isTopLevel() {
- return block instanceof FunctionNode && ((FunctionNode) block).isProgram();
+ public static void setSymbolIsScope(final LexicalContext lc, final Symbol symbol) {
+ symbol.setIsScope();
+ if (!symbol.isGlobal()) {
+ lc.setFlag(lc.getDefiningBlock(symbol), Block.NEEDS_SCOPE);
+ }
}
-
private void trace(final String desc) {
if (TRACE_SYMBOLS != null && (TRACE_SYMBOLS.isEmpty() || TRACE_SYMBOLS.contains(name))) {
- Context.err("SYMBOL: '" + name + "' " + desc);
+ Context.err(Debug.id(this) + " SYMBOL: '" + name + "' " + desc);
if (TRACE_SYMBOLS_STACKTRACE != null && (TRACE_SYMBOLS_STACKTRACE.isEmpty() || TRACE_SYMBOLS_STACKTRACE.contains(name))) {
new Throwable().printStackTrace(Context.getCurrentErr());
}
diff --git a/src/jdk/nashorn/internal/ir/TernaryNode.java b/src/jdk/nashorn/internal/ir/TernaryNode.java
index de333851..e2ccdb91 100644
--- a/src/jdk/nashorn/internal/ir/TernaryNode.java
+++ b/src/jdk/nashorn/internal/ir/TernaryNode.java
@@ -25,15 +25,21 @@
package jdk.nashorn.internal.ir;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
/**
* TernaryNode nodes represent three operand operations (?:).
*/
-public class TernaryNode extends BinaryNode {
+@Immutable
+public final class TernaryNode extends Node {
+ private final Node lhs;
+
+ private final Node rhs;
+
/** Third argument. */
- private Node third;
+ private final Node third;
/**
* Constructor
@@ -45,43 +51,26 @@ public class TernaryNode extends BinaryNode {
* @param third third node
*/
public TernaryNode(final Source source, final long token, final Node lhs, final Node rhs, final Node third) {
- super(source, token, lhs, rhs);
-
- this.finish = third.getFinish();
+ super(source, token, third.getFinish());
+ this.lhs = lhs;
+ this.rhs = rhs;
this.third = third;
}
- private TernaryNode(final TernaryNode ternaryNode, final CopyState cs) {
- super(ternaryNode, cs);
-
- this.third = cs.existingOrCopy(ternaryNode.third);
- }
-
- @Override
- protected Node copy(final CopyState cs) {
- return new TernaryNode(this, cs);
- }
-
- @Override
- public boolean equals(final Object other) {
- if (!super.equals(other)) {
- return false;
- }
- return third.equals(((TernaryNode)other).third());
- }
-
- @Override
- public int hashCode() {
- return super.hashCode() ^ third().hashCode();
+ private TernaryNode(final TernaryNode ternaryNode, final Node lhs, final Node rhs, final Node third) {
+ super(ternaryNode);
+ this.lhs = lhs;
+ this.rhs = rhs;
+ this.third = third;
}
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterTernaryNode(this) != null) {
+ if (visitor.enterTernaryNode(this)) {
final Node newLhs = lhs().accept(visitor);
final Node newRhs = rhs().accept(visitor);
final Node newThird = third.accept(visitor);
- return visitor.leaveTernaryNode((TernaryNode)setThird(newThird).setLHS(newLhs).setRHS(newRhs));
+ return visitor.leaveTernaryNode(setThird(newThird).setLHS(newLhs).setRHS(newRhs));
}
return this;
@@ -123,6 +112,22 @@ public class TernaryNode extends BinaryNode {
}
/**
+ * Get the lhs node for this ternary expression, i.e. "x" in x ? y : z
+ * @return a node
+ */
+ public Node lhs() {
+ return lhs;
+ }
+
+ /**
+ * Get the rhs node for this ternary expression, i.e. "y" in x ? y : z
+ * @return a node
+ */
+ public Node rhs() {
+ return rhs;
+ }
+
+ /**
* Get the "third" node for this ternary expression, i.e. "z" in x ? y : z
* @return a node
*/
@@ -131,14 +136,38 @@ public class TernaryNode extends BinaryNode {
}
/**
+ * Set the left hand side expression for this node
+ * @param lhs new left hand side expression
+ * @return a node equivalent to this one except for the requested change.
+ */
+ public TernaryNode setLHS(final Node lhs) {
+ if (this.lhs == lhs) {
+ return this;
+ }
+ return new TernaryNode(this, lhs, rhs, third);
+ }
+
+ /**
+ * Set the right hand side expression for this node
+ * @param rhs new left hand side expression
+ * @return a node equivalent to this one except for the requested change.
+ */
+ public TernaryNode setRHS(final Node rhs) {
+ if (this.rhs == rhs) {
+ return this;
+ }
+ return new TernaryNode(this, lhs, rhs, third);
+ }
+
+ /**
* Reset the "third" node for this ternary expression, i.e. "z" in x ? y : z
* @param third a node
* @return a node equivalent to this one except for the requested change.
*/
public TernaryNode setThird(final Node third) {
- if(this.third == third) return this;
- final TernaryNode n = (TernaryNode)clone();
- n.third = third;
- return n;
+ if (this.third == third) {
+ return this;
+ }
+ return new TernaryNode(this, lhs, rhs, third);
}
}
diff --git a/src/jdk/nashorn/internal/ir/ThrowNode.java b/src/jdk/nashorn/internal/ir/ThrowNode.java
index ab6d59e2..7a10a6ad 100644
--- a/src/jdk/nashorn/internal/ir/ThrowNode.java
+++ b/src/jdk/nashorn/internal/ir/ThrowNode.java
@@ -25,20 +25,17 @@
package jdk.nashorn.internal.ir;
-import jdk.nashorn.internal.ir.annotations.Ignore;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
/**
* IR representation for THROW statements.
*/
-public class ThrowNode extends Node {
+@Immutable
+public final class ThrowNode extends Node {
/** Exception expression. */
- private Node expression;
-
- /** Try chain. */
- @Ignore
- private final TryNode tryChain;
+ private final Node expression;
/**
* Constructor
@@ -47,26 +44,21 @@ public class ThrowNode extends Node {
* @param token token
* @param finish finish
* @param expression expression to throw
- * @param tryChain surrounding try chain
*/
- public ThrowNode(final Source source, final long token, final int finish, final Node expression, final TryNode tryChain) {
+ public ThrowNode(final Source source, final long token, final int finish, final Node expression) {
super(source, token, finish);
this.expression = expression;
- this.tryChain = tryChain;
- setIsTerminal(true);
}
- private ThrowNode(final ThrowNode throwNode, final CopyState cs) {
- super(throwNode);
-
- this.expression = cs.existingOrCopy(throwNode.expression);
- this.tryChain = (TryNode)cs.existingOrSame(throwNode.tryChain);
+ private ThrowNode(final Node node, final Node expression) {
+ super(node);
+ this.expression = expression;
}
@Override
- protected Node copy(final CopyState cs) {
- return new ThrowNode(this, cs);
+ public boolean isTerminal() {
+ return true;
}
/**
@@ -75,9 +67,8 @@ public class ThrowNode extends Node {
*/
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterThrowNode(this) != null) {
- setExpression(expression.accept(visitor));
- return visitor.leaveThrowNode(this);
+ if (visitor.enterThrowNode(this)) {
+ return visitor.leaveThrowNode(setExpression(expression.accept(visitor)));
}
return this;
@@ -103,16 +94,13 @@ public class ThrowNode extends Node {
/**
* Reset the expression being thrown by this node
* @param expression new expression
+ * @return new or same thrownode
*/
- public void setExpression(final Node expression) {
- this.expression = expression;
+ public ThrowNode setExpression(final Node expression) {
+ if (this.expression == expression) {
+ return this;
+ }
+ return new ThrowNode(this, expression);
}
- /**
- * Get surrounding tryChain for this node
- * @return try chain
- */
- public TryNode getTryChain() {
- return tryChain;
- }
}
diff --git a/src/jdk/nashorn/internal/ir/TryNode.java b/src/jdk/nashorn/internal/ir/TryNode.java
index 7d3864bc..5e3ff7af 100644
--- a/src/jdk/nashorn/internal/ir/TryNode.java
+++ b/src/jdk/nashorn/internal/ir/TryNode.java
@@ -28,30 +28,28 @@ package jdk.nashorn.internal.ir;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+
import jdk.nashorn.internal.codegen.Label;
-import jdk.nashorn.internal.ir.annotations.Ignore;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
/**
* IR representation of a TRY statement.
*/
-public class TryNode extends Node {
- /** Try chain. */
- @Ignore //don't print, will be apparent from the AST
- private TryNode next;
-
+@Immutable
+public final class TryNode extends Node {
/** Try statements. */
- private Block body;
+ private final Block body;
/** List of catch clauses. */
- private List<Block> catchBlocks;
+ private final List<Block> catchBlocks;
/** Finally clause. */
- private Block finallyBody;
+ private final Block finallyBody;
/** Exit label. */
- private Label exit;
+ private final Label exit;
/** Exception symbol. */
private Symbol exception;
@@ -62,37 +60,46 @@ public class TryNode extends Node {
/**
* Constructor
*
- * @param source the source
- * @param token token
- * @param finish finish
- * @param next next try node in chain
- */
- public TryNode(final Source source, final long token, final int finish, final TryNode next) {
+ * @param source the source
+ * @param token token
+ * @param finish finish
+ * @param body try node body
+ * @param catchBlocks list of catch blocks in order
+ * @param finallyBody body of finally block or null if none
+ */
+ public TryNode(final Source source, final long token, final int finish, final Block body, final List<Block> catchBlocks, final Block finallyBody) {
super(source, token, finish);
-
- this.next = next;
+ this.body = body;
+ this.catchBlocks = catchBlocks;
+ this.finallyBody = finallyBody;
this.exit = new Label("exit");
}
- private TryNode(final TryNode tryNode, final CopyState cs) {
+ private TryNode(final TryNode tryNode, final Block body, final List<Block> catchBlocks, final Block finallyBody) {
super(tryNode);
+ this.body = body;
+ this.catchBlocks = catchBlocks;
+ this.finallyBody = finallyBody;
+ this.exit = new Label(tryNode.exit);
+ }
- final List<Block> newCatchBlocks = new ArrayList<>();
-
- for (final Block block : tryNode.catchBlocks) {
- newCatchBlocks.add((Block)cs.existingOrCopy(block));
- }
-
- this.next = (TryNode)cs.existingOrSame(tryNode.getNext());
- this.body = (Block)cs.existingOrCopy(tryNode.getBody());
- this.catchBlocks = newCatchBlocks;
- this.finallyBody = (Block)cs.existingOrCopy(tryNode.getFinallyBody());
- this.exit = new Label(tryNode.getExit());
+ @Override
+ public Node ensureUniqueLabels(final LexicalContext lc) {
+ //try nodes are never in lex context
+ return new TryNode(this, body, catchBlocks, finallyBody);
}
@Override
- protected Node copy(final CopyState cs) {
- return new TryNode(this, cs);
+ public boolean isTerminal() {
+ if (body.isTerminal()) {
+ for (final Block catchBlock : getCatchBlocks()) {
+ if (!catchBlock.isTerminal()) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
}
/**
@@ -101,21 +108,16 @@ public class TryNode extends Node {
*/
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterTryNode(this) != null) {
- // Need to do first for termination analysis.
- if (finallyBody != null) {
- finallyBody = (Block)finallyBody.accept(visitor);
- }
-
- body = (Block)body.accept(visitor);
-
- final List<Block> newCatchBlocks = new ArrayList<>(catchBlocks.size());
- for (final Block catchBlock : catchBlocks) {
- newCatchBlocks.add((Block)catchBlock.accept(visitor));
- }
- this.catchBlocks = newCatchBlocks;
-
- return visitor.leaveTryNode(this);
+ if (visitor.enterTryNode(this)) {
+ // Need to do finallybody first for termination analysis. TODO still necessary?
+ final Block newFinallyBody = finallyBody == null ? null : (Block)finallyBody.accept(visitor);
+ final Block newBody = (Block)body.accept(visitor);
+ return visitor.leaveTryNode(
+ setBody(newBody).
+ setFinallyBody(newFinallyBody).
+ setCatchBlocks(Node.accept(visitor, Block.class, catchBlocks)).
+ setException(exception).
+ setFinallyCatchAll(finallyCatchAll));
}
return this;
@@ -123,7 +125,7 @@ public class TryNode extends Node {
@Override
public void toString(final StringBuilder sb) {
- sb.append("try");
+ sb.append("try ");
}
/**
@@ -137,9 +139,13 @@ public class TryNode extends Node {
/**
* Reset the body of this try block
* @param body new body
+ * @return new TryNode or same if unchanged
*/
- public void setBody(final Block body) {
- this.body = body;
+ public TryNode setBody(final Block body) {
+ if (this.body == body) {
+ return this;
+ }
+ return new TryNode(this, body, catchBlocks, finallyBody);
}
/**
@@ -151,16 +157,7 @@ public class TryNode extends Node {
for (final Block catchBlock : catchBlocks) {
catches.add((CatchNode)catchBlock.getStatements().get(0));
}
- return catches;
- }
-
- /**
- * Returns true if the specified block is the body of this try block, or any of its catch blocks.
- * @param block the block
- * @return true if the specified block is the body of this try block, or any of its catch blocks.
- */
- public boolean isChildBlock(Block block) {
- return body == block || catchBlocks.contains(block);
+ return Collections.unmodifiableList(catches);
}
/**
@@ -174,9 +171,13 @@ public class TryNode extends Node {
/**
* Set the catch blocks of this try
* @param catchBlocks list of catch blocks
+ * @return new TryNode or same if unchanged
*/
- public void setCatchBlocks(final List<Block> catchBlocks) {
- this.catchBlocks = catchBlocks;
+ public TryNode setCatchBlocks(final List<Block> catchBlocks) {
+ if (this.catchBlocks == catchBlocks) {
+ return this;
+ }
+ return new TryNode(this, body, catchBlocks, finallyBody);
}
/**
@@ -190,9 +191,11 @@ public class TryNode extends Node {
/**
* Set the exception symbol for this try block
* @param exception a symbol for the compiler to store the exception in
+ * @return new TryNode or same if unchanged
*/
- public void setException(final Symbol exception) {
+ public TryNode setException(final Symbol exception) {
this.exception = exception;
+ return this;
}
/**
@@ -207,9 +210,13 @@ public class TryNode extends Node {
* If a finally block exists, the synthetic catchall needs another symbol to
* store its throwable
* @param finallyCatchAll a symbol for the finally catch all exception
+ * @return new TryNode or same if unchanged
+ *
+ * TODO can this still be stateful?
*/
- public void setFinallyCatchAll(final Symbol finallyCatchAll) {
+ public TryNode setFinallyCatchAll(final Symbol finallyCatchAll) {
this.finallyCatchAll = finallyCatchAll;
+ return this;
}
/**
@@ -221,14 +228,6 @@ public class TryNode extends Node {
}
/**
- * Set the exit label for this try block
- * @param exit label
- */
- public void setExit(final Label exit) {
- this.exit = exit;
- }
-
- /**
* Get the body of the finally clause for this try
* @return finally body, or null if no finally
*/
@@ -239,24 +238,12 @@ public class TryNode extends Node {
/**
* Set the finally body of this try
* @param finallyBody new finally body
+ * @return new TryNode or same if unchanged
*/
- public void setFinallyBody(final Block finallyBody) {
- this.finallyBody = finallyBody;
- }
-
- /**
- * Get next try node in try chain
- * @return next try node
- */
- public TryNode getNext() {
- return next;
- }
-
- /**
- * Set next try node in try chain
- * @param next next try node
- */
- public void setNext(final TryNode next) {
- this.next = next;
+ public TryNode setFinallyBody(final Block finallyBody) {
+ if (this.finallyBody == finallyBody) {
+ return this;
+ }
+ return new TryNode(this, body, catchBlocks, finallyBody);
}
}
diff --git a/src/jdk/nashorn/internal/ir/UnaryNode.java b/src/jdk/nashorn/internal/ir/UnaryNode.java
index d823c058..fed5e408 100644
--- a/src/jdk/nashorn/internal/ir/UnaryNode.java
+++ b/src/jdk/nashorn/internal/ir/UnaryNode.java
@@ -31,6 +31,7 @@ import static jdk.nashorn.internal.parser.TokenType.DECPOSTFIX;
import static jdk.nashorn.internal.parser.TokenType.INCPOSTFIX;
import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.Token;
import jdk.nashorn.internal.parser.TokenType;
@@ -39,9 +40,10 @@ import jdk.nashorn.internal.runtime.Source;
/**
* UnaryNode nodes represent single operand operations.
*/
-public class UnaryNode extends Node implements Assignment<Node> {
+@Immutable
+public final class UnaryNode extends Node implements Assignment<Node> {
/** Right hand side argument. */
- private Node rhs;
+ private final Node rhs;
/**
* Constructor
@@ -51,23 +53,26 @@ public class UnaryNode extends Node implements Assignment<Node> {
* @param rhs expression
*/
public UnaryNode(final Source source, final long token, final Node rhs) {
- super(source, token, Token.descPosition(token));
-
- this.start = Math.min(rhs.getStart(), Token.descPosition(token));
- this.finish = Math.max(Token.descPosition(token) + Token.descLength(token), rhs.getFinish());
- this.rhs = rhs;
+ this(source, token, Math.min(rhs.getStart(), Token.descPosition(token)), Math.max(Token.descPosition(token) + Token.descLength(token), rhs.getFinish()), rhs);
}
/**
- * Copy constructor
- *
- * @param unaryNode source node
- * @param cs copy state
+ * Constructor
+ * @param source the source
+ * @param token token
+ * @param start start
+ * @param finish finish
+ * @param rhs expression
*/
- protected UnaryNode(final UnaryNode unaryNode, final CopyState cs) {
- super(unaryNode);
+ public UnaryNode(final Source source, final long token, final int start, final int finish, final Node rhs) {
+ super(source, token, start, finish);
+ this.rhs = rhs;
+ }
+
- this.rhs = cs.existingOrCopy(unaryNode.rhs);
+ private UnaryNode(final UnaryNode unaryNode, final Node rhs) {
+ super(unaryNode);
+ this.rhs = rhs;
}
/**
@@ -113,31 +118,13 @@ public class UnaryNode extends Node implements Assignment<Node> {
return getAssignmentDest();
}
- @Override
- public boolean equals(final Object other) {
- if (!super.equals(other)) {
- return false;
- }
- return rhs.equals(((UnaryNode)other).rhs());
- }
-
- @Override
- public int hashCode() {
- return super.hashCode() ^ rhs().hashCode();
- }
-
- @Override
- protected Node copy(final CopyState cs) {
- return new UnaryNode(this, cs);
- }
-
/**
* Assist in IR navigation.
* @param visitor IR navigating visitor.
*/
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterUnaryNode(this) != null) {
+ if (visitor.enterUnaryNode(this)) {
return visitor.leaveUnaryNode(setRHS(rhs.accept(visitor)));
}
@@ -219,9 +206,9 @@ public class UnaryNode extends Node implements Assignment<Node> {
* @return a node equivalent to this one except for the requested change.
*/
public UnaryNode setRHS(final Node rhs) {
- if(this.rhs == rhs) return this;
- final UnaryNode n = (UnaryNode)clone();
- n.rhs = rhs;
- return n;
+ if (this.rhs == rhs) {
+ return this;
+ }
+ return new UnaryNode(this, rhs);
}
}
diff --git a/src/jdk/nashorn/internal/ir/VarNode.java b/src/jdk/nashorn/internal/ir/VarNode.java
index b719c99d..fbc3eabe 100644
--- a/src/jdk/nashorn/internal/ir/VarNode.java
+++ b/src/jdk/nashorn/internal/ir/VarNode.java
@@ -25,21 +25,31 @@
package jdk.nashorn.internal.ir;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
/**
* Node represents a var/let declaration.
*/
-public class VarNode extends Node implements Assignment<IdentNode> {
+@Immutable
+public final class VarNode extends Node implements Assignment<IdentNode> {
/** Var name. */
- private IdentNode name;
+ private final IdentNode name;
/** Initialization expression. */
- private Node init;
+ private final Node init;
/** Is this a var statement (as opposed to a "var" in a for loop statement) */
- private final boolean isStatement;
+ private final int flags;
+
+ /** Flag that determines if this function node is a statement */
+ public static final int IS_STATEMENT = 1 << 0;
+
+ /** Flag that determines if this is the last function declaration in a function
+ * This is used to micro optimize the placement of return value assignments for
+ * a program node */
+ public static final int IS_LAST_FUNCTION_DECLARATION = 1 << 1;
/**
* Constructor
@@ -51,7 +61,14 @@ public class VarNode extends Node implements Assignment<IdentNode> {
* @param init init node or null if just a declaration
*/
public VarNode(final Source source, final long token, final int finish, final IdentNode name, final Node init) {
- this(source, token, finish, name, init, true);
+ this(source, token, finish, name, init, IS_STATEMENT);
+ }
+
+ private VarNode(final VarNode varNode, final IdentNode name, final Node init, final int flags) {
+ super(varNode);
+ this.name = init == null ? name : name.setIsInitializedHere();
+ this.init = init;
+ this.flags = flags;
}
/**
@@ -62,28 +79,14 @@ public class VarNode extends Node implements Assignment<IdentNode> {
* @param finish finish
* @param name name of variable
* @param init init node or null if just a declaration
- * @param isStatement if this is a var statement (true), or a for-loop initializer (false)
+ * @param flags flags
*/
- public VarNode(final Source source, final long token, final int finish, final IdentNode name, final Node init, boolean isStatement) {
+ public VarNode(final Source source, final long token, final int finish, final IdentNode name, final Node init, final int flags) {
super(source, token, finish);
this.name = init == null ? name : name.setIsInitializedHere();
this.init = init;
- this.isStatement = isStatement;
- }
-
-
- private VarNode(final VarNode varNode, final CopyState cs) {
- super(varNode);
-
- this.name = (IdentNode)cs.existingOrCopy(varNode.name);
- this.init = cs.existingOrCopy(varNode.init);
- this.isStatement = varNode.isStatement;
- }
-
- @Override
- protected Node copy(final CopyState cs) {
- return new VarNode(this, cs);
+ this.flags = flags;
}
@Override
@@ -115,45 +118,17 @@ public class VarNode extends Node implements Assignment<IdentNode> {
}
/**
- * Test to see if two VarNodes are the same.
- * @param other Other VarNode.
- * @return True if the VarNodes are the same.
- */
- @Override
- public boolean equals(final Object other) {
- if (other instanceof VarNode) {
- final VarNode otherNode = (VarNode)other;
- final boolean nameMatches = name.equals(otherNode.name);
- if (hasInit() != otherNode.hasInit()) {
- return false;
- } else if (init == null) {
- return nameMatches;
- } else {
- return nameMatches && init.equals(otherNode.init);
- }
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return name.hashCode() ^ (init == null ? 0 : init.hashCode());
- }
-
- /**
* Assist in IR navigation.
* @param visitor IR navigating visitor.
*/
@Override
public Node accept(final NodeVisitor visitor) {
- if (visitor.enterVarNode(this) != null) {
+ if (visitor.enterVarNode(this)) {
final IdentNode newName = (IdentNode)name.accept(visitor);
- final Node newInit = init == null ? null : init.accept(visitor);
- final VarNode newThis;
- if(name != newName || init != newInit) {
- newThis = (VarNode)clone();
- newThis.init = newInit;
- newThis.name = newInit == null ? newName : newName.setIsInitializedHere();
+ final Node newInit = init == null ? null : init.accept(visitor);
+ final VarNode newThis;
+ if (name != newName || init != newInit) {
+ newThis = new VarNode(this, newName, newInit, flags);
} else {
newThis = this;
}
@@ -187,10 +162,10 @@ public class VarNode extends Node implements Assignment<IdentNode> {
* @return a node equivalent to this one except for the requested change.
*/
public VarNode setInit(final Node init) {
- if(this.init == init) return this;
- final VarNode n = (VarNode)clone();
- n.init = init;
- return n;
+ if (this.init == init) {
+ return this;
+ }
+ return new VarNode(this, name, init, flags);
}
/**
@@ -204,12 +179,38 @@ public class VarNode extends Node implements Assignment<IdentNode> {
/**
* Reset the identifier for this VarNode
* @param name new IdentNode representing the variable being set or declared
+ * @return a node equivalent to this one except for the requested change.
+ */
+ public VarNode setName(final IdentNode name) {
+ if (this.name == name) {
+ return this;
+ }
+ return new VarNode(this, name, init, flags);
+ }
+
+ private VarNode setFlags(final int flags) {
+ if (this.flags == flags) {
+ return this;
+ }
+ return new VarNode(this, name, init, flags);
+ }
+
+ /**
+ * Check if a flag is set for this var node
+ * @param flag flag
+ * @return true if flag is set
+ */
+ public boolean getFlag(final int flag) {
+ return (flags & flag) == flag;
+ }
+
+ /**
+ * Set a flag for this var node
+ * @param flag flag
+ * @return new node if flags changed, same otherwise
*/
- private VarNode setName(final IdentNode name) {
- if(this.name == name) return this;
- final VarNode n = (VarNode)clone();
- n.name = name;
- return n;
+ public VarNode setFlag(final int flag) {
+ return setFlags(flags | flag);
}
/**
@@ -217,7 +218,7 @@ public class VarNode extends Node implements Assignment<IdentNode> {
* @return true if this is a var statement (as opposed to a var initializer in a for loop).
*/
public boolean isStatement() {
- return isStatement;
+ return (flags & IS_STATEMENT) != 0;
}
/**
diff --git a/src/jdk/nashorn/internal/ir/WhileNode.java b/src/jdk/nashorn/internal/ir/WhileNode.java
index 8db31c08..438be013 100644
--- a/src/jdk/nashorn/internal/ir/WhileNode.java
+++ b/src/jdk/nashorn/internal/ir/WhileNode.java
@@ -25,7 +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;
import jdk.nashorn.internal.runtime.Source;
@@ -33,130 +33,126 @@ import jdk.nashorn.internal.runtime.Source;
* IR representation for a WHILE statement. This is the superclass of all
* loop nodes
*/
-public class WhileNode extends BreakableNode {
- /** Test expression. */
- protected Node test;
+@Immutable
+public final class WhileNode extends LoopNode {
- /** For body. */
- protected Block body;
-
- /** loop continue label. */
- protected Label continueLabel;
+ /** is this a do while node ? */
+ private final boolean isDoWhile;
/**
* Constructor
*
- * @param source the source
- * @param token token
- * @param finish finish
+ * @param source the source
+ * @param token token
+ * @param finish finish
+ * @param isDoWhile is this a do while loop?
*/
- public WhileNode(final Source source, final long token, final int finish) {
- super(source, token, finish);
-
- this.breakLabel = new Label("while_break");
- this.continueLabel = new Label("while_continue");
+ public WhileNode(final Source source, final long token, final int finish, final boolean isDoWhile) {
+ super(source, token, finish, null, null, false);
+ this.isDoWhile = isDoWhile;
}
/**
- * Copy constructor
+ * Internal copy constructor
*
- * @param whileNode source node
- * @param cs copy state
+ * @param whileNode while node
+ * @param test test
+ * @param body body
+ * @param controlFlowEscapes control flow escapes?
*/
- protected WhileNode(final WhileNode whileNode, final CopyState cs) {
- super(whileNode);
-
- this.test = cs.existingOrCopy(whileNode.test);
- this.body = (Block)cs.existingOrCopy(whileNode.body);
- this.breakLabel = new Label(whileNode.breakLabel);
- this.continueLabel = new Label(whileNode.continueLabel);
+ protected WhileNode(final WhileNode whileNode, final Node test, final Block body, final boolean controlFlowEscapes) {
+ super(whileNode, test, body, controlFlowEscapes);
+ this.isDoWhile = whileNode.isDoWhile;
}
@Override
- protected Node copy(final CopyState cs) {
- return new WhileNode(this, cs);
+ public Node ensureUniqueLabels(final LexicalContext lc) {
+ return Node.replaceInLexicalContext(lc, this, new WhileNode(this, test, body, controlFlowEscapes));
}
@Override
- public boolean isLoop() {
- return true;
+ public boolean hasGoto() {
+ return test == null;
}
- /**
- * Assist in IR navigation.
- * @param visitor IR navigating visitor.
- */
@Override
- public Node accept(final NodeVisitor visitor) {
- if (visitor.enterWhileNode(this) != null) {
- test = test.accept(visitor);
- body = (Block)body.accept(visitor);
+ protected Node accept(final LexicalContext lc, final NodeVisitor visitor) {
+ if (visitor.enterWhileNode(this)) {
+ if (isDoWhile()) {
+ return visitor.leaveWhileNode(
+ setTest(lc, test.accept(visitor)).
+ setBody(lc, (Block)body.accept(visitor)));
+ }
+ return visitor.leaveWhileNode(
+ setBody(lc, (Block)body.accept(visitor)).
+ setTest(lc, test.accept(visitor)));
- return visitor.leaveWhileNode(this);
}
return this;
}
@Override
- public void toString(final StringBuilder sb) {
- sb.append("while (");
- test.toString(sb);
- sb.append(')');
+ public Node getTest() {
+ return test;
}
- /**
- * Get the loop body
- * @return body
- */
- public Block getBody() {
- return body;
+ @Override
+ public WhileNode setTest(final LexicalContext lc, final Node test) {
+ if (this.test == test) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new WhileNode(this, test, body, controlFlowEscapes));
}
- /**
- * Reset the loop body
- * @param body new body
- */
- public void setBody(final Block body) {
- this.body = body;
+ @Override
+ public Block getBody() {
+ return body;
}
- /**
- * Set the break label (described in {@link WhileNode#getBreakLabel()} for this while node
- * @param breakLabel break label
- */
- public void setBreakLabel(final Label breakLabel) {
- this.breakLabel = breakLabel;
+ @Override
+ public WhileNode setBody(final LexicalContext lc, final Block body) {
+ if (this.body == body) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new WhileNode(this, test, body, controlFlowEscapes));
}
- /**
- * Get the continue label for this while node, i.e. location to go to on continue
- * @return continue label
- */
- public Label getContinueLabel() {
- return continueLabel;
+ @Override
+ public WhileNode setControlFlowEscapes(final LexicalContext lc, final boolean controlFlowEscapes) {
+ if (this.controlFlowEscapes == controlFlowEscapes) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new WhileNode(this, test, body, controlFlowEscapes));
}
/**
- * Set the continue label (described in {@link WhileNode#getContinueLabel()} for this while node
- * @param continueLabel continue label
+ * Check if this is a do while loop or a normal while loop
+ * @return true if do while
*/
- public void setContinueLabel(final Label continueLabel) {
- this.continueLabel = continueLabel;
+ public boolean isDoWhile() {
+ return isDoWhile;
}
- /**
- * Get the test expression for this loop, that upon evaluation to true does another iteration
- * @return test expression
- */
- public Node getTest() {
- return test;
+ @Override
+ public void toString(final StringBuilder sb) {
+ if (isDoWhile()) {
+ sb.append("do {");
+ body.toString(sb);
+ sb.append("} while (");
+ test.toString(sb);
+ sb.append(')');
+ } else {
+ sb.append("while (");
+ test.toString(sb);
+ sb.append(')');
+ }
}
- /**
- * Set the test expression for this loop
- * @param test test expression, null if infinite loop
- */
- public void setTest(final Node test) {
- this.test = test;
+ @Override
+ public boolean mustEnter() {
+ if (isDoWhile()) {
+ return true;
+ }
+ return test == null;
}
}
diff --git a/src/jdk/nashorn/internal/ir/WithNode.java b/src/jdk/nashorn/internal/ir/WithNode.java
index f5ad3b13..5ebbfd55 100644
--- a/src/jdk/nashorn/internal/ir/WithNode.java
+++ b/src/jdk/nashorn/internal/ir/WithNode.java
@@ -25,18 +25,20 @@
package jdk.nashorn.internal.ir;
+import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
/**
* IR representation for {@code with} statements.
*/
-public class WithNode extends Node {
+@Immutable
+public final class WithNode extends LexicalContextNode {
/** This expression. */
- private Node expression;
+ private final Node expression;
/** Statements. */
- private Block body;
+ private final Block body;
/**
* Constructor
@@ -44,26 +46,19 @@ public class WithNode extends Node {
* @param source the source
* @param token token
* @param finish finish
- * @param expression expression in parenthesis
- * @param body with node body
*/
- public WithNode(final Source source, final long token, final int finish, final Node expression, final Block body) {
+ public WithNode(final Source source, final long token, final int finish) {
super(source, token, finish);
- this.expression = expression;
- this.body = body;
+ this.expression = null;
+ this.body = null;
}
- private WithNode(final WithNode withNode, final CopyState cs) {
- super(withNode);
-
- this.expression = cs.existingOrCopy(withNode.expression);
- this.body = (Block)cs.existingOrCopy(withNode.body);
- }
+ private WithNode(final WithNode node, final Node expression, final Block body) {
+ super(node);
- @Override
- protected Node copy(final CopyState cs) {
- return new WithNode(this, cs);
+ this.expression = expression;
+ this.body = body;
}
/**
@@ -72,17 +67,21 @@ public class WithNode extends Node {
* @param visitor IR navigating visitor.
*/
@Override
- public Node accept(final NodeVisitor visitor) {
- if (visitor.enterWithNode(this) != null) {
- expression = expression.accept(visitor);
- body = (Block)body.accept(visitor);
- return visitor.leaveWithNode(this);
+ public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
+ if (visitor.enterWithNode(this)) {
+ return visitor.leaveWithNode(
+ setExpression(lc, expression.accept(visitor)).
+ setBody(lc, (Block)body.accept(visitor)));
}
-
return this;
}
@Override
+ public boolean isTerminal() {
+ return body.isTerminal();
+ }
+
+ @Override
public void toString(final StringBuilder sb) {
sb.append("with (");
expression.toString(sb);
@@ -99,10 +98,15 @@ public class WithNode extends Node {
/**
* Reset the body of this with node
+ * @param lc lexical context
* @param body new body
+ * @return new or same withnode
*/
- public void setBody(final Block body) {
- this.body = body;
+ public WithNode setBody(final LexicalContext lc, final Block body) {
+ if (this.body == body) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new WithNode(this, expression, body));
}
/**
@@ -115,10 +119,15 @@ public class WithNode extends Node {
/**
* Reset the expression of this with node
+ * @param lc lexical context
* @param expression new expression
+ * @return new or same withnode
*/
- public void setExpression(final Node expression) {
- this.expression = expression;
+ public WithNode setExpression(final LexicalContext lc, final Node expression) {
+ if (this.expression == expression) {
+ return this;
+ }
+ return Node.replaceInLexicalContext(lc, this, new WithNode(this, expression, body));
}
}
diff --git a/src/jdk/nashorn/internal/ir/annotations/Immutable.java b/src/jdk/nashorn/internal/ir/annotations/Immutable.java
new file mode 100644
index 00000000..663abf7c
--- /dev/null
+++ b/src/jdk/nashorn/internal/ir/annotations/Immutable.java
@@ -0,0 +1,34 @@
+/*
+ * 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.ir.annotations;
+
+/**
+ * Tag for nodes that are immutable. To be immutable all fields must be
+ * final and copy on write semantics must be in place
+ */
+public @interface Immutable {
+ //empty
+}
diff --git a/src/jdk/nashorn/internal/ir/debug/ASTWriter.java b/src/jdk/nashorn/internal/ir/debug/ASTWriter.java
index 7bbe3836..5d9b5dfb 100644
--- a/src/jdk/nashorn/internal/ir/debug/ASTWriter.java
+++ b/src/jdk/nashorn/internal/ir/debug/ASTWriter.java
@@ -33,7 +33,9 @@ import java.util.Deque;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
+
import jdk.nashorn.internal.ir.BinaryNode;
+import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.TernaryNode;
import jdk.nashorn.internal.ir.annotations.Ignore;
@@ -113,6 +115,10 @@ public final class ASTWriter {
type += "#" + node.getSymbol();
}
+ if (node instanceof Block && ((Block)node).needsScope()) {
+ type += " <scope>";
+ }
+
final List<Field> children = new LinkedList<>();
if (!isReference) {
@@ -121,10 +127,6 @@ public final class ASTWriter {
String status = "";
- if (node.shouldDiscard()) {
- status += " Discard";
- }
-
if (node.isTerminal()) {
status += " Terminal";
}
diff --git a/src/jdk/nashorn/internal/ir/debug/JSONWriter.java b/src/jdk/nashorn/internal/ir/debug/JSONWriter.java
index a8c3c4a4..988b756c 100644
--- a/src/jdk/nashorn/internal/ir/debug/JSONWriter.java
+++ b/src/jdk/nashorn/internal/ir/debug/JSONWriter.java
@@ -36,7 +36,6 @@ import jdk.nashorn.internal.ir.CallNode;
import jdk.nashorn.internal.ir.CaseNode;
import jdk.nashorn.internal.ir.CatchNode;
import jdk.nashorn.internal.ir.ContinueNode;
-import jdk.nashorn.internal.ir.DoWhileNode;
import jdk.nashorn.internal.ir.EmptyNode;
import jdk.nashorn.internal.ir.ExecuteNode;
import jdk.nashorn.internal.ir.ForNode;
@@ -88,7 +87,7 @@ public final class JSONWriter extends NodeVisitor {
final Parser parser = new Parser(env, new Source(name, code), new Context.ThrowErrorManager(), env._strict);
final JSONWriter jsonWriter = new JSONWriter(includeLoc);
try {
- final FunctionNode functionNode = parser.parse(CompilerConstants.RUN_SCRIPT.tag());
+ final FunctionNode functionNode = parser.parse(CompilerConstants.RUN_SCRIPT.symbolName());
functionNode.accept(jsonWriter);
return jsonWriter.getString();
} catch (final ParserException e) {
@@ -98,11 +97,16 @@ public final class JSONWriter extends NodeVisitor {
}
@Override
- protected Node enterDefault(final Node node) {
+ protected boolean enterDefault(final Node node) {
objectStart();
location(node);
- return node;
+ return true;
+ }
+
+ private boolean leave() {
+ objectEnd();
+ return false;
}
@Override
@@ -112,7 +116,7 @@ public final class JSONWriter extends NodeVisitor {
}
@Override
- public Node enterAccessNode(final AccessNode accessNode) {
+ public boolean enterAccessNode(final AccessNode accessNode) {
enterDefault(accessNode);
type("MemberExpression");
@@ -128,11 +132,11 @@ public final class JSONWriter extends NodeVisitor {
property("computed", false);
- return leaveDefault(accessNode);
+ return leave();
}
@Override
- public Node enterBlock(final Block block) {
+ public boolean enterBlock(final Block block) {
enterDefault(block);
type("BlockStatement");
@@ -140,21 +144,21 @@ public final class JSONWriter extends NodeVisitor {
array("body", block.getStatements());
- return leaveDefault(block);
+ return leave();
}
private static boolean isLogical(final TokenType tt) {
switch (tt) {
- case AND:
- case OR:
- return true;
- default:
- return false;
+ case AND:
+ case OR:
+ return true;
+ default:
+ return false;
}
}
@Override
- public Node enterBinaryNode(final BinaryNode binaryNode) {
+ public boolean enterBinaryNode(final BinaryNode binaryNode) {
enterDefault(binaryNode);
final String name;
@@ -179,29 +183,29 @@ public final class JSONWriter extends NodeVisitor {
property("right");
binaryNode.rhs().accept(this);
- return leaveDefault(binaryNode);
+ return leave();
}
@Override
- public Node enterBreakNode(final BreakNode breakNode) {
+ public boolean enterBreakNode(final BreakNode breakNode) {
enterDefault(breakNode);
type("BreakStatement");
comma();
- final LabelNode label = breakNode.getLabel();
+ final IdentNode label = breakNode.getLabel();
if (label != null) {
- property("label", label.getLabel().getName());
+ property("label", label.getName());
} else {
property("label");
nullValue();
}
- return leaveDefault(breakNode);
+ return leave();
}
@Override
- public Node enterCallNode(final CallNode callNode) {
+ public boolean enterCallNode(final CallNode callNode) {
enterDefault(callNode);
type("CallExpression");
@@ -213,11 +217,11 @@ public final class JSONWriter extends NodeVisitor {
array("arguments", callNode.getArgs());
- return leaveDefault(callNode);
+ return leave();
}
@Override
- public Node enterCaseNode(final CaseNode caseNode) {
+ public boolean enterCaseNode(final CaseNode caseNode) {
enterDefault(caseNode);
type("SwitchCase");
@@ -234,11 +238,11 @@ public final class JSONWriter extends NodeVisitor {
array("consequent", caseNode.getBody().getStatements());
- return leaveDefault(caseNode);
+ return leave();
}
@Override
- public Node enterCatchNode(final CatchNode catchNode) {
+ public boolean enterCatchNode(final CatchNode catchNode) {
enterDefault(catchNode);
type("CatchClause");
@@ -260,55 +264,38 @@ public final class JSONWriter extends NodeVisitor {
property("body");
catchNode.getBody().accept(this);
- return leaveDefault(catchNode);
+ return leave();
}
@Override
- public Node enterContinueNode(final ContinueNode continueNode) {
+ public boolean enterContinueNode(final ContinueNode continueNode) {
enterDefault(continueNode);
type("ContinueStatement");
comma();
- final LabelNode label = continueNode.getLabel();
+ final IdentNode label = continueNode.getLabel();
if (label != null) {
- property("label", label.getLabel().getName());
+ property("label", label.getName());
} else {
property("label");
nullValue();
}
- return leaveDefault(continueNode);
+ return leave();
}
@Override
- public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
- enterDefault(doWhileNode);
-
- type("DoWhileStatement");
- comma();
-
- property("body");
- doWhileNode.getBody().accept(this);
- comma();
-
- property("test");
- doWhileNode.getTest().accept(this);
-
- return leaveDefault(doWhileNode);
- }
-
- @Override
- public Node enterEmptyNode(final EmptyNode emptyNode) {
+ public boolean enterEmptyNode(final EmptyNode emptyNode) {
enterDefault(emptyNode);
type("EmptyStatement");
- return leaveDefault(emptyNode);
+ return leave();
}
@Override
- public Node enterExecuteNode(final ExecuteNode executeNode) {
+ public boolean enterExecuteNode(final ExecuteNode executeNode) {
enterDefault(executeNode);
type("ExpressionStatement");
@@ -317,11 +304,11 @@ public final class JSONWriter extends NodeVisitor {
property("expression");
executeNode.getExpression().accept(this);
- return leaveDefault(executeNode);
+ return leave();
}
@Override
- public Node enterForNode(final ForNode forNode) {
+ public boolean enterForNode(final ForNode forNode) {
enterDefault(forNode);
if (forNode.isForIn() || (forNode.isForEach() && forNode.getInit() != null)) {
@@ -380,11 +367,11 @@ public final class JSONWriter extends NodeVisitor {
forNode.getBody().accept(this);
}
- return leaveDefault(forNode);
+ return leave();
}
@Override
- public Node enterFunctionNode(final FunctionNode functionNode) {
+ public boolean enterFunctionNode(final FunctionNode functionNode) {
enterDefault(functionNode);
final boolean program = functionNode.isProgram();
@@ -419,7 +406,7 @@ public final class JSONWriter extends NodeVisitor {
}
// body consists of nested functions and statements
- final List<Node> stats = functionNode.getStatements();
+ final List<Node> stats = functionNode.getBody().getStatements();
final int size = stats.size();
int idx = 0;
arrayStart("body");
@@ -435,11 +422,11 @@ public final class JSONWriter extends NodeVisitor {
}
arrayEnd();
- return leaveDefault(functionNode);
+ return leave();
}
@Override
- public Node enterIdentNode(final IdentNode identNode) {
+ public boolean enterIdentNode(final IdentNode identNode) {
enterDefault(identNode);
final String name = identNode.getName();
@@ -451,11 +438,11 @@ public final class JSONWriter extends NodeVisitor {
property("name", identNode.getName());
}
- return leaveDefault(identNode);
+ return leave();
}
@Override
- public Node enterIfNode(final IfNode ifNode) {
+ public boolean enterIfNode(final IfNode ifNode) {
enterDefault(ifNode);
type("IfStatement");
@@ -477,11 +464,11 @@ public final class JSONWriter extends NodeVisitor {
nullValue();
}
- return leaveDefault(ifNode);
+ return leave();
}
@Override
- public Node enterIndexNode(final IndexNode indexNode) {
+ public boolean enterIndexNode(final IndexNode indexNode) {
enterDefault(indexNode);
type("MemberExpression");
@@ -497,11 +484,11 @@ public final class JSONWriter extends NodeVisitor {
property("computed", true);
- return leaveDefault(indexNode);
+ return leave();
}
@Override
- public Node enterLabelNode(final LabelNode labelNode) {
+ public boolean enterLabelNode(final LabelNode labelNode) {
enterDefault(labelNode);
type("LabeledStatement");
@@ -514,17 +501,17 @@ public final class JSONWriter extends NodeVisitor {
property("body");
labelNode.getBody().accept(this);
- return leaveDefault(labelNode);
+ return leave();
}
@Override
- public Node enterLineNumberNode(final LineNumberNode lineNumberNode) {
- return null;
+ public boolean enterLineNumberNode(final LineNumberNode lineNumberNode) {
+ return false;
}
@SuppressWarnings("rawtypes")
@Override
- public Node enterLiteralNode(final LiteralNode literalNode) {
+ public boolean enterLiteralNode(final LiteralNode literalNode) {
enterDefault(literalNode);
if (literalNode instanceof LiteralNode.ArrayLiteralNode) {
@@ -556,11 +543,11 @@ public final class JSONWriter extends NodeVisitor {
}
}
- return leaveDefault(literalNode);
+ return leave();
}
@Override
- public Node enterObjectNode(final ObjectNode objectNode) {
+ public boolean enterObjectNode(final ObjectNode objectNode) {
enterDefault(objectNode);
type("ObjectExpression");
@@ -568,11 +555,11 @@ public final class JSONWriter extends NodeVisitor {
array("properties", objectNode.getElements());
- return leaveDefault(objectNode);
+ return leave();
}
@Override
- public Node enterPropertyNode(final PropertyNode propertyNode) {
+ public boolean enterPropertyNode(final PropertyNode propertyNode) {
final Node key = propertyNode.getKey();
final Node value = propertyNode.getValue();
@@ -634,11 +621,11 @@ public final class JSONWriter extends NodeVisitor {
}
}
- return null;
+ return false;
}
@Override
- public Node enterReturnNode(final ReturnNode returnNode) {
+ public boolean enterReturnNode(final ReturnNode returnNode) {
enterDefault(returnNode);
type("ReturnStatement");
@@ -652,31 +639,29 @@ public final class JSONWriter extends NodeVisitor {
nullValue();
}
- return leaveDefault(returnNode);
+ return leave();
}
@Override
- public Node enterRuntimeNode(final RuntimeNode runtimeNode) {
+ public boolean enterRuntimeNode(final RuntimeNode runtimeNode) {
final RuntimeNode.Request req = runtimeNode.getRequest();
if (req == RuntimeNode.Request.DEBUGGER) {
enterDefault(runtimeNode);
-
type("DebuggerStatement");
-
- return leaveDefault(runtimeNode);
+ return leave();
}
- return null;
+ return false;
}
@Override
- public Node enterSplitNode(final SplitNode splitNode) {
- return null;
+ public boolean enterSplitNode(final SplitNode splitNode) {
+ return false;
}
@Override
- public Node enterSwitchNode(final SwitchNode switchNode) {
+ public boolean enterSwitchNode(final SwitchNode switchNode) {
enterDefault(switchNode);
type("SwitchStatement");
@@ -688,11 +673,11 @@ public final class JSONWriter extends NodeVisitor {
array("cases", switchNode.getCases());
- return leaveDefault(switchNode);
+ return leave();
}
@Override
- public Node enterTernaryNode(final TernaryNode ternaryNode) {
+ public boolean enterTernaryNode(final TernaryNode ternaryNode) {
enterDefault(ternaryNode);
type("ConditionalExpression");
@@ -709,11 +694,11 @@ public final class JSONWriter extends NodeVisitor {
property("alternate");
ternaryNode.third().accept(this);
- return leaveDefault(ternaryNode);
+ return leave();
}
@Override
- public Node enterThrowNode(final ThrowNode throwNode) {
+ public boolean enterThrowNode(final ThrowNode throwNode) {
enterDefault(throwNode);
type("ThrowStatement");
@@ -722,11 +707,11 @@ public final class JSONWriter extends NodeVisitor {
property("argument");
throwNode.getExpression().accept(this);
- return leaveDefault(throwNode);
+ return leave();
}
@Override
- public Node enterTryNode(final TryNode tryNode) {
+ public boolean enterTryNode(final TryNode tryNode) {
enterDefault(tryNode);
type("TryStatement");
@@ -747,11 +732,11 @@ public final class JSONWriter extends NodeVisitor {
nullValue();
}
- return leaveDefault(tryNode);
+ return leave();
}
@Override
- public Node enterUnaryNode(final UnaryNode unaryNode) {
+ public boolean enterUnaryNode(final UnaryNode unaryNode) {
enterDefault(unaryNode);
final TokenType tokenType = unaryNode.tokenType();
@@ -769,25 +754,25 @@ public final class JSONWriter extends NodeVisitor {
final boolean prefix;
final String operator;
switch (tokenType) {
- case INCPOSTFIX:
- prefix = false;
- operator = "++";
- break;
- case DECPOSTFIX:
- prefix = false;
- operator = "--";
- break;
- case INCPREFIX:
- operator = "++";
- prefix = true;
- break;
- case DECPREFIX:
- operator = "--";
- prefix = true;
- break;
- default:
- prefix = false;
- operator = tokenType.getName();
+ case INCPOSTFIX:
+ prefix = false;
+ operator = "++";
+ break;
+ case DECPOSTFIX:
+ prefix = false;
+ operator = "--";
+ break;
+ case INCPREFIX:
+ operator = "++";
+ prefix = true;
+ break;
+ case DECPREFIX:
+ operator = "--";
+ prefix = true;
+ break;
+ default:
+ prefix = false;
+ operator = tokenType.getName();
}
type(unaryNode.isAssignment()? "UpdateExpression" : "UnaryExpression");
@@ -803,11 +788,11 @@ public final class JSONWriter extends NodeVisitor {
unaryNode.rhs().accept(this);
}
- return leaveDefault(unaryNode);
+ return leave();
}
@Override
- public Node enterVarNode(final VarNode varNode) {
+ public boolean enterVarNode(final VarNode varNode) {
enterDefault(varNode);
type("VariableDeclaration");
@@ -839,28 +824,37 @@ public final class JSONWriter extends NodeVisitor {
// declarations
arrayEnd();
- return leaveDefault(varNode);
+ return leave();
}
@Override
- public Node enterWhileNode(final WhileNode whileNode) {
+ public boolean enterWhileNode(final WhileNode whileNode) {
enterDefault(whileNode);
- type("WhileStatement");
+ type(whileNode.isDoWhile() ? "DoWhileStatement" : "WhileStatement");
comma();
- property("test");
- whileNode.getTest().accept(this);
- comma();
+ if (whileNode.isDoWhile()) {
+ property("body");
+ whileNode.getBody().accept(this);
+ comma();
- property("block");
- whileNode.getBody().accept(this);
+ property("test");
+ whileNode.getTest().accept(this);
+ } else {
+ property("test");
+ whileNode.getTest().accept(this);
+ comma();
+
+ property("block");
+ whileNode.getBody().accept(this);
+ }
- return leaveDefault(whileNode);
+ return leave();
}
@Override
- public Node enterWithNode(final WithNode withNode) {
+ public boolean enterWithNode(final WithNode withNode) {
enterDefault(withNode);
type("WithStatement");
@@ -873,8 +867,8 @@ public final class JSONWriter extends NodeVisitor {
property("body");
withNode.getBody().accept(this);
- return leaveDefault(withNode);
- }
+ return leave();
+ }
// Internals below
diff --git a/src/jdk/nashorn/internal/ir/debug/PrintVisitor.java b/src/jdk/nashorn/internal/ir/debug/PrintVisitor.java
index d2f40d1a..8637b66d 100644
--- a/src/jdk/nashorn/internal/ir/debug/PrintVisitor.java
+++ b/src/jdk/nashorn/internal/ir/debug/PrintVisitor.java
@@ -26,30 +26,22 @@
package jdk.nashorn.internal.ir.debug;
import java.util.List;
-import jdk.nashorn.internal.ir.AccessNode;
+
+import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.Block;
-import jdk.nashorn.internal.ir.BreakNode;
-import jdk.nashorn.internal.ir.CallNode;
import jdk.nashorn.internal.ir.CaseNode;
import jdk.nashorn.internal.ir.CatchNode;
-import jdk.nashorn.internal.ir.ContinueNode;
-import jdk.nashorn.internal.ir.DoWhileNode;
import jdk.nashorn.internal.ir.ExecuteNode;
import jdk.nashorn.internal.ir.ForNode;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.IfNode;
-import jdk.nashorn.internal.ir.IndexNode;
import jdk.nashorn.internal.ir.LabelNode;
import jdk.nashorn.internal.ir.LineNumberNode;
import jdk.nashorn.internal.ir.Node;
-import jdk.nashorn.internal.ir.ReturnNode;
-import jdk.nashorn.internal.ir.RuntimeNode;
import jdk.nashorn.internal.ir.SplitNode;
import jdk.nashorn.internal.ir.SwitchNode;
import jdk.nashorn.internal.ir.Symbol;
-import jdk.nashorn.internal.ir.ThrowNode;
import jdk.nashorn.internal.ir.TryNode;
-import jdk.nashorn.internal.ir.UnaryNode;
import jdk.nashorn.internal.ir.VarNode;
import jdk.nashorn.internal.ir.WhileNode;
import jdk.nashorn.internal.ir.WithNode;
@@ -136,21 +128,20 @@ public final class PrintVisitor extends NodeVisitor {
/*
* Visits.
*/
+
@Override
- public Node enterAccessNode(final AccessNode accessNode) {
- accessNode.toString(sb);
- return null;
+ public boolean enterDefault(final Node node) {
+ node.toString(sb);
+ return false;
}
@Override
- public Node enterBlock(final Block block) {
+ public boolean enterBlock(final Block block) {
sb.append(' ');
sb.append('{');
indent += TABWIDTH;
- final boolean isFunction = block instanceof FunctionNode;
-
final List<Node> statements = block.getStatements();
boolean lastLineNumber = false;
@@ -161,14 +152,14 @@ public final class PrintVisitor extends NodeVisitor {
indent();
}
- if (statement instanceof UnaryNode) {
- statement.toString(sb);
- } else {
- statement.accept(this);
- }
+ statement.accept(this);
lastLineNumber = statement instanceof LineNumberNode;
+ if (statement instanceof FunctionNode) {
+ continue;
+ }
+
final Symbol symbol = statement.getSymbol();
if (symbol != null) {
@@ -200,72 +191,42 @@ public final class PrintVisitor extends NodeVisitor {
indent();
sb.append("}");
- if (isFunction) {
- sb.append(EOLN);
- }
-
- return null;
- }
-
- @Override
- public Node enterBreakNode(final BreakNode breakNode) {
- breakNode.toString(sb);
- return null;
- }
-
- @Override
- public Node enterCallNode(final CallNode callNode) {
- callNode.toString(sb);
- return null;
+ return false;
}
@Override
- public Node enterContinueNode(final ContinueNode continueNode) {
- continueNode.toString(sb);
- return null;
- }
-
- @Override
- public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
- sb.append("do");
- doWhileNode.getBody().accept(this);
+ public boolean enterBinaryNode(final BinaryNode binaryNode) {
+ binaryNode.lhs().accept(this);
sb.append(' ');
- doWhileNode.toString(sb);
-
- return null;
+ sb.append(binaryNode.tokenType());
+ sb.append(' ');
+ binaryNode.rhs().accept(this);
+ return false;
}
@Override
- public Node enterExecuteNode(final ExecuteNode executeNode) {
- final Node expression = executeNode.getExpression();
-
- if (expression instanceof UnaryNode) {
- expression.toString(sb);
- } else {
- expression.accept(this);
- }
-
- return null;
+ public boolean enterExecuteNode(final ExecuteNode executeNode) {
+ executeNode.getExpression().accept(this);
+ return false;
}
@Override
- public Node enterForNode(final ForNode forNode) {
+ public boolean enterForNode(final ForNode forNode) {
forNode.toString(sb);
forNode.getBody().accept(this);
-
- return null;
+ return false;
}
@Override
- public Node enterFunctionNode(final FunctionNode functionNode) {
+ public boolean enterFunctionNode(final FunctionNode functionNode) {
functionNode.toString(sb);
- enterBlock(functionNode);
-
- return null;
+ enterBlock(functionNode.getBody());
+ sb.append(EOLN);
+ return false;
}
@Override
- public Node enterIfNode(final IfNode ifNode) {
+ public boolean enterIfNode(final IfNode ifNode) {
ifNode.toString(sb);
ifNode.getPass().accept(this);
@@ -276,55 +237,36 @@ public final class PrintVisitor extends NodeVisitor {
fail.accept(this);
}
- return null;
+ return false;
}
@Override
- public Node enterIndexNode(final IndexNode indexNode) {
- indexNode.toString(sb);
- return null;
- }
-
- @Override
- public Node enterLabelNode(final LabelNode labeledNode) {
+ public boolean enterLabelNode(final LabelNode labeledNode) {
indent -= TABWIDTH;
indent();
indent += TABWIDTH;
labeledNode.toString(sb);
labeledNode.getBody().accept(this);
- return null;
+ return false;
}
@Override
- public Node enterLineNumberNode(final LineNumberNode lineNumberNode) {
+ public boolean enterLineNumberNode(final LineNumberNode lineNumberNode) {
if (printLineNumbers) {
lineNumberNode.toString(sb);
}
- return null;
+ return false;
}
-
@Override
- public Node enterReturnNode(final ReturnNode returnNode) {
- returnNode.toString(sb);
- return null;
- }
-
- @Override
- public Node enterRuntimeNode(final RuntimeNode runtimeNode) {
- runtimeNode.toString(sb);
- return null;
- }
-
- @Override
- public Node enterSplitNode(final SplitNode splitNode) {
+ public boolean enterSplitNode(final SplitNode splitNode) {
splitNode.toString(sb);
sb.append(EOLN);
indent += TABWIDTH;
indent();
- return splitNode;
+ return true;
}
@Override
@@ -337,7 +279,7 @@ public final class PrintVisitor extends NodeVisitor {
}
@Override
- public Node enterSwitchNode(final SwitchNode switchNode) {
+ public boolean enterSwitchNode(final SwitchNode switchNode) {
switchNode.toString(sb);
sb.append(" {");
@@ -357,24 +299,18 @@ public final class PrintVisitor extends NodeVisitor {
indent();
sb.append("}");
- return null;
- }
-
- @Override
- public Node enterThrowNode(final ThrowNode throwNode) {
- throwNode.toString(sb);
- return null;
+ return false;
}
@Override
- public Node enterTryNode(final TryNode tryNode) {
+ public boolean enterTryNode(final TryNode tryNode) {
tryNode.toString(sb);
tryNode.getBody().accept(this);
final List<Block> catchBlocks = tryNode.getCatchBlocks();
for (final Block catchBlock : catchBlocks) {
- final CatchNode catchNode = (CatchNode) catchBlock.getStatements().get(0);
+ final CatchNode catchNode = (CatchNode)catchBlock.getStatements().get(0);
catchNode.toString(sb);
catchNode.getBody().accept(this);
}
@@ -386,35 +322,42 @@ public final class PrintVisitor extends NodeVisitor {
finallyBody.accept(this);
}
- return null;
+ return false;
}
@Override
- public Node enterVarNode(final VarNode varNode) {
+ public boolean enterVarNode(final VarNode varNode) {
sb.append("var ");
varNode.getName().toString(sb);
final Node init = varNode.getInit();
- if(init != null) {
+ if (init != null) {
sb.append(" = ");
init.accept(this);
}
- return null;
+ return false;
}
@Override
- public Node enterWhileNode(final WhileNode whileNode) {
- whileNode.toString(sb);
- whileNode.getBody().accept(this);
+ public boolean enterWhileNode(final WhileNode whileNode) {
+ if (whileNode.isDoWhile()) {
+ sb.append("do");
+ whileNode.getBody().accept(this);
+ sb.append(' ');
+ whileNode.toString(sb);
+ } else {
+ whileNode.toString(sb);
+ whileNode.getBody().accept(this);
+ }
- return null;
+ return false;
}
@Override
- public Node enterWithNode(final WithNode withNode) {
+ public boolean enterWithNode(final WithNode withNode) {
withNode.toString(sb);
withNode.getBody().accept(this);
- return null;
+ return false;
}
}
diff --git a/src/jdk/nashorn/internal/ir/visitor/NodeOperatorVisitor.java b/src/jdk/nashorn/internal/ir/visitor/NodeOperatorVisitor.java
index 0021b7d2..4f128412 100644
--- a/src/jdk/nashorn/internal/ir/visitor/NodeOperatorVisitor.java
+++ b/src/jdk/nashorn/internal/ir/visitor/NodeOperatorVisitor.java
@@ -25,9 +25,8 @@
package jdk.nashorn.internal.ir.visitor;
-import jdk.nashorn.internal.codegen.CompileUnit;
-import jdk.nashorn.internal.codegen.MethodEmitter;
import jdk.nashorn.internal.ir.BinaryNode;
+import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.UnaryNode;
@@ -45,15 +44,14 @@ public class NodeOperatorVisitor extends NodeVisitor {
/**
* Constructor
*
- * @param compileUnit compile unit
- * @param method method emitter
+ * @param lc a custom lexical context
*/
- public NodeOperatorVisitor(final CompileUnit compileUnit, final MethodEmitter method) {
- super(compileUnit, method);
+ public NodeOperatorVisitor(final LexicalContext lc) {
+ super(lc);
}
@Override
- public final Node enterUnaryNode(final UnaryNode unaryNode) {
+ public final boolean enterUnaryNode(final UnaryNode unaryNode) {
switch (unaryNode.tokenType()) {
case ADD:
return enterADD(unaryNode);
@@ -119,7 +117,7 @@ public class NodeOperatorVisitor extends NodeVisitor {
}
@Override
- public final Node enterBinaryNode(final BinaryNode binaryNode) {
+ public final boolean enterBinaryNode(final BinaryNode binaryNode) {
switch (binaryNode.tokenType()) {
case ADD:
return enterADD(binaryNode);
@@ -287,17 +285,6 @@ public class NodeOperatorVisitor extends NodeVisitor {
}
/*
- @Override
- public Node enter(final TernaryNode ternaryNode) {
- return enterDefault(ternaryNode);
- }
-
- @Override
- public Node leave(final TernaryNode ternaryNode) {
- return leaveDefault(ternaryNode);
- }*/
-
- /*
* Unary entries and exists.
*/
@@ -305,9 +292,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Unary enter - callback for entering a unary +
*
* @param unaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterADD(final UnaryNode unaryNode) {
+ public boolean enterADD(final UnaryNode unaryNode) {
return enterDefault(unaryNode);
}
@@ -325,9 +312,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Unary enter - callback for entering a ~ operator
*
* @param unaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterBIT_NOT(final UnaryNode unaryNode) {
+ public boolean enterBIT_NOT(final UnaryNode unaryNode) {
return enterDefault(unaryNode);
}
@@ -345,9 +332,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Unary enter - callback for entering a conversion
*
* @param unaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterCONVERT(final UnaryNode unaryNode) {
+ public boolean enterCONVERT(final UnaryNode unaryNode) {
return enterDefault(unaryNode);
}
@@ -365,9 +352,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Unary enter - callback for entering a ++ or -- operator
*
* @param unaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterDECINC(final UnaryNode unaryNode) {
+ public boolean enterDECINC(final UnaryNode unaryNode) {
return enterDefault(unaryNode);
}
@@ -387,7 +374,7 @@ public class NodeOperatorVisitor extends NodeVisitor {
* @param unaryNode the node
* @return processed node
*/
- public Node enterDELETE(final UnaryNode unaryNode) {
+ public boolean enterDELETE(final UnaryNode unaryNode) {
return enterDefault(unaryNode);
}
@@ -405,9 +392,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Unary enter - callback for entering a discard operator
*
* @param unaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterDISCARD(final UnaryNode unaryNode) {
+ public boolean enterDISCARD(final UnaryNode unaryNode) {
return enterDefault(unaryNode);
}
@@ -425,9 +412,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Unary enter - callback for entering a new operator
*
* @param unaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterNEW(final UnaryNode unaryNode) {
+ public boolean enterNEW(final UnaryNode unaryNode) {
return enterDefault(unaryNode);
}
@@ -445,9 +432,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Unary enter - callback for entering a ! operator
*
* @param unaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterNOT(final UnaryNode unaryNode) {
+ public boolean enterNOT(final UnaryNode unaryNode) {
return enterDefault(unaryNode);
}
@@ -465,9 +452,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Unary enter - callback for entering a unary -
*
* @param unaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterSUB(final UnaryNode unaryNode) {
+ public boolean enterSUB(final UnaryNode unaryNode) {
return enterDefault(unaryNode);
}
@@ -485,9 +472,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Unary enter - callback for entering a typeof
*
* @param unaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterTYPEOF(final UnaryNode unaryNode) {
+ public boolean enterTYPEOF(final UnaryNode unaryNode) {
return enterDefault(unaryNode);
}
@@ -505,9 +492,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Unary enter - callback for entering a void
*
* @param unaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterVOID(final UnaryNode unaryNode) {
+ public boolean enterVOID(final UnaryNode unaryNode) {
return enterDefault(unaryNode);
}
@@ -525,9 +512,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering + operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterADD(final BinaryNode binaryNode) {
+ public boolean enterADD(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -545,9 +532,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering {@literal &&} operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterAND(final BinaryNode binaryNode) {
+ public boolean enterAND(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -565,9 +552,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering an assignment
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterASSIGN(final BinaryNode binaryNode) {
+ public boolean enterASSIGN(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -585,9 +572,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering += operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterASSIGN_ADD(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_ADD(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -605,9 +592,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering {@literal &=} operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterASSIGN_BIT_AND(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_BIT_AND(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -625,9 +612,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering |= operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterASSIGN_BIT_OR(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_BIT_OR(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -645,9 +632,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering ^= operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterASSIGN_BIT_XOR(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_BIT_XOR(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -665,9 +652,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering /= operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterASSIGN_DIV(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_DIV(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -685,9 +672,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering %= operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterASSIGN_MOD(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_MOD(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -705,9 +692,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering *= operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterASSIGN_MUL(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_MUL(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -725,9 +712,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering {@literal >>=} operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterASSIGN_SAR(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_SAR(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -745,9 +732,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering a {@literal <<=} operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterASSIGN_SHL(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_SHL(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -765,9 +752,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering {@literal >>>=} operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterASSIGN_SHR(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_SHR(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -785,9 +772,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering -= operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterASSIGN_SUB(final BinaryNode binaryNode) {
+ public boolean enterASSIGN_SUB(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -805,9 +792,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering a bind operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterBIND(final BinaryNode binaryNode) {
+ public boolean enterBIND(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -825,9 +812,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering {@literal &} operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterBIT_AND(final BinaryNode binaryNode) {
+ public boolean enterBIT_AND(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -845,9 +832,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering | operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterBIT_OR(final BinaryNode binaryNode) {
+ public boolean enterBIT_OR(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -865,9 +852,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering ^ operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterBIT_XOR(final BinaryNode binaryNode) {
+ public boolean enterBIT_XOR(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -886,9 +873,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* (a, b) where the result is a
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterCOMMALEFT(final BinaryNode binaryNode) {
+ public boolean enterCOMMALEFT(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -908,9 +895,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* (a, b) where the result is b
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterCOMMARIGHT(final BinaryNode binaryNode) {
+ public boolean enterCOMMARIGHT(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -929,9 +916,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering a division
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterDIV(final BinaryNode binaryNode) {
+ public boolean enterDIV(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -949,9 +936,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering == operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterEQ(final BinaryNode binaryNode) {
+ public boolean enterEQ(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -969,9 +956,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering === operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterEQ_STRICT(final BinaryNode binaryNode) {
+ public boolean enterEQ_STRICT(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -989,9 +976,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering {@literal >=} operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterGE(final BinaryNode binaryNode) {
+ public boolean enterGE(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -1009,9 +996,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering {@literal >} operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterGT(final BinaryNode binaryNode) {
+ public boolean enterGT(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -1029,9 +1016,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering in operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterIN(final BinaryNode binaryNode) {
+ public boolean enterIN(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -1049,9 +1036,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering instanceof operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterINSTANCEOF(final BinaryNode binaryNode) {
+ public boolean enterINSTANCEOF(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -1069,9 +1056,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering {@literal <=} operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterLE(final BinaryNode binaryNode) {
+ public boolean enterLE(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -1089,9 +1076,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering {@literal <} operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterLT(final BinaryNode binaryNode) {
+ public boolean enterLT(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -1108,9 +1095,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering % operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterMOD(final BinaryNode binaryNode) {
+ public boolean enterMOD(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -1128,9 +1115,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering * operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterMUL(final BinaryNode binaryNode) {
+ public boolean enterMUL(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -1148,9 +1135,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering != operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterNE(final BinaryNode binaryNode) {
+ public boolean enterNE(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -1168,9 +1155,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering a !== operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterNE_STRICT(final BinaryNode binaryNode) {
+ public boolean enterNE_STRICT(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -1188,9 +1175,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering || operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterOR(final BinaryNode binaryNode) {
+ public boolean enterOR(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -1208,9 +1195,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering {@literal >>} operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterSAR(final BinaryNode binaryNode) {
+ public boolean enterSAR(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -1228,9 +1215,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering {@literal <<} operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterSHL(final BinaryNode binaryNode) {
+ public boolean enterSHL(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -1247,9 +1234,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering {@literal >>>} operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterSHR(final BinaryNode binaryNode) {
+ public boolean enterSHR(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -1267,9 +1254,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering - operator
*
* @param binaryNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterSUB(final BinaryNode binaryNode) {
+ public boolean enterSUB(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
diff --git a/src/jdk/nashorn/internal/ir/visitor/NodeVisitor.java b/src/jdk/nashorn/internal/ir/visitor/NodeVisitor.java
index f10d8c03..e3c0d34e 100644
--- a/src/jdk/nashorn/internal/ir/visitor/NodeVisitor.java
+++ b/src/jdk/nashorn/internal/ir/visitor/NodeVisitor.java
@@ -25,8 +25,6 @@
package jdk.nashorn.internal.ir.visitor;
-import jdk.nashorn.internal.codegen.CompileUnit;
-import jdk.nashorn.internal.codegen.MethodEmitter;
import jdk.nashorn.internal.ir.AccessNode;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.Block;
@@ -35,7 +33,6 @@ import jdk.nashorn.internal.ir.CallNode;
import jdk.nashorn.internal.ir.CaseNode;
import jdk.nashorn.internal.ir.CatchNode;
import jdk.nashorn.internal.ir.ContinueNode;
-import jdk.nashorn.internal.ir.DoWhileNode;
import jdk.nashorn.internal.ir.EmptyNode;
import jdk.nashorn.internal.ir.ExecuteNode;
import jdk.nashorn.internal.ir.ForNode;
@@ -44,6 +41,7 @@ import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.IfNode;
import jdk.nashorn.internal.ir.IndexNode;
import jdk.nashorn.internal.ir.LabelNode;
+import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LineNumberNode;
import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.Node;
@@ -65,41 +63,30 @@ import jdk.nashorn.internal.ir.WithNode;
* Visitor used to navigate the IR.
*/
public abstract class NodeVisitor {
- /** Current functionNode. */
- private FunctionNode currentFunctionNode;
-
- /** Current compile unit used for class generation. */
- private CompileUnit compileUnit;
-
- /**
- * Current method visitor used for method generation.
- * <p>
- * TODO: protected is just for convenience and readability, so that
- * subclasses can directly use 'method' - might want to change that
- */
- protected MethodEmitter method;
-
- /** Current block. */
- private Block currentBlock;
+ private final LexicalContext lc;
/**
- * Constructor.
+ * Constructor
*/
public NodeVisitor() {
- this(null, null);
+ this(new LexicalContext());
}
/**
* Constructor
*
- * @param compileUnit compile unit for this node visitor
- * @param method method emitter for this node visitor
+ * @param lc a custom lexical context
*/
- public NodeVisitor(final CompileUnit compileUnit, final MethodEmitter method) {
- super();
+ public NodeVisitor(final LexicalContext lc) {
+ this.lc = lc;
+ }
- this.compileUnit = compileUnit;
- this.method = method;
+ /**
+ * Get the lexical context of this node visitor
+ * @return lexical context
+ */
+ public LexicalContext getLexicalContext() {
+ return lc;
}
/**
@@ -118,10 +105,10 @@ public abstract class NodeVisitor {
*
* @see NodeVisitor#leaveDefault(Node)
* @param node the node to visit
- * @return the node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- protected Node enterDefault(final Node node) {
- return node;
+ protected boolean enterDefault(final Node node) {
+ return true;
}
/**
@@ -150,9 +137,9 @@ public abstract class NodeVisitor {
* Callback for entering an AccessNode
*
* @param accessNode the node
- * @return processed node, null if traversal should end, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterAccessNode(final AccessNode accessNode) {
+ public boolean enterAccessNode(final AccessNode accessNode) {
return enterDefault(accessNode);
}
@@ -170,9 +157,9 @@ public abstract class NodeVisitor {
* Callback for entering a Block
*
* @param block the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterBlock(final Block block) {
+ public boolean enterBlock(final Block block) {
return enterDefault(block);
}
@@ -192,7 +179,7 @@ public abstract class NodeVisitor {
* @param binaryNode the node
* @return processed node
*/
- public Node enterBinaryNode(final BinaryNode binaryNode) {
+ public boolean enterBinaryNode(final BinaryNode binaryNode) {
return enterDefault(binaryNode);
}
@@ -210,9 +197,9 @@ public abstract class NodeVisitor {
* Callback for entering a BreakNode
*
* @param breakNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterBreakNode(final BreakNode breakNode) {
+ public boolean enterBreakNode(final BreakNode breakNode) {
return enterDefault(breakNode);
}
@@ -230,9 +217,9 @@ public abstract class NodeVisitor {
* Callback for entering a CallNode
*
* @param callNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterCallNode(final CallNode callNode) {
+ public boolean enterCallNode(final CallNode callNode) {
return enterDefault(callNode);
}
@@ -250,9 +237,9 @@ public abstract class NodeVisitor {
* Callback for entering a CaseNode
*
* @param caseNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterCaseNode(final CaseNode caseNode) {
+ public boolean enterCaseNode(final CaseNode caseNode) {
return enterDefault(caseNode);
}
@@ -270,9 +257,9 @@ public abstract class NodeVisitor {
* Callback for entering a CatchNode
*
* @param catchNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterCatchNode(final CatchNode catchNode) {
+ public boolean enterCatchNode(final CatchNode catchNode) {
return enterDefault(catchNode);
}
@@ -290,9 +277,9 @@ public abstract class NodeVisitor {
* Callback for entering a ContinueNode
*
* @param continueNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterContinueNode(final ContinueNode continueNode) {
+ public boolean enterContinueNode(final ContinueNode continueNode) {
return enterDefault(continueNode);
}
@@ -307,32 +294,12 @@ public abstract class NodeVisitor {
}
/**
- * Callback for entering a DoWhileNode
- *
- * @param doWhileNode the node
- * @return processed node
- */
- public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
- return enterDefault(doWhileNode);
- }
-
- /**
- * Callback for leaving a DoWhileNode
- *
- * @param doWhileNode the node
- * @return processed node, which will replace the original one, or the original node
- */
- public Node leaveDoWhileNode(final DoWhileNode doWhileNode) {
- return leaveDefault(doWhileNode);
- }
-
- /**
* Callback for entering an EmptyNode
*
* @param emptyNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterEmptyNode(final EmptyNode emptyNode) {
+ public boolean enterEmptyNode(final EmptyNode emptyNode) {
return enterDefault(emptyNode);
}
@@ -350,9 +317,9 @@ public abstract class NodeVisitor {
* Callback for entering an ExecuteNode
*
* @param executeNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterExecuteNode(final ExecuteNode executeNode) {
+ public boolean enterExecuteNode(final ExecuteNode executeNode) {
return enterDefault(executeNode);
}
@@ -370,9 +337,9 @@ public abstract class NodeVisitor {
* Callback for entering a ForNode
*
* @param forNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterForNode(final ForNode forNode) {
+ public boolean enterForNode(final ForNode forNode) {
return enterDefault(forNode);
}
@@ -390,9 +357,9 @@ public abstract class NodeVisitor {
* Callback for entering a FunctionNode
*
* @param functionNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterFunctionNode(final FunctionNode functionNode) {
+ public boolean enterFunctionNode(final FunctionNode functionNode) {
return enterDefault(functionNode);
}
@@ -410,9 +377,9 @@ public abstract class NodeVisitor {
* Callback for entering an IdentNode
*
* @param identNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterIdentNode(final IdentNode identNode) {
+ public boolean enterIdentNode(final IdentNode identNode) {
return enterDefault(identNode);
}
@@ -429,10 +396,10 @@ public abstract class NodeVisitor {
/**
* Callback for entering an IfNode
*
- * @param ifNode the node
- * @return processed node, null if traversal should end
+ * @param ifNode the node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterIfNode(final IfNode ifNode) {
+ public boolean enterIfNode(final IfNode ifNode) {
return enterDefault(ifNode);
}
@@ -450,9 +417,9 @@ public abstract class NodeVisitor {
* Callback for entering an IndexNode
*
* @param indexNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterIndexNode(final IndexNode indexNode) {
+ public boolean enterIndexNode(final IndexNode indexNode) {
return enterDefault(indexNode);
}
@@ -470,9 +437,9 @@ public abstract class NodeVisitor {
* Callback for entering a LabelNode
*
* @param labelNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterLabelNode(final LabelNode labelNode) {
+ public boolean enterLabelNode(final LabelNode labelNode) {
return enterDefault(labelNode);
}
@@ -490,9 +457,9 @@ public abstract class NodeVisitor {
* Callback for entering a LineNumberNode
*
* @param lineNumberNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterLineNumberNode(final LineNumberNode lineNumberNode) {
+ public boolean enterLineNumberNode(final LineNumberNode lineNumberNode) {
return enterDefault(lineNumberNode);
}
@@ -510,9 +477,9 @@ public abstract class NodeVisitor {
* Callback for entering a LiteralNode
*
* @param literalNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterLiteralNode(final LiteralNode<?> literalNode) {
+ public boolean enterLiteralNode(final LiteralNode<?> literalNode) {
return enterDefault(literalNode);
}
@@ -530,9 +497,9 @@ public abstract class NodeVisitor {
* Callback for entering an ObjectNode
*
* @param objectNode the node
- * @return processed node
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterObjectNode(final ObjectNode objectNode) {
+ public boolean enterObjectNode(final ObjectNode objectNode) {
return enterDefault(objectNode);
}
@@ -550,9 +517,9 @@ public abstract class NodeVisitor {
* Callback for entering a PropertyNode
*
* @param propertyNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterPropertyNode(final PropertyNode propertyNode) {
+ public boolean enterPropertyNode(final PropertyNode propertyNode) {
return enterDefault(propertyNode);
}
@@ -570,9 +537,9 @@ public abstract class NodeVisitor {
* Callback for entering a ReturnNode
*
* @param returnNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterReturnNode(final ReturnNode returnNode) {
+ public boolean enterReturnNode(final ReturnNode returnNode) {
return enterDefault(returnNode);
}
@@ -590,9 +557,9 @@ public abstract class NodeVisitor {
* Callback for entering a RuntimeNode
*
* @param runtimeNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterRuntimeNode(final RuntimeNode runtimeNode) {
+ public boolean enterRuntimeNode(final RuntimeNode runtimeNode) {
return enterDefault(runtimeNode);
}
@@ -610,9 +577,9 @@ public abstract class NodeVisitor {
* Callback for entering a SplitNode
*
* @param splitNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterSplitNode(final SplitNode splitNode) {
+ public boolean enterSplitNode(final SplitNode splitNode) {
return enterDefault(splitNode);
}
@@ -630,9 +597,9 @@ public abstract class NodeVisitor {
* Callback for entering a SwitchNode
*
* @param switchNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterSwitchNode(final SwitchNode switchNode) {
+ public boolean enterSwitchNode(final SwitchNode switchNode) {
return enterDefault(switchNode);
}
@@ -650,9 +617,9 @@ public abstract class NodeVisitor {
* Callback for entering a TernaryNode
*
* @param ternaryNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterTernaryNode(final TernaryNode ternaryNode) {
+ public boolean enterTernaryNode(final TernaryNode ternaryNode) {
return enterDefault(ternaryNode);
}
@@ -670,9 +637,9 @@ public abstract class NodeVisitor {
* Callback for entering a ThrowNode
*
* @param throwNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterThrowNode(final ThrowNode throwNode) {
+ public boolean enterThrowNode(final ThrowNode throwNode) {
return enterDefault(throwNode);
}
@@ -690,9 +657,9 @@ public abstract class NodeVisitor {
* Callback for entering a TryNode
*
* @param tryNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterTryNode(final TryNode tryNode) {
+ public boolean enterTryNode(final TryNode tryNode) {
return enterDefault(tryNode);
}
@@ -710,9 +677,9 @@ public abstract class NodeVisitor {
* Callback for entering a UnaryNode
*
* @param unaryNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterUnaryNode(final UnaryNode unaryNode) {
+ public boolean enterUnaryNode(final UnaryNode unaryNode) {
return enterDefault(unaryNode);
}
@@ -730,9 +697,9 @@ public abstract class NodeVisitor {
* Callback for entering a VarNode
*
* @param varNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterVarNode(final VarNode varNode) {
+ public boolean enterVarNode(final VarNode varNode) {
return enterDefault(varNode);
}
@@ -750,9 +717,9 @@ public abstract class NodeVisitor {
* Callback for entering a WhileNode
*
* @param whileNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterWhileNode(final WhileNode whileNode) {
+ public boolean enterWhileNode(final WhileNode whileNode) {
return enterDefault(whileNode);
}
@@ -770,9 +737,9 @@ public abstract class NodeVisitor {
* Callback for entering a WithNode
*
* @param withNode the node
- * @return processed node, null if traversal should end
+ * @return true if traversal should continue and node children be traversed, false otherwise
*/
- public Node enterWithNode(final WithNode withNode) {
+ public boolean enterWithNode(final WithNode withNode) {
return enterDefault(withNode);
}
@@ -786,74 +753,5 @@ public abstract class NodeVisitor {
return leaveDefault(withNode);
}
- /**
- * Get the current function node for this NodeVisitor
- * @see FunctionNode
- * @return the function node being visited
- */
- public FunctionNode getCurrentFunctionNode() {
- return currentFunctionNode;
- }
-
- /**
- * Reset the current function node being visited for this NodeVisitor
- * @see FunctionNode
- * @param currentFunctionNode a new function node to traverse
- */
- public void setCurrentFunctionNode(final FunctionNode currentFunctionNode) {
- this.currentFunctionNode = currentFunctionNode;
- }
-
- /**
- * Get the current compile unit for this NodeVisitor
- * @see CompileUnit
- * @return a compile unit, or null if not a compiling NodeVisitor
- */
- public CompileUnit getCurrentCompileUnit() {
- return compileUnit;
- }
-
- /**
- * Set the current compile unit for this NodeVisitor
- * @see CompileUnit
- * @param compileUnit a new compile unit
- */
- public void setCurrentCompileUnit(final CompileUnit compileUnit) {
- this.compileUnit = compileUnit;
- }
-
- /**
- * Get the current method emitter for this NodeVisitor
- * @see MethodEmitter
- * @return the method emitter
- */
- public MethodEmitter getCurrentMethodEmitter() {
- return method;
- }
-
- /**
- * Reset the current method emitter for this NodeVisitor
- * @see MethodEmitter
- * @param method a new method emitter
- */
- public void setCurrentMethodEmitter(final MethodEmitter method) {
- this.method = method;
- }
-
- /**
- * Get the current Block being traversed for this NodeVisitor
- * @return the current block
- */
- public Block getCurrentBlock() {
- return currentBlock;
- }
-
- /**
- * Reset the Block to be traversed for this NodeVisitor
- * @param currentBlock the new current block
- */
- public void setCurrentBlock(final Block currentBlock) {
- this.currentBlock = currentBlock;
- }
}
diff --git a/src/jdk/nashorn/internal/lookup/MethodHandleFactory.java b/src/jdk/nashorn/internal/lookup/MethodHandleFactory.java
index 2161c782..4d8a2977 100644
--- a/src/jdk/nashorn/internal/lookup/MethodHandleFactory.java
+++ b/src/jdk/nashorn/internal/lookup/MethodHandleFactory.java
@@ -137,7 +137,7 @@ public final class MethodHandleFactory {
*/
static Object traceReturn(final DebugLogger logger, final Object value) {
final String str = "\treturn: " + stripName(value) + " [type=" + (value == null ? "null" : stripName(value.getClass()) + ']');
- logger.log(str, TRACE_LEVEL);
+ logger.log(TRACE_LEVEL, str);
return value;
}
@@ -173,7 +173,7 @@ public final class MethodHandleFactory {
}
assert logger != null;
- logger.log(sb.toString(), TRACE_LEVEL);
+ logger.log(TRACE_LEVEL, sb);
stacktrace(logger);
}
@@ -184,7 +184,7 @@ public final class MethodHandleFactory {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final PrintStream ps = new PrintStream(baos);
new Throwable().printStackTrace(ps);
- logger.log(baos.toString(), TRACE_LEVEL);
+ logger.log(TRACE_LEVEL, baos.toString());
}
private static String argString(final Object arg) {
@@ -614,7 +614,7 @@ public final class MethodHandleFactory {
@Override
public SwitchPoint createSwitchPoint() {
final SwitchPoint sp = super.createSwitchPoint();
- LOG.log("createSwitchPoint " + sp, TRACE_LEVEL);
+ LOG.log(TRACE_LEVEL, "createSwitchPoint ", sp);
return sp;
}
@@ -627,7 +627,7 @@ public final class MethodHandleFactory {
@Override
public MethodType type(final Class<?> returnType, final Class<?>... paramTypes) {
final MethodType mt = super.type(returnType, paramTypes);
- LOG.log("methodType " + returnType + ' ' + Arrays.toString(paramTypes) + ' ' + mt, TRACE_LEVEL);
+ LOG.log(TRACE_LEVEL, "methodType ", returnType, " ", Arrays.toString(paramTypes), " ", mt);
return mt;
}
}
@@ -638,7 +638,7 @@ public final class MethodHandleFactory {
private static class TraceCreateMethodHandleFunctionality extends TraceMethodHandleFunctionality {
@Override
public MethodHandle debug(final MethodHandle master, final String str, final Object... args) {
- LOG.log(str + ' ' + describe(args), TRACE_LEVEL);
+ LOG.log(TRACE_LEVEL, str, " ", describe(args));
stacktrace(LOG);
return master;
}
diff --git a/src/jdk/nashorn/internal/objects/BoundScriptFunctionImpl.java b/src/jdk/nashorn/internal/objects/BoundScriptFunctionImpl.java
index 1e9e9d18..be155f50 100644
--- a/src/jdk/nashorn/internal/objects/BoundScriptFunctionImpl.java
+++ b/src/jdk/nashorn/internal/objects/BoundScriptFunctionImpl.java
@@ -40,7 +40,7 @@ class BoundScriptFunctionImpl extends ScriptFunctionImpl {
BoundScriptFunctionImpl(ScriptFunctionData data, ScriptFunction targetFunction) {
super(data);
- this.prototype = ScriptRuntime.UNDEFINED;
+ setPrototype(ScriptRuntime.UNDEFINED);
this.targetFunction = targetFunction;
}
diff --git a/src/jdk/nashorn/internal/objects/NativeArray.java b/src/jdk/nashorn/internal/objects/NativeArray.java
index 16e237ba..a659af4f 100644
--- a/src/jdk/nashorn/internal/objects/NativeArray.java
+++ b/src/jdk/nashorn/internal/objects/NativeArray.java
@@ -76,7 +76,7 @@ public final class NativeArray extends ScriptObject {
private static final MethodHandle REDUCE_CALLBACK_INVOKER = Bootstrap.createDynamicInvoker("dyn:call", Object.class,
Object.class, Undefined.class, Object.class, Object.class, int.class, Object.class);
- private static final MethodHandle CALL_CMP = Bootstrap.createDynamicInvoker("dyn:call", int.class,
+ private static final MethodHandle CALL_CMP = Bootstrap.createDynamicInvoker("dyn:call", double.class,
ScriptFunction.class, Object.class, Object.class, Object.class);
private static final InvokeByName TO_LOCALE_STRING = new InvokeByName("toLocaleString", ScriptObject.class, String.class);
@@ -160,7 +160,7 @@ public final class NativeArray extends ScriptObject {
if ("length".equals(key)) {
// Step 3a
if (!desc.has(VALUE)) {
- return super.defineOwnProperty("length", propertyDesc, reject);
+ return super.defineOwnProperty("length", desc, reject);
}
// Step 3b
@@ -242,7 +242,7 @@ public final class NativeArray extends ScriptObject {
// Step 4c
// set the new array element
- final boolean succeeded = super.defineOwnProperty(key, propertyDesc, false);
+ final boolean succeeded = super.defineOwnProperty(key, desc, false);
// Step 4d
if (!succeeded) {
@@ -263,7 +263,7 @@ public final class NativeArray extends ScriptObject {
}
// not an index property
- return super.defineOwnProperty(key, propertyDesc, reject);
+ return super.defineOwnProperty(key, desc, reject);
}
/**
@@ -337,8 +337,9 @@ public final class NativeArray extends ScriptObject {
*/
@Function(attributes = Attribute.NOT_ENUMERABLE)
public static Object toString(final Object self) {
- if (self instanceof ScriptObject) {
- final ScriptObject sobj = (ScriptObject) self;
+ final Object obj = Global.toObject(self);
+ if (obj instanceof ScriptObject) {
+ final ScriptObject sobj = (ScriptObject)obj;
try {
final Object join = JOIN.getGetter().invokeExact(sobj);
if (join instanceof ScriptFunction) {
@@ -417,7 +418,7 @@ public final class NativeArray extends ScriptObject {
long length;
if (len instanceof Integer || len instanceof Long) {
length = ((Number) len).longValue();
- if (length >= 0 && length < 0xffff_ffffL) {
+ if (length >= 0 && length < JSType.MAX_UINT) {
return new NativeArray(length);
}
}
@@ -573,9 +574,9 @@ public final class NativeArray extends ScriptObject {
*/
@Function(attributes = Attribute.NOT_ENUMERABLE)
public static Object join(final Object self, final Object separator) {
- final String sep = separator == ScriptRuntime.UNDEFINED ? "," : JSType.toString(separator);
final StringBuilder sb = new StringBuilder();
final Iterator<Object> iter = arrayLikeIterator(self, true);
+ final String sep = separator == ScriptRuntime.UNDEFINED ? "," : JSType.toString(separator);
while (iter.hasNext()) {
final Object obj = iter.next();
@@ -754,8 +755,9 @@ public final class NativeArray extends ScriptObject {
final Object obj = Global.toObject(self);
final ScriptObject sobj = (ScriptObject)obj;
final long len = JSType.toUint32(sobj.getLength());
- final long relativeStartUint32 = JSType.toUint32(start);
- final long relativeStart = JSType.toInteger(start);
+ final double startNum = JSType.toNumber(start);
+ final long relativeStartUint32 = JSType.toUint32(startNum);
+ final long relativeStart = JSType.toInteger(startNum);
long k = relativeStart < 0 ?
Math.max(len + relativeStart, 0) :
@@ -763,8 +765,9 @@ public final class NativeArray extends ScriptObject {
Math.max(relativeStartUint32, relativeStart),
len);
- final long relativeEndUint32 = end == ScriptRuntime.UNDEFINED ? len : JSType.toUint32(end);
- final long relativeEnd = end == ScriptRuntime.UNDEFINED ? len : JSType.toInteger(end);
+ final double endNum = (end == ScriptRuntime.UNDEFINED)? Double.NaN : JSType.toNumber(end);
+ final long relativeEndUint32 = (end == ScriptRuntime.UNDEFINED)? len : JSType.toUint32(endNum);
+ final long relativeEnd = (end == ScriptRuntime.UNDEFINED)? len : JSType.toInteger(endNum);
final long finale = relativeEnd < 0 ?
Math.max(len + relativeEnd, 0) :
@@ -790,11 +793,15 @@ public final class NativeArray extends ScriptObject {
}
private static ScriptFunction compareFunction(final Object comparefn) {
- try {
- return (ScriptFunction)comparefn;
- } catch (final ClassCastException e) {
- return null; //undefined or null
+ if (comparefn == ScriptRuntime.UNDEFINED) {
+ return null;
+ }
+
+ if (! (comparefn instanceof ScriptFunction)) {
+ throw typeError("not.a.function", ScriptRuntime.safeToString(comparefn));
}
+
+ return (ScriptFunction)comparefn;
}
private static Object[] sort(final Object[] array, final Object comparefn) {
@@ -816,7 +823,7 @@ public final class NativeArray extends ScriptObject {
if (cmp != null) {
try {
- return (int)CALL_CMP.invokeExact(cmp, cmpThis, x, y);
+ return (int)Math.signum((double)CALL_CMP.invokeExact(cmp, cmpThis, x, y));
} catch (final RuntimeException | Error e) {
throw e;
} catch (final Throwable t) {
@@ -846,17 +853,26 @@ public final class NativeArray extends ScriptObject {
final long len = JSType.toUint32(sobj.getLength());
if (len > 1) {
- final Object[] src = new Object[(int) len];
- for (int i = 0; i < src.length; i++) {
- src[i] = sobj.get(i);
+ // Get only non-missing elements. Missing elements go at the end
+ // of the sorted array. So, just don't copy these to sort input.
+
+ final ArrayList<Object> src = new ArrayList<>();
+ for (int i = 0; i < (int)len; i++) {
+ if (sobj.has(i)) {
+ src.add(sobj.get(i));
+ }
}
- final Object[] sorted = sort(src, comparefn);
- assert sorted.length == src.length;
+ final Object[] sorted = sort(src.toArray(), comparefn);
for (int i = 0; i < sorted.length; i++) {
sobj.set(i, sorted[i], strict);
}
+
+ // delete missing elements - which are at the end of sorted array
+ for (int j = sorted.length; j < (int)len; j++) {
+ sobj.delete(j, strict);
+ }
}
return sobj;
@@ -895,8 +911,9 @@ public final class NativeArray extends ScriptObject {
final ScriptObject sobj = (ScriptObject)obj;
final boolean strict = Global.isStrict();
final long len = JSType.toUint32(sobj.getLength());
- final long relativeStartUint32 = JSType.toUint32(start);
- final long relativeStart = JSType.toInteger(start);
+ final double startNum = JSType.toNumber(start);
+ final long relativeStartUint32 = JSType.toUint32(startNum);
+ final long relativeStart = JSType.toInteger(startNum);
//TODO: workaround overflow of relativeStart for start > Integer.MAX_VALUE
final long actualStart = relativeStart < 0 ?
diff --git a/src/jdk/nashorn/internal/objects/NativeDate.java b/src/jdk/nashorn/internal/objects/NativeDate.java
index c4bf8e54..c97514a3 100644
--- a/src/jdk/nashorn/internal/objects/NativeDate.java
+++ b/src/jdk/nashorn/internal/objects/NativeDate.java
@@ -182,7 +182,8 @@ public final class NativeDate extends ScriptObject {
@Override
public String safeToString() {
- return "[Date " + toISOStringImpl(this) + "]";
+ final String str = isValidDate() ? toISOStringImpl(this) : INVALID_DATE;
+ return "[Date " + str + "]";
}
@Override
@@ -518,7 +519,7 @@ public final class NativeDate extends ScriptObject {
@Function(attributes = Attribute.NOT_ENUMERABLE)
public static Object getTimezoneOffset(final Object self) {
final NativeDate nd = getNativeDate(self);
- if (nd != null) {
+ if (nd != null && nd.isValidDate()) {
final long msec = (long) nd.getTime();
return - nd.getTimeZone().getOffset(msec) / msPerMinute;
}
@@ -534,8 +535,8 @@ public final class NativeDate extends ScriptObject {
*/
@Function(attributes = Attribute.NOT_ENUMERABLE)
public static Object setTime(final Object self, final Object time) {
- final double num = timeClip(JSType.toNumber(time));
final NativeDate nd = getNativeDate(self);
+ final double num = timeClip(JSType.toNumber(time));
nd.setTime(num);
return num;
}
@@ -550,9 +551,7 @@ public final class NativeDate extends ScriptObject {
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
public static Object setMilliseconds(final Object self, final Object... args) {
final NativeDate nd = getNativeDate(self);
- if (nd.isValidDate()) {
- setFields(nd, MILLISECOND, args, true);
- }
+ setFields(nd, MILLISECOND, args, true);
return nd.getTime();
}
@@ -566,9 +565,7 @@ public final class NativeDate extends ScriptObject {
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
public static Object setUTCMilliseconds(final Object self, final Object... args) {
final NativeDate nd = getNativeDate(self);
- if (nd.isValidDate()) {
- setFields(nd, MILLISECOND, args, false);
- }
+ setFields(nd, MILLISECOND, args, false);
return nd.getTime();
}
@@ -582,9 +579,7 @@ public final class NativeDate extends ScriptObject {
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2)
public static Object setSeconds(final Object self, final Object... args) {
final NativeDate nd = getNativeDate(self);
- if (nd.isValidDate()) {
- setFields(nd, SECOND, args, true);
- }
+ setFields(nd, SECOND, args, true);
return nd.getTime();
}
@@ -598,9 +593,7 @@ public final class NativeDate extends ScriptObject {
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2)
public static Object setUTCSeconds(final Object self, final Object... args) {
final NativeDate nd = getNativeDate(self);
- if (nd.isValidDate()) {
- setFields(nd, SECOND, args, false);
- }
+ setFields(nd, SECOND, args, false);
return nd.getTime();
}
@@ -614,9 +607,7 @@ public final class NativeDate extends ScriptObject {
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 3)
public static Object setMinutes(final Object self, final Object... args) {
final NativeDate nd = getNativeDate(self);
- if (nd.isValidDate()) {
- setFields(nd, MINUTE, args, true);
- }
+ setFields(nd, MINUTE, args, true);
return nd.getTime();
}
@@ -630,9 +621,7 @@ public final class NativeDate extends ScriptObject {
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 3)
public static Object setUTCMinutes(final Object self, final Object... args) {
final NativeDate nd = getNativeDate(self);
- if (nd.isValidDate()) {
- setFields(nd, MINUTE, args, false);
- }
+ setFields(nd, MINUTE, args, false);
return nd.getTime();
}
@@ -646,9 +635,7 @@ public final class NativeDate extends ScriptObject {
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 4)
public static Object setHours(final Object self, final Object... args) {
final NativeDate nd = getNativeDate(self);
- if (nd.isValidDate()) {
- setFields(nd, HOUR, args, true);
- }
+ setFields(nd, HOUR, args, true);
return nd.getTime();
}
@@ -662,9 +649,7 @@ public final class NativeDate extends ScriptObject {
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 4)
public static Object setUTCHours(final Object self, final Object... args) {
final NativeDate nd = getNativeDate(self);
- if (nd.isValidDate()) {
- setFields(nd, HOUR, args, false);
- }
+ setFields(nd, HOUR, args, false);
return nd.getTime();
}
@@ -678,9 +663,7 @@ public final class NativeDate extends ScriptObject {
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
public static Object setDate(final Object self, final Object... args) {
final NativeDate nd = getNativeDate(self);
- if (nd.isValidDate()) {
- setFields(nd, DAY, args, true);
- }
+ setFields(nd, DAY, args, true);
return nd.getTime();
}
@@ -694,9 +677,7 @@ public final class NativeDate extends ScriptObject {
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
public static Object setUTCDate(final Object self, final Object... args) {
final NativeDate nd = getNativeDate(self);
- if (nd.isValidDate()) {
- setFields(nd, DAY, args, false);
- }
+ setFields(nd, DAY, args, false);
return nd.getTime();
}
@@ -710,9 +691,7 @@ public final class NativeDate extends ScriptObject {
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2)
public static Object setMonth(final Object self, final Object... args) {
final NativeDate nd = getNativeDate(self);
- if (nd.isValidDate()) {
- setFields(nd, MONTH, args, true);
- }
+ setFields(nd, MONTH, args, true);
return nd.getTime();
}
@@ -726,9 +705,7 @@ public final class NativeDate extends ScriptObject {
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2)
public static Object setUTCMonth(final Object self, final Object... args) {
final NativeDate nd = ensureNativeDate(self);
- if (nd.isValidDate()) {
- setFields(nd, MONTH, args, false);
- }
+ setFields(nd, MONTH, args, false);
return nd.getTime();
}
@@ -746,7 +723,11 @@ public final class NativeDate extends ScriptObject {
setFields(nd, YEAR, args, true);
} else {
final double[] d = convertArgs(args, 0, YEAR, YEAR, 3);
- nd.setTime(timeClip(utc(makeDate(makeDay(d[0], d[1], d[2]), 0), nd.getTimeZone())));
+ if (d != null) {
+ nd.setTime(timeClip(utc(makeDate(makeDay(d[0], d[1], d[2]), 0), nd.getTimeZone())));
+ } else {
+ nd.setTime(NaN);
+ }
}
return nd.getTime();
}
@@ -781,13 +762,13 @@ public final class NativeDate extends ScriptObject {
public static Object setYear(final Object self, final Object year) {
final NativeDate nd = getNativeDate(self);
if (isNaN(nd.getTime())) {
- return null;
+ nd.setTime(utc(0, nd.getTimeZone()));
}
final double yearNum = JSType.toNumber(year);
if (isNaN(yearNum)) {
nd.setTime(NaN);
- return nd;
+ return nd.getTime();
}
int yearInt = JSType.toInteger(yearNum);
if (0 <= yearInt && yearInt <= 99) {
@@ -795,7 +776,7 @@ public final class NativeDate extends ScriptObject {
}
setFields(nd, YEAR, new Object[] {yearInt}, true);
- return nd;
+ return nd.getTime();
}
/**
@@ -844,10 +825,6 @@ public final class NativeDate extends ScriptObject {
*/
@Function(attributes = Attribute.NOT_ENUMERABLE)
public static Object toJSON(final Object self, final Object key) {
- if (self instanceof NativeDate) {
- final NativeDate nd = (NativeDate)self;
- return (isNaN(nd.getTime())) ? null : toISOStringImpl(nd);
- }
// NOTE: Date.prototype.toJSON is generic. Accepts other objects as well.
final Object selfObj = Global.toObject(self);
if (!(selfObj instanceof ScriptObject)) {
@@ -855,11 +832,11 @@ public final class NativeDate extends ScriptObject {
}
final ScriptObject sobj = (ScriptObject)selfObj;
final Object value = sobj.getDefaultValue(Number.class);
-
- final double num = (value instanceof Number) ? ((Number)value).doubleValue() : NaN;
-
- if (isInfinite(num) || isNaN(num)) {
- return null;
+ if (value instanceof Number) {
+ final double num = ((Number)value).doubleValue();
+ if (isInfinite(num) || isNaN(num)) {
+ return null;
+ }
}
try {
@@ -1200,13 +1177,18 @@ public final class NativeDate extends ScriptObject {
// Convert Date constructor args, checking for NaN, filling in defaults etc.
private static double[] convertCtorArgs(final Object[] args) {
final double[] d = new double[7];
+ boolean nullReturn = false;
+ // should not bailout on first NaN or infinite. Need to convert all
+ // subsequent args for possible side-effects via valueOf/toString overrides
+ // on argument objects.
for (int i = 0; i < d.length; i++) {
if (i < args.length) {
final double darg = JSType.toNumber(args[i]);
if (isNaN(darg) || isInfinite(darg)) {
- return null;
+ nullReturn = true;
}
+
d[i] = (long)darg;
} else {
d[i] = i == 2 ? 1 : 0; // day in month defaults to 1
@@ -1217,31 +1199,39 @@ public final class NativeDate extends ScriptObject {
d[0] += 1900;
}
- return d;
+ return nullReturn? null : d;
}
// This method does the hard work for all setter methods: If a value is provided
// as argument it is used, otherwise the value is calculated from the existing time value.
private static double[] convertArgs(final Object[] args, final double time, final int fieldId, final int start, final int length) {
final double[] d = new double[length];
+ boolean nullReturn = false;
+ // Need to call toNumber on all args for side-effects - even if an argument
+ // fails to convert to number, subsequent toNumber calls needed for possible
+ // side-effects via valueOf/toString overrides.
for (int i = start; i < start + length; i++) {
if (fieldId <= i && i < fieldId + args.length) {
final double darg = JSType.toNumber(args[i - fieldId]);
if (isNaN(darg) || isInfinite(darg)) {
- return null;
+ nullReturn = true;
}
+
d[i - start] = (long) darg;
} else {
// Date.prototype.set* methods require first argument to be defined
if (i == fieldId) {
- return null;
+ nullReturn = true;
+ }
+
+ if (! nullReturn) {
+ d[i - start] = valueFromTime(i, time);
}
- d[i - start] = valueFromTime(i, time);
}
}
- return d;
+ return nullReturn? null : d;
}
// ECMA 15.9.1.14 TimeClip (time)
@@ -1288,6 +1278,10 @@ public final class NativeDate extends ScriptObject {
final double time = local ? nd.getLocalTime() : nd.getTime();
final double d[] = convertArgs(args, time, fieldId, start, length);
+ if (! nd.isValidDate()) {
+ return;
+ }
+
double newTime;
if (d == null) {
newTime = NaN;
diff --git a/src/jdk/nashorn/internal/objects/NativeDebug.java b/src/jdk/nashorn/internal/objects/NativeDebug.java
index b311981e..aba76c8e 100644
--- a/src/jdk/nashorn/internal/objects/NativeDebug.java
+++ b/src/jdk/nashorn/internal/objects/NativeDebug.java
@@ -247,7 +247,6 @@ public final class NativeDebug extends ScriptObject {
out.println("Scope count " + ScriptObject.getScopeCount());
out.println("ScriptObject listeners added " + PropertyListenerManager.getListenersAdded());
out.println("ScriptObject listeners removed " + PropertyListenerManager.getListenersRemoved());
- out.println("ScriptObject listeners dead " + PropertyListenerManager.getListenersDead());
out.println("ScriptFunction count " + ScriptObject.getCount());
out.println("ScriptFunction invokes " + ScriptFunction.getInvokes());
out.println("ScriptFunction allocations " + ScriptFunction.getAllocations());
diff --git a/src/jdk/nashorn/internal/objects/NativeFunction.java b/src/jdk/nashorn/internal/objects/NativeFunction.java
index f5c0c290..13c1bc15 100644
--- a/src/jdk/nashorn/internal/objects/NativeFunction.java
+++ b/src/jdk/nashorn/internal/objects/NativeFunction.java
@@ -81,23 +81,13 @@ public final class NativeFunction {
Object[] args = null;
- if (ScriptObject.isArray(array)) {
- args = ((NativeArray)array).asObjectArray();
- } else if (array instanceof ScriptObject) {
+ if (array instanceof ScriptObject) {
// look for array-like object
final ScriptObject sobj = (ScriptObject)array;
final Object len = sobj.getLength();
-
- if (len == UNDEFINED || len == null) {
- throw typeError("function.apply.expects.array");
- }
-
final int n = (int)JSType.toUint32(len);
- if (n != JSType.toNumber(len)) {
- throw typeError("function.apply.expects.array");
- }
- args = new Object[(int)JSType.toUint32(len)];
+ args = new Object[n];
for (int i = 0; i < args.length; i++) {
args[i] = sobj.get(i);
}
diff --git a/src/jdk/nashorn/internal/objects/NativeJSAdapter.java b/src/jdk/nashorn/internal/objects/NativeJSAdapter.java
index 409b9fa4..8a3f42d2 100644
--- a/src/jdk/nashorn/internal/objects/NativeJSAdapter.java
+++ b/src/jdk/nashorn/internal/objects/NativeJSAdapter.java
@@ -148,11 +148,7 @@ public final class NativeJSAdapter extends ScriptObject {
if (overrides instanceof ScriptObject) {
this.overrides = true;
final ScriptObject sobj = (ScriptObject)overrides;
- final Iterator<String> iter = sobj.propertyIterator();
- while (iter.hasNext()) {
- final String prop = iter.next();
- super.set(prop, sobj.get(prop), false);
- }
+ this.addBoundProperties(sobj);
} else {
this.overrides = false;
}
diff --git a/src/jdk/nashorn/internal/objects/NativeJSON.java b/src/jdk/nashorn/internal/objects/NativeJSON.java
index faeebc1d..b3d6d873 100644
--- a/src/jdk/nashorn/internal/objects/NativeJSON.java
+++ b/src/jdk/nashorn/internal/objects/NativeJSON.java
@@ -229,7 +229,7 @@ public final class NativeJSON extends ScriptObject {
final JSType type = JSType.of(value);
if (type == JSType.OBJECT) {
if (isArray(value)) {
- return JA((NativeArray)value, state);
+ return JA((ScriptObject)value, state);
} else if (value instanceof ScriptObject) {
return JO((ScriptObject)value, state);
}
@@ -315,7 +315,7 @@ public final class NativeJSON extends ScriptObject {
}
// Spec: The abstract operation JA(value) serializes an array.
- private static Object JA(final NativeArray value, final StringifyState state) {
+ private static Object JA(final ScriptObject value, final StringifyState state) {
if (state.stack.containsKey(value)) {
throw typeError("JSON.stringify.cyclic");
}
diff --git a/src/jdk/nashorn/internal/objects/NativeJava.java b/src/jdk/nashorn/internal/objects/NativeJava.java
index 2519c028..5faec5ea 100644
--- a/src/jdk/nashorn/internal/objects/NativeJava.java
+++ b/src/jdk/nashorn/internal/objects/NativeJava.java
@@ -394,22 +394,56 @@ public final class NativeJava {
* </pre>
* We can see several important concepts in the above example:
* <ul>
- * <li>Every Java class will have exactly one extender subclass in Nashorn - repeated invocations of {@code extend}
- * for the same type will yield the same extender type. It's a generic adapter that delegates to whatever JavaScript
- * functions its implementation object has on a per-instance basis.</li>
+ * <li>Every specified list of Java types will have exactly one extender subclass in Nashorn - repeated invocations
+ * of {@code extend} for the same list of types will yield the same extender type. It's a generic adapter that
+ * delegates to whatever JavaScript functions its implementation object has on a per-instance basis.</li>
* <li>If the Java method is overloaded (as in the above example {@code List.add()}), then your JavaScript adapter
* must be prepared to deal with all overloads.</li>
* <li>You can't invoke {@code super.*()} from adapters for now.</li>
+ * <li>It is also possible to specify an ordinary JavaScript object as the last argument to {@code extend}. In that
+ * case, it is treated as a class-level override. {@code extend} will return an extender class where all instances
+ * will have the methods implemented by functions on that object, just as if that object were passed as the last
+ * argument to their constructor. Example:
+ * <pre>
+ * var Runnable = Java.type("java.lang.Runnable")
+ * var R1 = Java.extend(Runnable, {
+ * run: function() {
+ * print("R1.run() invoked!")
+ * }
+ * })
+ * var r1 = new R1
+ * var t = new java.lang.Thread(r1)
+ * t.start()
+ * t.join()
+ * </pre>
+ * As you can see, you don't have to pass any object when you create a new instance of {@code R1} as its
+ * {@code run()} function was defined already when extending the class. Of course, you can still provide
+ * instance-level overrides on these objects. The order of precedence is instance-level method, class-level method,
+ * superclass method, or {@code UnsupportedOperationException} if the superclass method is abstract. If we continue
+ * our previous example:
+ * <pre>
+ * var r2 = new R1(function() { print("r2.run() invoked!") })
+ * r2.run()
+ * </pre>
+ * We'll see it'll print {@code "r2.run() invoked!"}, thus overriding on instance-level the class-level behavior.
+ * </li>
* </ul>
* @param self not used
* @param types the original types. The caller must pass at least one Java type object of class {@link StaticClass}
* representing either a public interface or a non-final public class with at least one public or protected
* constructor. If more than one type is specified, at most one can be a class and the rest have to be interfaces.
- * Invoking the method twice with exactly the same types in the same order will return the same adapter
- * class, any reordering of types or even addition or removal of redundant types (i.e. interfaces that other types
- * in the list already implement/extend, or {@code java.lang.Object} in a list of types consisting purely of
- * interfaces) will result in a different adapter class, even though those adapter classes are functionally
- * identical; we deliberately don't want to incur the additional processing cost of canonicalizing type lists.
+ * Invoking the method twice with exactly the same types in the same order - in absence of class-level overrides -
+ * will return the same adapter class, any reordering of types or even addition or removal of redundant types (i.e.
+ * interfaces that other types in the list already implement/extend, or {@code java.lang.Object} in a list of types
+ * consisting purely of interfaces) will result in a different adapter class, even though those adapter classes are
+ * functionally identical; we deliberately don't want to incur the additional processing cost of canonicalizing type
+ * lists. As a special case, the last argument can be a {@code ScriptObject} instead of a type. In this case, a
+ * separate adapter class is generated - new one for each invocation - that will use the passed script object as its
+ * implementation for all instances. Instances of such adapter classes can then be created without passing another
+ * script object in the constructor, as the class has a class-level behavior defined by the script object. However,
+ * you can still pass a script object (or if it's a SAM type, a function) to the constructor to provide further
+ * instance-level overrides.
+ *
* @return a new {@link StaticClass} that represents the adapter for the original types.
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
@@ -417,14 +451,27 @@ public final class NativeJava {
if(types == null || types.length == 0) {
throw typeError("extend.expects.at.least.one.argument");
}
- final Class<?>[] stypes = new Class<?>[types.length];
+ final int l = types.length;
+ final int typesLen;
+ final ScriptObject classOverrides;
+ if(types[l - 1] instanceof ScriptObject) {
+ classOverrides = (ScriptObject)types[l - 1];
+ typesLen = l - 1;
+ if(typesLen == 0) {
+ throw typeError("extend.expects.at.least.one.type.argument");
+ }
+ } else {
+ classOverrides = null;
+ typesLen = l;
+ }
+ final Class<?>[] stypes = new Class<?>[typesLen];
try {
- for(int i = 0; i < types.length; ++i) {
+ for(int i = 0; i < typesLen; ++i) {
stypes[i] = ((StaticClass)types[i]).getRepresentedClass();
}
} catch(final ClassCastException e) {
throw typeError("extend.expects.java.types");
}
- return JavaAdapterFactory.getAdapterClassFor(stypes);
+ return JavaAdapterFactory.getAdapterClassFor(stypes, classOverrides);
}
}
diff --git a/src/jdk/nashorn/internal/objects/NativeRegExp.java b/src/jdk/nashorn/internal/objects/NativeRegExp.java
index d386531f..9a12911f 100644
--- a/src/jdk/nashorn/internal/objects/NativeRegExp.java
+++ b/src/jdk/nashorn/internal/objects/NativeRegExp.java
@@ -523,20 +523,28 @@ public final class NativeRegExp extends ScriptObject {
}
private RegExpResult execInner(final String string) {
+ final boolean isGlobal = regexp.isGlobal();
+ int start = getLastIndex();
+ if (!isGlobal) {
+ start = 0;
+ }
- final int start = regexp.isGlobal() ? getLastIndex() : 0;
if (start < 0 || start > string.length()) {
- setLastIndex(0);
+ if (isGlobal) {
+ setLastIndex(0);
+ }
return null;
}
final RegExpMatcher matcher = regexp.match(string);
if (matcher == null || !matcher.search(start)) {
- setLastIndex(0);
+ if (isGlobal) {
+ setLastIndex(0);
+ }
return null;
}
- if (regexp.isGlobal()) {
+ if (isGlobal) {
setLastIndex(matcher.end());
}
@@ -545,6 +553,22 @@ public final class NativeRegExp extends ScriptObject {
return match;
}
+ // String.prototype.split method ignores the global flag and should not update lastIndex property.
+ private RegExpResult execSplit(final String string, int start) {
+ if (start < 0 || start > string.length()) {
+ return null;
+ }
+
+ final RegExpMatcher matcher = regexp.match(string);
+ if (matcher == null || !matcher.search(start)) {
+ return null;
+ }
+
+ final RegExpResult match = new RegExpResult(string, matcher.start(), groups(matcher));
+ globalObject.setLastRegExpResult(match);
+ return match;
+ }
+
/**
* Convert java.util.regex.Matcher groups to JavaScript groups.
* That is, replace null and groups that didn't match with undefined.
@@ -597,7 +621,7 @@ public final class NativeRegExp extends ScriptObject {
* @return True if a match is found.
*/
public Object test(final String string) {
- return exec(string) != null;
+ return execInner(string) != null;
}
/**
@@ -762,35 +786,31 @@ public final class NativeRegExp extends ScriptObject {
* @return Array of substrings.
*/
Object split(final String string, final long limit) {
- return split(this, string, limit);
- }
-
- private static Object split(final NativeRegExp regexp0, final String input, final long limit) {
- final List<Object> matches = new ArrayList<>();
-
- final NativeRegExp regexp = new NativeRegExp(regexp0);
- regexp.setGlobal(true);
-
if (limit == 0L) {
return new NativeArray();
}
+ final List<Object> matches = new ArrayList<>();
+
RegExpResult match;
- final int inputLength = input.length();
+ final int inputLength = string.length();
int lastLength = -1;
+ int lastIndex = 0;
int lastLastIndex = 0;
- while ((match = regexp.execInner(input)) != null) {
- final int lastIndex = match.getIndex() + match.length();
+ while ((match = execSplit(string, lastIndex)) != null) {
+ lastIndex = match.getIndex() + match.length();
if (lastIndex > lastLastIndex) {
- matches.add(input.substring(lastLastIndex, match.getIndex()));
- if (match.getGroups().length > 1 && match.getIndex() < inputLength) {
- matches.addAll(Arrays.asList(match.getGroups()).subList(1, match.getGroups().length));
+ matches.add(string.substring(lastLastIndex, match.getIndex()));
+ final Object[] groups = match.getGroups();
+ if (groups.length > 1 && match.getIndex() < inputLength) {
+ for (int index = 1; index < groups.length && matches.size() < limit; index++) {
+ matches.add(groups[index]);
+ }
}
lastLength = match.length();
- lastLastIndex = lastIndex;
if (matches.size() >= limit) {
break;
@@ -798,8 +818,10 @@ public final class NativeRegExp extends ScriptObject {
}
// bump the index to avoid infinite loop
- if (regexp.getLastIndex() == match.getIndex()) {
- regexp.setLastIndex(match.getIndex() + 1);
+ if (lastIndex == lastLastIndex) {
+ lastIndex++;
+ } else {
+ lastLastIndex = lastIndex;
}
}
@@ -807,12 +829,12 @@ public final class NativeRegExp extends ScriptObject {
// check special case if we need to append an empty string at the
// end of the match
// if the lastIndex was the entire string
- if (lastLastIndex == input.length()) {
- if (lastLength > 0 || regexp.test("") == Boolean.FALSE) {
+ if (lastLastIndex == string.length()) {
+ if (lastLength > 0 || execSplit("", 0) == null) {
matches.add("");
}
} else {
- matches.add(input.substring(lastLastIndex, inputLength));
+ matches.add(string.substring(lastLastIndex, inputLength));
}
}
diff --git a/src/jdk/nashorn/internal/objects/NativeRegExpExecResult.java b/src/jdk/nashorn/internal/objects/NativeRegExpExecResult.java
index 5d4f8578..a88f69c7 100644
--- a/src/jdk/nashorn/internal/objects/NativeRegExpExecResult.java
+++ b/src/jdk/nashorn/internal/objects/NativeRegExpExecResult.java
@@ -51,6 +51,7 @@ public final class NativeRegExpExecResult extends ScriptObject {
NativeRegExpExecResult(final RegExpResult result) {
setProto(Global.instance().getArrayPrototype());
+ setIsArray();
this.setArray(ArrayData.allocate(result.getGroups().clone()));
this.index = result.getIndex();
this.input = result.getInput();
diff --git a/src/jdk/nashorn/internal/objects/NativeString.java b/src/jdk/nashorn/internal/objects/NativeString.java
index 5f48ad98..2715d4f4 100644
--- a/src/jdk/nashorn/internal/objects/NativeString.java
+++ b/src/jdk/nashorn/internal/objects/NativeString.java
@@ -25,11 +25,11 @@
package jdk.nashorn.internal.objects;
+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 static jdk.nashorn.internal.runtime.arrays.ArrayIndex.getArrayIndexNoThrow;
-import static jdk.nashorn.internal.lookup.Lookup.MH;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
@@ -41,6 +41,7 @@ import java.util.List;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
+import jdk.nashorn.internal.lookup.MethodHandleFactory;
import jdk.nashorn.internal.objects.annotations.Attribute;
import jdk.nashorn.internal.objects.annotations.Constructor;
import jdk.nashorn.internal.objects.annotations.Function;
@@ -55,7 +56,6 @@ import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
-import jdk.nashorn.internal.lookup.MethodHandleFactory;
import jdk.nashorn.internal.runtime.linker.NashornGuards;
import jdk.nashorn.internal.runtime.linker.PrimitiveLookup;
@@ -179,7 +179,6 @@ public final class NativeString extends ScriptObject {
return ((ScriptObject) Global.toObject(self)).get(key);
}
- @SuppressWarnings("unused")
private static Object get(final Object self, final int key) {
final CharSequence cs = JSType.toCharSequence(self);
if (key >= 0 && key < cs.length()) {
@@ -838,15 +837,13 @@ public final class NativeString extends ScriptObject {
*/
@Function(attributes = Attribute.NOT_ENUMERABLE)
public static Object split(final Object self, final Object separator, final Object limit) {
-
final String str = checkObjectToString(self);
+ final long lim = (limit == UNDEFINED) ? JSType.MAX_UINT : JSType.toUint32(limit);
if (separator == UNDEFINED) {
- return new NativeArray(new Object[]{str});
+ return lim == 0 ? new NativeArray() : new NativeArray(new Object[]{str});
}
- final long lim = (limit == UNDEFINED) ? JSType.MAX_UINT : JSType.toUint32(limit);
-
if (separator instanceof NativeRegExp) {
return ((NativeRegExp) separator).split(str, lim);
}
@@ -857,8 +854,9 @@ public final class NativeString extends ScriptObject {
private static Object splitString(String str, String separator, long limit) {
if (separator.isEmpty()) {
- final Object[] array = new Object[str.length()];
- for (int i = 0; i < array.length; i++) {
+ final int length = (int) Math.min(str.length(), limit);
+ final Object[] array = new Object[length];
+ for (int i = 0; i < length; i++) {
array[i] = String.valueOf(str.charAt(i));
}
return new NativeArray(array);
diff --git a/src/jdk/nashorn/internal/objects/NativeUint32Array.java b/src/jdk/nashorn/internal/objects/NativeUint32Array.java
index 7ad8a939..9be01312 100644
--- a/src/jdk/nashorn/internal/objects/NativeUint32Array.java
+++ b/src/jdk/nashorn/internal/objects/NativeUint32Array.java
@@ -29,6 +29,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.runtime.JSType;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.arrays.ArrayData;
@@ -71,17 +72,17 @@ public final class NativeUint32Array extends ArrayBufferView {
@Override
protected long getLongImpl(final int key) {
- return getIntImpl(key) & 0xffff_ffffL;
+ return getIntImpl(key) & JSType.MAX_UINT;
}
@Override
protected double getDoubleImpl(final int key) {
- return getIntImpl(key) & 0xffff_ffffL;
+ return getIntImpl(key) & JSType.MAX_UINT;
}
@Override
protected Object getObjectImpl(final int key) {
- return getIntImpl(key) & 0xffff_ffffL;
+ return getIntImpl(key) & JSType.MAX_UINT;
}
@Override
diff --git a/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java b/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java
index 46b353f1..23a13f25 100644
--- a/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java
+++ b/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java
@@ -42,6 +42,10 @@ import jdk.nashorn.internal.lookup.Lookup;
* function objects -- to expose properties like "prototype", "length" etc.
*/
public class ScriptFunctionImpl extends ScriptFunction {
+
+ /** Reference to constructor prototype. */
+ private Object prototype;
+
// property map for strict mode functions
private static final PropertyMap strictmodemap$;
// property map for bound functions
@@ -49,6 +53,9 @@ public class ScriptFunctionImpl extends ScriptFunction {
// property map for non-strict, non-bound functions.
private static final PropertyMap nasgenmap$;
+ // Marker object for lazily initialized prototype object
+ private static final Object LAZY_PROTOTYPE = new Object();
+
/**
* Constructor called by Nasgen generated code, no membercount, use the default map.
* Creates builtin functions only.
@@ -83,8 +90,8 @@ public class ScriptFunctionImpl extends ScriptFunction {
* @param methodHandle handle for invocation
* @param scope scope object
* @param specs specialized versions of this method, if available, null otherwise
- * @param strict are we in strict mode
- * @param builtin is this a built-in function
+ * @param isStrict are we in strict mode
+ * @param isBuiltin is this a built-in function
* @param isConstructor can the function be used as a constructor (most can; some built-ins are restricted).
*/
ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final MethodHandle[] specs, final boolean isStrict, final boolean isBuiltin, final boolean isConstructor) {
@@ -125,11 +132,17 @@ public class ScriptFunctionImpl extends ScriptFunction {
// function object representing TypeErrorThrower
private static ScriptFunction typeErrorThrower;
+ /*
+ * ECMA section 13.2.3 The [[ThrowTypeError]] Function Object
+ */
static synchronized ScriptFunction getTypeErrorThrower() {
if (typeErrorThrower == null) {
- //name handle
- final ScriptFunctionImpl func = new ScriptFunctionImpl("TypeErrorThrower", Lookup.TYPE_ERROR_THROWER_SETTER, null, null, false, false, false);
+ // use "getter" so that [[ThrowTypeError]] function's arity is 0 - as specified in step 10 of section 13.2.3
+ final ScriptFunctionImpl func = new ScriptFunctionImpl("TypeErrorThrower", Lookup.TYPE_ERROR_THROWER_GETTER, null, null, false, false, false);
func.setPrototype(UNDEFINED);
+ // Non-constructor built-in functions do not have "prototype" property
+ func.deleteOwnProperty(func.getMap().findProperty("prototype"));
+ func.preventExtensions();
typeErrorThrower = func;
}
@@ -152,7 +165,7 @@ public class ScriptFunctionImpl extends ScriptFunction {
}
private static PropertyMap createBoundFunctionMap(final PropertyMap strictModeMap) {
- // Bond function map is same as strict function map, but additionally lacks the "prototype" property, see
+ // Bound function map is same as strict function map, but additionally lacks the "prototype" property, see
// ECMAScript 5.1 section 15.3.4.5
return strictModeMap.deleteProperty(strictModeMap.findProperty("prototype"));
}
@@ -182,6 +195,8 @@ public class ScriptFunctionImpl extends ScriptFunction {
static ScriptFunction makeFunction(final String name, final MethodHandle methodHandle, final MethodHandle[] specs) {
final ScriptFunctionImpl func = new ScriptFunctionImpl(name, methodHandle, null, specs, false, true, false);
func.setPrototype(UNDEFINED);
+ // Non-constructor built-in functions do not have "prototype" property
+ func.deleteOwnProperty(func.getMap().findProperty("prototype"));
return func;
}
@@ -227,10 +242,23 @@ public class ScriptFunctionImpl extends ScriptFunction {
return Global.objectPrototype();
}
+ @Override
+ public final Object getPrototype() {
+ if (prototype == LAZY_PROTOTYPE) {
+ prototype = new PrototypeObject(this);
+ }
+ return prototype;
+ }
+
+ @Override
+ public final void setPrototype(final Object prototype) {
+ this.prototype = prototype;
+ }
+
// Internals below..
private void init() {
this.setProto(Global.instance().getFunctionPrototype());
- this.setPrototype(new PrototypeObject(this));
+ this.prototype = LAZY_PROTOTYPE;
if (isStrict()) {
final ScriptFunction func = getTypeErrorThrower();
diff --git a/src/jdk/nashorn/internal/parser/AbstractParser.java b/src/jdk/nashorn/internal/parser/AbstractParser.java
index a3263b81..8f65e0f7 100644
--- a/src/jdk/nashorn/internal/parser/AbstractParser.java
+++ b/src/jdk/nashorn/internal/parser/AbstractParser.java
@@ -37,8 +37,8 @@ import jdk.nashorn.internal.runtime.ECMAErrors;
import jdk.nashorn.internal.runtime.ErrorManager;
import jdk.nashorn.internal.runtime.JSErrorType;
import jdk.nashorn.internal.runtime.ParserException;
-import jdk.nashorn.internal.runtime.regexp.RegExpFactory;
import jdk.nashorn.internal.runtime.Source;
+import jdk.nashorn.internal.runtime.regexp.RegExpFactory;
/**
* Base class for parsers.
@@ -197,9 +197,10 @@ public abstract class AbstractParser {
*
* @param message Error message.
* @param errorToken Offending token.
+ * @return ParserException upon failure. Caller should throw and not ignore
*/
- protected final void error(final String message, final long errorToken) {
- error(JSErrorType.SYNTAX_ERROR, message, errorToken);
+ protected final ParserException error(final String message, final long errorToken) {
+ return error(JSErrorType.SYNTAX_ERROR, message, errorToken);
}
/**
@@ -208,22 +209,24 @@ public abstract class AbstractParser {
* @param errorType The error type
* @param message Error message.
* @param errorToken Offending token.
+ * @return ParserException upon failure. Caller should throw and not ignore
*/
- protected final void error(final JSErrorType errorType, final String message, final long errorToken) {
+ protected final ParserException error(final JSErrorType errorType, final String message, final long errorToken) {
final int position = Token.descPosition(errorToken);
final int lineNum = source.getLine(position);
final int columnNum = source.getColumn(position);
final String formatted = ErrorManager.format(message, source, lineNum, columnNum, errorToken);
- throw new ParserException(errorType, formatted, source, lineNum, columnNum, errorToken);
+ return new ParserException(errorType, formatted, source, lineNum, columnNum, errorToken);
}
/**
* Report an error.
*
* @param message Error message.
+ * @return ParserException upon failure. Caller should throw and not ignore
*/
- protected final void error(final String message) {
- error(JSErrorType.SYNTAX_ERROR, message);
+ protected final ParserException error(final String message) {
+ return error(JSErrorType.SYNTAX_ERROR, message);
}
/**
@@ -231,13 +234,24 @@ public abstract class AbstractParser {
*
* @param errorType The error type
* @param message Error message.
+ * @return ParserException upon failure. Caller should throw and not ignore
*/
- protected final void error(final JSErrorType errorType, final String message) {
+ protected final ParserException error(final JSErrorType errorType, final String message) {
// TODO - column needs to account for tabs.
final int position = Token.descPosition(token);
final int column = position - linePosition;
final String formatted = ErrorManager.format(message, source, line, column, token);
- throw new ParserException(errorType, formatted, source, line, column, token);
+ return new ParserException(errorType, formatted, source, line, column, token);
+ }
+
+ /**
+ * Report a warning to the error manager.
+ *
+ * @param errorType The error type of the warning
+ * @param message Warning message.
+ */
+ protected final void warning(final JSErrorType errorType, final String message, final long errorToken) {
+ errors.warning(error(errorType, message, errorToken));
}
/**
@@ -270,7 +284,7 @@ public abstract class AbstractParser {
*/
protected final void expect(final TokenType expected) throws ParserException {
if (type != expected) {
- error(expectMessage(expected));
+ throw error(expectMessage(expected));
}
next();
@@ -285,7 +299,7 @@ public abstract class AbstractParser {
*/
protected final Object expectValue(final TokenType expected) throws ParserException {
if (type != expected) {
- error(expectMessage(expected));
+ throw error(expectMessage(expected));
}
final Object value = getValue();
@@ -429,7 +443,7 @@ public abstract class AbstractParser {
try {
RegExpFactory.validate(regex.getExpression(), regex.getOptions());
} catch (final ParserException e) {
- error(e.getMessage());
+ throw error(e.getMessage());
}
}
node = LiteralNode.newInstance(source, literalToken, finish, (LexerToken)value);
diff --git a/src/jdk/nashorn/internal/parser/JSONParser.java b/src/jdk/nashorn/internal/parser/JSONParser.java
index 5468ca3d..e074cf01 100644
--- a/src/jdk/nashorn/internal/parser/JSONParser.java
+++ b/src/jdk/nashorn/internal/parser/JSONParser.java
@@ -170,8 +170,7 @@ public class JSONParser extends AbstractParser {
}
case '"':
case '\\':
- error(AbstractParser.message("unexpected.token", str));
- break;
+ throw error(AbstractParser.message("unexpected.token", str));
}
}
@@ -222,14 +221,12 @@ public class JSONParser extends AbstractParser {
return new UnaryNode(source, literalToken, LiteralNode.newInstance(source, realToken, finish, (Number)value));
}
- error(AbstractParser.message("expected", "number", type.getNameOrType()));
- break;
+ throw error(AbstractParser.message("expected", "number", type.getNameOrType()));
default:
break;
}
- error(AbstractParser.message("expected", "json literal", type.getNameOrType()));
- return null;
+ throw error(AbstractParser.message("expected", "json literal", type.getNameOrType()));
}
/**
@@ -265,7 +262,7 @@ loop:
elements.add(jsonLiteral());
// Comma between array elements is mandatory in JSON.
if (type != COMMARIGHT && type != RBRACKET) {
- error(AbstractParser.message("expected", ", or ]", type.getNameOrType()));
+ throw error(AbstractParser.message("expected", ", or ]", type.getNameOrType()));
}
break;
}
@@ -306,7 +303,7 @@ loop:
// Comma between property assigments is mandatory in JSON.
if (type != RBRACE && type != COMMARIGHT) {
- error(AbstractParser.message("expected", ", or }", type.getNameOrType()));
+ throw error(AbstractParser.message("expected", ", or }", type.getNameOrType()));
}
break;
}
@@ -334,13 +331,11 @@ loop:
if (name != null) {
expect(COLON);
final Node value = jsonLiteral();
- return new PropertyNode(source, propertyToken, value.getFinish(), name, value);
+ return new PropertyNode(source, propertyToken, value.getFinish(), name, value, null, null);
}
// Raise an error.
- error(AbstractParser.message("expected", "string", type.getNameOrType()));
-
- return null;
+ throw error(AbstractParser.message("expected", "string", type.getNameOrType()));
}
}
diff --git a/src/jdk/nashorn/internal/parser/Lexer.java b/src/jdk/nashorn/internal/parser/Lexer.java
index 0c2255d4..d9ebb365 100644
--- a/src/jdk/nashorn/internal/parser/Lexer.java
+++ b/src/jdk/nashorn/internal/parser/Lexer.java
@@ -57,6 +57,9 @@ import jdk.nashorn.internal.runtime.options.Options;
*/
@SuppressWarnings("fallthrough")
public class Lexer extends Scanner {
+ private static final long MIN_INT_L = Integer.MIN_VALUE;
+ private static final long MAX_INT_L = Integer.MAX_VALUE;
+
private static final boolean XML_LITERALS = Options.getBooleanProperty("nashorn.lexer.xmlliterals");
/** Content source. */
@@ -984,27 +987,27 @@ public class Lexer extends Scanner {
*/
private static Number valueOf(final String valueString, final int radix) throws NumberFormatException {
try {
- return Integer.valueOf(valueString, radix);
+ final long value = Long.parseLong(valueString, radix);
+ if(value >= MIN_INT_L && value <= MAX_INT_L) {
+ return Integer.valueOf((int)value);
+ }
+ return Long.valueOf(value);
} catch (final NumberFormatException e) {
- try {
- return Long.valueOf(valueString, radix);
- } catch (final NumberFormatException e2) {
- if (radix == 10) {
- return Double.valueOf(valueString);
- }
-
- double value = 0.0;
+ if (radix == 10) {
+ return Double.valueOf(valueString);
+ }
- for (int i = 0; i < valueString.length(); i++) {
- final char ch = valueString.charAt(i);
- // Preverified, should always be a valid digit.
- final int digit = convertDigit(ch, radix);
- value *= radix;
- value += digit;
- }
+ double value = 0.0;
- return value;
+ for (int i = 0; i < valueString.length(); i++) {
+ final char ch = valueString.charAt(i);
+ // Preverified, should always be a valid digit.
+ final int digit = convertDigit(ch, radix);
+ value *= radix;
+ value += digit;
}
+
+ return value;
}
}
diff --git a/src/jdk/nashorn/internal/parser/Parser.java b/src/jdk/nashorn/internal/parser/Parser.java
index b810043e..ab70859b 100644
--- a/src/jdk/nashorn/internal/parser/Parser.java
+++ b/src/jdk/nashorn/internal/parser/Parser.java
@@ -54,24 +54,23 @@ import static jdk.nashorn.internal.parser.TokenType.TERNARY;
import static jdk.nashorn.internal.parser.TokenType.WHILE;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-import java.util.Stack;
import jdk.nashorn.internal.codegen.CompilerConstants;
import jdk.nashorn.internal.codegen.Namespace;
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.BreakableNode;
import jdk.nashorn.internal.ir.CallNode;
import jdk.nashorn.internal.ir.CaseNode;
import jdk.nashorn.internal.ir.CatchNode;
import jdk.nashorn.internal.ir.ContinueNode;
-import jdk.nashorn.internal.ir.DoWhileNode;
import jdk.nashorn.internal.ir.EmptyNode;
import jdk.nashorn.internal.ir.ExecuteNode;
import jdk.nashorn.internal.ir.ForNode;
@@ -84,6 +83,7 @@ import jdk.nashorn.internal.ir.LabelNode;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LineNumberNode;
import jdk.nashorn.internal.ir.LiteralNode;
+import jdk.nashorn.internal.ir.LoopNode;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.ObjectNode;
import jdk.nashorn.internal.ir.PropertyKey;
@@ -117,9 +117,10 @@ public class Parser extends AbstractParser {
/** Is scripting mode. */
private final boolean scripting;
- private final LexicalContext lexicalContext = new LexicalContext();
private List<Node> functionDeclarations;
+ private final BlockLexicalContext lc = new BlockLexicalContext();
+
/** Namespace for function names where not explicitly given */
private final Namespace namespace;
@@ -146,7 +147,7 @@ public class Parser extends AbstractParser {
*/
public Parser(final ScriptEnvironment env, final Source source, final ErrorManager errors, final boolean strict) {
super(source, errors, strict);
- this.env = env;
+ this.env = env;
this.namespace = new Namespace(env.getNamespace());
this.scripting = env._scripting;
}
@@ -162,7 +163,7 @@ public class Parser extends AbstractParser {
* @return function node resulting from successful parse
*/
public FunctionNode parse() {
- return parse(RUN_SCRIPT.tag());
+ return parse(RUN_SCRIPT.symbolName());
}
/**
@@ -176,7 +177,7 @@ public class Parser extends AbstractParser {
*/
public FunctionNode parse(final String scriptName) {
final long t0 = Timing.isEnabled() ? System.currentTimeMillis() : 0L;
- LOG.info(this + " begin for '" + scriptName + "'");
+ LOG.info(this, " begin for '", scriptName, "'");
try {
stream = new TokenStream();
@@ -214,7 +215,7 @@ public class Parser extends AbstractParser {
final String end = this + " end '" + scriptName + "'";
if (Timing.isEnabled()) {
Timing.accumulateTime(toString(), System.currentTimeMillis() - t0);
- LOG.info(end + "' in " + (System.currentTimeMillis() - t0) + " ms");
+ LOG.info(end, "' in ", (System.currentTimeMillis() - t0), " ms");
} else {
LOG.info(end);
}
@@ -275,8 +276,7 @@ loop:
*/
private Block newBlock() {
final Block block = new Block(source, token, Token.descPosition(token));
- lexicalContext.push(block);
- return block;
+ return lc.push(block);
}
/**
@@ -285,36 +285,60 @@ loop:
* @param ident Name of function.
* @return New block.
*/
- private FunctionNode newFunctionBlock(final IdentNode ident) {
+ private FunctionNode newFunctionNode(final long startToken, final IdentNode ident, final List<IdentNode> parameters, final FunctionNode.Kind kind) {
// Build function name.
final StringBuilder sb = new StringBuilder();
- final FunctionNode parentFunction = getFunction();
- if(parentFunction != null && !parentFunction.isProgram()) {
+ final FunctionNode parentFunction = lc.getCurrentFunction();
+ if (parentFunction != null && !parentFunction.isProgram()) {
sb.append(parentFunction.getName()).append('$');
}
- sb.append(ident != null ? ident.getName() : FUNCTION_PREFIX.tag());
+ sb.append(ident != null ? ident.getName() : FUNCTION_PREFIX.symbolName());
final String name = namespace.uniqueName(sb.toString());
- assert parentFunction != null || name.equals(RUN_SCRIPT.tag()) : "name = " + name;// must not rename runScript().
+ assert parentFunction != null || name.equals(RUN_SCRIPT.symbolName()) : "name = " + name;// must not rename runScript().
- // Start new block.
- final FunctionNode functionBlock = new FunctionNode(source, token, Token.descPosition(token), namespace, ident, name);
- if(parentFunction == null) {
- functionBlock.setProgram();
+ int flags = 0;
+ if (parentFunction == null) {
+ flags |= FunctionNode.IS_PROGRAM;
+ }
+ if (isStrictMode) {
+ flags |= FunctionNode.IS_STRICT;
}
- functionBlock.setStrictMode(isStrictMode);
- functionBlock.setState(errors.hasErrors() ? CompilationState.PARSE_ERROR : CompilationState.PARSED);
- lexicalContext.push(functionBlock);
- return functionBlock;
+ // Start new block.
+ FunctionNode functionNode =
+ new FunctionNode(
+ source,
+ token,
+ Token.descPosition(token),
+ startToken,
+ namespace,
+ ident,
+ name,
+ parameters,
+ kind,
+ flags);
+
+ functionNode = functionNode.setState(lc, errors.hasErrors() ? CompilationState.PARSE_ERROR : CompilationState.PARSED);
+ lc.push(functionNode);
+ // Create new block, and just put it on the context stack, restoreFunctionNode() will associate it with the
+ // FunctionNode.
+ newBlock();
+ return functionNode;
}
/**
* Restore the current block.
*/
- private void restoreBlock(Block block) {
- lexicalContext.pop(block);
+ private Block restoreBlock(final Block block) {
+ return lc.pop(block);//.setFlag(lc, flags);
+ }
+
+ private FunctionNode restoreFunctionNode(final FunctionNode functionNode, final long lastToken) {
+ final Block newBody = restoreBlock(lc.getFunctionBody(functionNode));
+
+ return lc.pop(functionNode).setBody(lc, newBody).setLastToken(lc, lastToken);
}
/**
@@ -323,23 +347,17 @@ loop:
*/
private Block getBlock(final boolean needsBraces) {
// Set up new block. Captures LBRACE.
- final Block newBlock = newBlock();
+ Block newBlock = newBlock();
try {
- pushControlNode(newBlock);
-
// Block opening brace.
if (needsBraces) {
expect(LBRACE);
}
+ // Accumulate block statements.
+ statementList();
- try {
- // Accumulate block statements.
- statementList();
- } finally {
- popControlNode();
- }
} finally {
- restoreBlock(newBlock);
+ newBlock = restoreBlock(newBlock);
}
final int possibleEnd = Token.descPosition(token) + Token.descLength(token);
@@ -363,15 +381,12 @@ loop:
return getBlock(true);
}
// Set up new block. Captures first token.
- final Block newBlock = newBlock();
-
+ Block newBlock = newBlock();
try {
- // Accumulate statements.
statement();
} finally {
- restoreBlock(newBlock);
+ newBlock = restoreBlock(newBlock);
}
-
return newBlock;
}
@@ -382,11 +397,8 @@ loop:
private void detectSpecialFunction(final IdentNode ident) {
final String name = ident.getName();
- if (EVAL.tag().equals(name)) {
- final Iterator<FunctionNode> it = lexicalContext.getFunctions();
- if(it.hasNext()) {
- it.next().setHasEval(it);
- }
+ if (EVAL.symbolName().equals(name)) {
+ markEval(lc);
}
}
@@ -397,8 +409,8 @@ loop:
private void detectSpecialProperty(final IdentNode ident) {
final String name = ident.getName();
- if (ARGUMENTS.tag().equals(name)) {
- getFunction().setUsesArguments();
+ if (ARGUMENTS.symbolName().equals(name)) {
+ lc.setFlag(lc.getCurrentFunction(), FunctionNode.USES_ARGUMENTS);
}
}
@@ -439,7 +451,7 @@ loop:
lhs instanceof IndexNode ||
lhs instanceof IdentNode)) {
if (env._early_lvalue_error) {
- error(JSErrorType.REFERENCE_ERROR, AbstractParser.message("invalid.lvalue"), lhs.getToken());
+ throw error(JSErrorType.REFERENCE_ERROR, AbstractParser.message("invalid.lvalue"), lhs.getToken());
}
return referenceError(lhs, rhs);
}
@@ -469,143 +481,13 @@ loop:
* @return Reduced expression.
*/
private Node incDecExpression(final long firstToken, final TokenType tokenType, final Node expression, final boolean isPostfix) {
- long incDecToken = firstToken;
- if (isPostfix) {
- incDecToken = Token.recast(incDecToken, tokenType == DECPREFIX ? DECPOSTFIX : INCPOSTFIX);
- }
-
- final UnaryNode node = new UnaryNode(source, incDecToken, expression);
if (isPostfix) {
- node.setStart(expression.getStart());
- node.setFinish(Token.descPosition(incDecToken) + Token.descLength(incDecToken));
- }
-
- return node;
- }
-
- /**
- * Find a label node in the label stack.
- * @param ident Ident to find.
- * @return null or the found label node.
- */
- private LabelNode findLabel(final IdentNode ident) {
- for (final LabelNode labelNode : getFunction().getLabelStack()) {
- if (labelNode.getLabel().equals(ident)) {
- return labelNode;
- }
- }
-
- return null;
- }
-
- /**
- * Add a label to the label stack.
- * @param labelNode Label to add.
- */
- private void pushLabel(final LabelNode labelNode) {
- getFunction().getLabelStack().push(labelNode);
- }
-
- /**
- * Remove a label from the label stack.
- */
- private void popLabel() {
- getFunction().getLabelStack().pop();
- }
-
- /**
- * Track the current nesting of controls for break and continue.
- * @param node For, while, do or switch node.
- */
- private void pushControlNode(final Node node) {
- final boolean isLoop = node instanceof WhileNode;
- final boolean isBreakable = node instanceof BreakableNode || node instanceof Block;
- final FunctionNode function = getFunction();
- function.getControlStack().push(node);
-
- for (final LabelNode labelNode : function.getLabelStack()) {
- if (isBreakable && labelNode.getBreakNode() == null) {
- labelNode.setBreakNode(node);
- }
-
- if (isLoop && labelNode.getContinueNode() == null) {
- labelNode.setContinueNode(node);
- }
- }
- }
-
- /**
- * Finish with control.
- */
- private void popControlNode() {
- // Get control stack.
- final Stack<Node> controlStack = getFunction().getControlStack();
-
- // Can be empty if missing brace.
- if (!controlStack.isEmpty()) {
- controlStack.pop();
- }
- }
-
- private void popControlNode(final Node node) {
- // Get control stack.
- final Stack<Node> controlStack = getFunction().getControlStack();
-
- // Can be empty if missing brace.
- if (!controlStack.isEmpty() && controlStack.peek() == node) {
- controlStack.pop();
- }
- }
-
- private boolean isInWithBlock() {
- final Stack<Node> controlStack = getFunction().getControlStack();
- for (int i = controlStack.size() - 1; i >= 0; i--) {
- final Node node = controlStack.get(i);
-
- if (node instanceof WithNode) {
- return true;
- }
- }
-
- return false;
- }
-
- private <T extends Node> T findControl(final Class<T> ctype) {
- final Stack<Node> controlStack = getFunction().getControlStack();
- for (int i = controlStack.size() - 1; i >= 0; i--) {
- final Node node = controlStack.get(i);
-
- if (ctype.isAssignableFrom(node.getClass())) {
- return ctype.cast(node);
- }
- }
-
- return null;
- }
-
- private <T extends Node> List<T> findControls(final Class<T> ctype, final Node to) {
- final List<T> nodes = new ArrayList<>();
- final Stack<Node> controlStack = getFunction().getControlStack();
- for (int i = controlStack.size() - 1; i >= 0; i--) {
- final Node node = controlStack.get(i);
-
- if (to == node) {
- break; //stop looking
- }
-
- if (ctype.isAssignableFrom(node.getClass())) {
- nodes.add(ctype.cast(node));
- }
+ return new UnaryNode(source, Token.recast(firstToken, tokenType == DECPREFIX ? DECPOSTFIX : INCPOSTFIX), expression.getStart(), Token.descPosition(firstToken) + Token.descLength(firstToken), expression);
}
- return nodes;
+ return new UnaryNode(source, firstToken, expression);
}
- private <T extends Node> int countControls(final Class<T> ctype, final Node to) {
- return findControls(ctype, to).size();
- }
-
-
/**
* -----------------------------------------------------------------------
*
@@ -630,18 +512,23 @@ loop:
final long functionToken = Token.toDesc(FUNCTION, 0, source.getLength());
// Set up the script to append elements.
- final FunctionNode script = newFunctionBlock(new IdentNode(source, functionToken, Token.descPosition(functionToken), scriptName));
+ FunctionNode script = newFunctionNode(
+ functionToken,
+ new IdentNode(source, functionToken, Token.descPosition(functionToken), scriptName),
+ new ArrayList<IdentNode>(),
+ FunctionNode.Kind.SCRIPT);
- script.setKind(FunctionNode.Kind.SCRIPT);
- script.setFirstToken(functionToken);
functionDeclarations = new ArrayList<>();
sourceElements();
- script.prependStatements(functionDeclarations);
+ addFunctionDeclarations(script);
functionDeclarations = null;
+
expect(EOF);
- script.setLastToken(token);
+
script.setFinish(source.getLength() - 1);
+ script = restoreFunctionNode(script, token); //commit code
+ script = script.setBody(lc, script.getBody().setNeedsScope(lc));
return script;
}
@@ -670,24 +557,6 @@ loop:
}
/**
- * Return last node in a statement list.
- *
- * @param statements Statement list.
- *
- * @return Last (non-debug) statement or null if empty block.
- */
- private static Node lastStatement(final List<Node> statements) {
- for (int lastIndex = statements.size() - 1; lastIndex >= 0; lastIndex--) {
- final Node node = statements.get(lastIndex);
- if (!node.isDebug()) {
- return node;
- }
- }
-
- return null;
- }
-
- /**
* SourceElements :
* SourceElement
* SourceElements SourceElement
@@ -716,7 +585,7 @@ loop:
// check for directive prologues
if (checkDirective) {
// skip any debug statement like line number to get actual first line
- final Node lastStatement = lastStatement(getBlock().getStatements());
+ final Node lastStatement = lc.getLastStatement();
// get directive prologue, if any
final String directive = getDirective(lastStatement);
@@ -736,8 +605,8 @@ loop:
// handle use strict directive
if ("use strict".equals(directive)) {
isStrictMode = true;
- final FunctionNode function = getFunction();
- function.setStrictMode(true);
+ final FunctionNode function = lc.getCurrentFunction();
+ lc.setFlag(lc.getCurrentFunction(), FunctionNode.IS_STRICT);
// We don't need to check these, if lexical environment is already strict
if (!oldStrictMode && directiveStmts != null) {
@@ -759,7 +628,7 @@ loop:
}
}
} catch (final Exception e) {
- // Recover parsing.
+ //recover parsing
recover(e);
}
@@ -806,14 +675,13 @@ loop:
if (type == FUNCTION) {
// As per spec (ECMA section 12), function declarations as arbitrary statement
// is not "portable". Implementation can issue a warning or disallow the same.
- if (isStrictMode && !topLevel) {
- error(AbstractParser.message("strict.no.func.here"), token);
- }
functionExpression(true, topLevel);
return;
}
- getBlock().addStatement(lineNumberNode);
+ if (lineNumberNode != null) {
+ appendStatement(lineNumberNode);
+ }
switch (type) {
case LBRACE:
@@ -893,13 +761,9 @@ loop:
* Parse a statement block.
*/
private void block() {
- // Get statements in block.
final Block newBlock = getBlock(true);
-
// Force block execution.
- final ExecuteNode executeNode = new ExecuteNode(source, newBlock.getToken(), finish, newBlock);
-
- getBlock().addStatement(executeNode);
+ appendStatement(new ExecuteNode(source, newBlock.getToken(), finish, newBlock));
}
/**
@@ -942,7 +806,7 @@ loop:
switch (ident.getName()) {
case "eval":
case "arguments":
- error(AbstractParser.message("strict.name", ident.getName(), contextString), ident.getToken());
+ throw error(AbstractParser.message("strict.name", ident.getName(), contextString), ident.getToken());
default:
break;
}
@@ -995,8 +859,7 @@ loop:
// Allocate var node.
final VarNode var = new VarNode(source, varToken, finish, name, init);
vars.add(var);
- // Add to current block.
- getBlock().addStatement(var);
+ appendStatement(var);
if (type != COMMARIGHT) {
break;
@@ -1009,7 +872,7 @@ loop:
boolean semicolon = type == SEMICOLON;
endOfLine();
if (semicolon) {
- getBlock().setFinish(finish);
+ lc.getCurrentBlock().setFinish(finish);
}
}
@@ -1026,8 +889,7 @@ loop:
*/
private void emptyStatement() {
if (env._empty_statements) {
- getBlock().addStatement(new EmptyNode(source, token,
- Token.descPosition(token) + Token.descLength(token)));
+ appendStatement(new EmptyNode(source, token, Token.descPosition(token) + Token.descLength(token)));
}
// SEMICOLON checked in caller.
@@ -1052,7 +914,7 @@ loop:
ExecuteNode executeNode = null;
if (expression != null) {
executeNode = new ExecuteNode(source, expressionToken, finish, expression);
- getBlock().addStatement(executeNode);
+ appendStatement(executeNode);
} else {
expect(null);
}
@@ -1061,7 +923,7 @@ loop:
if (executeNode != null) {
executeNode.setFinish(finish);
- getBlock().setFinish(finish);
+ lc.getCurrentBlock().setFinish(finish);
}
}
@@ -1081,29 +943,17 @@ loop:
next();
expect(LPAREN);
-
- // Get the test expression.
final Node test = expression();
-
expect(RPAREN);
-
- // Get the pass statement.
final Block pass = getStatement();
- // Assume no else.
Block fail = null;
-
if (type == ELSE) {
next();
-
- // Get the else block.
fail = getStatement();
}
- // Construct and add new if node.
- final IfNode ifNode = new IfNode(source, ifToken, fail != null ? fail.getFinish() : pass.getFinish(), test, pass, fail);
-
- getBlock().addStatement(ifNode);
+ appendStatement(new IfNode(source, ifToken, fail != null ? fail.getFinish() : pass.getFinish(), test, pass, fail));
}
/**
@@ -1120,12 +970,12 @@ loop:
*/
private void forStatement() {
// Create FOR node, capturing FOR token.
- final ForNode forNode = new ForNode(source, token, Token.descPosition(token));
+ ForNode forNode = new ForNode(source, token, Token.descPosition(token), null, null, null, null, ForNode.IS_FOR);
- pushControlNode(forNode);
// Set up new block for scope of vars. Captures first token.
- final Block outer = newBlock();
+ Block outer = newBlock();
+ lc.push(forNode);
try {
// FOR tested in caller.
@@ -1134,31 +984,97 @@ loop:
// Nashorn extension: for each expression.
// iterate property values rather than property names.
if (!env._no_syntax_extensions && type == IDENT && "each".equals(getValue())) {
- forNode.setIsForEach();
+ forNode = forNode.setIsForEach(lc);
next();
}
expect(LPAREN);
- /// Capture control information.
- forControl(forNode);
+ List<VarNode> vars = null;
+
+ switch (type) {
+ case VAR:
+ // Var statements captured in for outer block.
+ vars = variableStatement(false);
+ break;
+ case SEMICOLON:
+ break;
+ default:
+ final Node expression = expression(unaryExpression(), COMMARIGHT.getPrecedence(), true);
+ forNode = forNode.setInit(lc, expression);
+ break;
+ }
+
+ switch (type) {
+ case SEMICOLON:
+ // for (init; test; modify)
+ expect(SEMICOLON);
+ if (type != SEMICOLON) {
+ forNode = forNode.setTest(lc, expression());
+ }
+ expect(SEMICOLON);
+ if (type != RPAREN) {
+ forNode = forNode.setModify(lc, expression());
+ }
+ break;
+
+ case IN:
+ forNode = forNode.setIsForIn(lc);
+ if (vars != null) {
+ // for (var i in obj)
+ if (vars.size() == 1) {
+ forNode = forNode.setInit(lc, new IdentNode(vars.get(0).getName()));
+ } else {
+ // for (var i, j in obj) is invalid
+ throw error(AbstractParser.message("many.vars.in.for.in.loop"), vars.get(1).getToken());
+ }
+
+ } else {
+ // for (expr in obj)
+ final Node init = forNode.getInit();
+ assert init != null : "for..in init expression can not be null here";
+
+ // check if initial expression is a valid L-value
+ if (!(init instanceof AccessNode ||
+ init instanceof IndexNode ||
+ init instanceof IdentNode)) {
+ throw error(AbstractParser.message("not.lvalue.for.in.loop"), init.getToken());
+ }
+
+ if (init instanceof IdentNode) {
+ if (!checkIdentLValue((IdentNode)init)) {
+ throw error(AbstractParser.message("not.lvalue.for.in.loop"), init.getToken());
+ }
+ verifyStrictIdent((IdentNode)init, "for-in iterator");
+ }
+ }
+
+ next();
+
+ // Get the collection expression.
+ forNode = forNode.setModify(lc, expression());
+ break;
+
+ default:
+ expect(SEMICOLON);
+ break;
+ }
expect(RPAREN);
// Set the for body.
final Block body = getStatement();
- forNode.setBody(body);
+ forNode = forNode.setBody(lc, body);
forNode.setFinish(body.getFinish());
outer.setFinish(body.getFinish());
- // Add for to current block.
- getBlock().addStatement(forNode);
+ appendStatement(forNode);
} finally {
- restoreBlock(outer);
- popControlNode();
+ lc.pop(forNode);
+ outer = restoreBlock(outer);
}
- getBlock().addStatement(new ExecuteNode(source, outer.getToken(), outer.getFinish(), outer));
+ appendStatement(new ExecuteNode(source, outer.getToken(), outer.getFinish(), outer));
}
/**
@@ -1175,87 +1091,7 @@ loop:
* comprehensions.
* @param forNode Owning FOR.
*/
- private void forControl(final ForNode forNode) {
- List<VarNode> vars = null;
-
- switch (type) {
- case VAR:
- // Var statements captured in for outer block.
- vars = variableStatement(false);
- break;
-
- case SEMICOLON:
- break;
-
- default:
- final Node expression = expression(unaryExpression(), COMMARIGHT.getPrecedence(), true);
- forNode.setInit(expression);
- }
-
- switch (type) {
- case SEMICOLON:
- // for (init; test; modify)
- expect(SEMICOLON);
-
- // Get the test expression.
- if (type != SEMICOLON) {
- forNode.setTest(expression());
- }
-
- expect(SEMICOLON);
-
- // Get the modify expression.
- if (type != RPAREN) {
- final Node expression = expression();
- forNode.setModify(expression);
- }
-
- break;
-
- case IN:
- forNode.setIsForIn();
- if (vars != null) {
- // for (var i in obj)
- if (vars.size() == 1) {
- forNode.setInit(new IdentNode(vars.get(0).getName()));
- } else {
- // for (var i, j in obj) is invalid
- error(AbstractParser.message("many.vars.in.for.in.loop"), vars.get(1).getToken());
- }
-
- } else {
- // for (expr in obj)
- final Node init = forNode.getInit();
- assert init != null : "for..in init expression can not be null here";
-
- // check if initial expression is a valid L-value
- if (!(init instanceof AccessNode ||
- init instanceof IndexNode ||
- init instanceof IdentNode)) {
- error(AbstractParser.message("not.lvalue.for.in.loop"), init.getToken());
- }
-
- if (init instanceof IdentNode) {
- if (!checkIdentLValue((IdentNode)init)) {
- error(AbstractParser.message("not.lvalue.for.in.loop"), init.getToken());
- }
- verifyStrictIdent((IdentNode)init, "for-in iterator");
- }
- }
-
- next();
-
- // Get the collection expression.
- forNode.setModify(expression());
- break;
-
- default:
- expect(SEMICOLON);
- break;
- }
-
- }
/**
* ...IterationStatement :
@@ -1274,27 +1110,17 @@ loop:
next();
// Construct WHILE node.
- final WhileNode whileNode = new WhileNode(source, whileToken, Token.descPosition(whileToken));
- pushControlNode(whileNode);
+ WhileNode whileNode = new WhileNode(source, whileToken, Token.descPosition(whileToken), false);
+ lc.push(whileNode);
try {
expect(LPAREN);
-
- // Get the test expression.
- final Node test = expression();
- whileNode.setTest(test);
-
+ whileNode = whileNode.setTest(lc, expression());
expect(RPAREN);
-
- // Get WHILE body.
- final Block statements = getStatement();
- whileNode.setBody(statements);
- whileNode.setFinish(statements.getFinish());
-
- // Add WHILE node.
- getBlock().addStatement(whileNode);
+ whileNode = whileNode.setBody(lc, getStatement());
+ appendStatement(whileNode);
} finally {
- popControlNode();
+ lc.pop(whileNode);
}
}
@@ -1314,34 +1140,25 @@ loop:
// DO tested in the caller.
next();
- final WhileNode doWhileNode = new DoWhileNode(source, doToken, Token.descPosition(doToken));
- pushControlNode(doWhileNode);
+ WhileNode doWhileNode = new WhileNode(source, doToken, Token.descPosition(doToken), true);
+ lc.push(doWhileNode);
try {
// Get DO body.
- final Block statements = getStatement();
- doWhileNode.setBody(statements);
+ doWhileNode = doWhileNode.setBody(lc, getStatement());
expect(WHILE);
-
expect(LPAREN);
-
- // Get the test expression.
- final Node test = expression();
- doWhileNode.setTest(test);
-
+ doWhileNode = doWhileNode.setTest(lc, expression());
expect(RPAREN);
if (type == SEMICOLON) {
endOfLine();
}
-
doWhileNode.setFinish(finish);
-
- // Add DO node.
- getBlock().addStatement(doWhileNode);
+ appendStatement(doWhileNode);
} finally {
- popControlNode();
+ lc.pop(doWhileNode);
}
}
@@ -1370,28 +1187,26 @@ loop:
default:
final IdentNode ident = getIdent();
- labelNode = findLabel(ident);
+ labelNode = lc.findLabel(ident.getName());
if (labelNode == null) {
- error(AbstractParser.message("undefined.label", ident.getName()), ident.getToken());
+ throw error(AbstractParser.message("undefined.label", ident.getName()), ident.getToken());
}
break;
}
- final Node targetNode = labelNode != null ? labelNode.getContinueNode() : findControl(WhileNode.class);
+ final IdentNode label = labelNode == null ? null : labelNode.getLabel();
+ final LoopNode targetNode = lc.getContinueTo(label);
if (targetNode == null) {
- error(AbstractParser.message("illegal.continue.stmt"), continueToken);
+ throw error(AbstractParser.message("illegal.continue.stmt"), continueToken);
}
endOfLine();
// Construct and add CONTINUE node.
- final ContinueNode continueNode = new ContinueNode(source, continueToken, finish, labelNode, targetNode, findControl(TryNode.class));
- continueNode.setScopeNestingLevel(countControls(WithNode.class, targetNode));
-
- getBlock().addStatement(continueNode);
+ appendStatement(new ContinueNode(source, continueToken, finish, label == null ? null : new IdentNode(label)));
}
/**
@@ -1418,28 +1233,27 @@ loop:
default:
final IdentNode ident = getIdent();
- labelNode = findLabel(ident);
+ labelNode = lc.findLabel(ident.getName());
if (labelNode == null) {
- error(AbstractParser.message("undefined.label", ident.getName()), ident.getToken());
+ throw error(AbstractParser.message("undefined.label", ident.getName()), ident.getToken());
}
break;
}
- final Node targetNode = labelNode != null ? labelNode.getBreakNode() : findControl(BreakableNode.class);
-
+ //either an explicit label - then get its node or just a "break" - get first breakable
+ //targetNode is what we are breaking out from.
+ final IdentNode label = labelNode == null ? null : labelNode.getLabel();
+ final BreakableNode targetNode = lc.getBreakable(label);
if (targetNode == null) {
- error(AbstractParser.message("illegal.break.stmt"), breakToken);
+ throw error(AbstractParser.message("illegal.break.stmt"), breakToken);
}
endOfLine();
// Construct and add BREAK node.
- final BreakNode breakNode = new BreakNode(source, breakToken, finish, labelNode, targetNode, findControl(TryNode.class));
- breakNode.setScopeNestingLevel(countControls(WithNode.class, targetNode));
-
- getBlock().addStatement(breakNode);
+ appendStatement(new BreakNode(source, breakToken, finish, label == null ? null : new IdentNode(label)));
}
/**
@@ -1452,8 +1266,8 @@ loop:
*/
private void returnStatement() {
// check for return outside function
- if (getFunction().getKind() == FunctionNode.Kind.SCRIPT) {
- error(AbstractParser.message("invalid.return"));
+ if (lc.getCurrentFunction().getKind() == FunctionNode.Kind.SCRIPT) {
+ throw error(AbstractParser.message("invalid.return"));
}
// Capture RETURN token.
@@ -1478,8 +1292,7 @@ loop:
endOfLine();
// Construct and add RETURN node.
- final ReturnNode returnNode = new ReturnNode(source, returnToken, finish, expression, findControl(TryNode.class));
- getBlock().addStatement(returnNode);
+ appendStatement(new ReturnNode(source, returnToken, finish, expression));
}
/**
@@ -1513,8 +1326,7 @@ loop:
endOfLine();
// Construct and add YIELD node.
- final ReturnNode yieldNode = new ReturnNode(source, yieldToken, finish, expression, findControl(TryNode.class));
- getBlock().addStatement(yieldNode);
+ appendStatement(new ReturnNode(source, yieldToken, finish, expression));
}
/**
@@ -1533,35 +1345,23 @@ loop:
// ECMA 12.10.1 strict mode restrictions
if (isStrictMode) {
- error(AbstractParser.message("strict.no.with"), withToken);
+ throw error(AbstractParser.message("strict.no.with"), withToken);
}
// Get WITH expression.
- final WithNode withNode = new WithNode(source, withToken, finish, null, null);
- final Iterator<FunctionNode> it = lexicalContext.getFunctions();
- if(it.hasNext()) {
- it.next().setHasWith(it);
- }
+ WithNode withNode = new WithNode(source, withToken, finish);
try {
- pushControlNode(withNode);
-
+ lc.push(withNode);
expect(LPAREN);
-
- final Node expression = expression();
- withNode.setExpression(expression);
-
+ withNode = withNode.setExpression(lc, expression());
expect(RPAREN);
-
- // Get WITH body.
- final Block statements = getStatement();
- withNode.setBody(statements);
- withNode.setFinish(finish);
+ withNode = withNode.setBody(lc, getStatement());
} finally {
- popControlNode(withNode);
+ lc.pop(withNode);
}
- getBlock().addStatement(withNode);
+ appendStatement(withNode);
}
/**
@@ -1587,22 +1387,17 @@ loop:
* Parse SWITCH statement.
*/
private void switchStatement() {
- // Capture SWITCH token.
final long switchToken = token;
// SWITCH tested in caller.
next();
// Create and add switch statement.
- final SwitchNode switchNode = new SwitchNode(source, switchToken, Token.descPosition(switchToken));
- pushControlNode(switchNode);
+ SwitchNode switchNode = new SwitchNode(source, switchToken, Token.descPosition(switchToken), null, new ArrayList<CaseNode>(), null);
+ lc.push(switchNode);
try {
expect(LPAREN);
-
- // Get switch expression.
- final Node switchExpression = expression();
- switchNode.setExpression(switchExpression);
-
+ switchNode = switchNode.setExpression(lc, expression());
expect(RPAREN);
expect(LBRACE);
@@ -1619,19 +1414,14 @@ loop:
switch (type) {
case CASE:
next();
-
- // Get case expression.
caseExpression = expression();
-
break;
case DEFAULT:
if (defaultCase != null) {
- error(AbstractParser.message("duplicate.default.in.switch"));
+ throw error(AbstractParser.message("duplicate.default.in.switch"));
}
-
next();
-
break;
default:
@@ -1654,16 +1444,13 @@ loop:
cases.add(caseNode);
}
- switchNode.setCases(cases);
- switchNode.setDefaultCase(defaultCase);
-
+ switchNode = switchNode.setCases(lc, cases, defaultCase);
next();
-
switchNode.setFinish(finish);
- getBlock().addStatement(switchNode);
+ appendStatement(switchNode);
} finally {
- popControlNode();
+ lc.pop(switchNode);
}
}
@@ -1683,23 +1470,19 @@ loop:
expect(COLON);
- if (findLabel(ident) != null) {
- error(AbstractParser.message("duplicate.label", ident.getName()), labelToken);
+ if (lc.findLabel(ident.getName()) != null) {
+ throw error(AbstractParser.message("duplicate.label", ident.getName()), labelToken);
}
+ LabelNode labelNode = new LabelNode(source, labelToken, finish, ident, null);
try {
- // Create and add label.
- final LabelNode labelNode = new LabelNode(source, labelToken, finish, ident, null);
- pushLabel(labelNode);
- // Get and save body.
- final Block statements = getStatement();
- labelNode.setBody(statements);
+ lc.push(labelNode);
+ labelNode = labelNode.setBody(lc, getStatement());
labelNode.setFinish(finish);
-
- getBlock().addStatement(labelNode);
+ appendStatement(labelNode);
} finally {
- // Remove label.
- popLabel();
+ assert lc.peek() instanceof LabelNode;
+ lc.pop(labelNode);
}
}
@@ -1732,14 +1515,12 @@ loop:
}
if (expression == null) {
- error(AbstractParser.message("expected.operand", type.getNameOrType()));
+ throw error(AbstractParser.message("expected.operand", type.getNameOrType()));
}
endOfLine();
- // Construct and add THROW node.
- final ThrowNode throwNode = new ThrowNode(source, throwToken, finish, expression, findControl(TryNode.class));
- getBlock().addStatement(throwNode);
+ appendStatement(new ThrowNode(source, throwToken, finish, expression));
}
/**
@@ -1766,28 +1547,18 @@ loop:
next();
// Container block needed to act as target for labeled break statements
- final Block outer = newBlock();
- pushControlNode(outer);
+ Block outer = newBlock();
// Create try.
- final TryNode tryNode = new TryNode(source, tryToken, Token.descPosition(tryToken), findControl(TryNode.class));
- pushControlNode(tryNode);
try {
- // Get TRY body.
- final Block tryBody = getBlock(true);
-
- // Prepare to accumulate catches.
+ final Block tryBody = getBlock(true);
final List<Block> catchBlocks = new ArrayList<>();
while (type == CATCH) {
- // Capture CATCH token.
final long catchToken = token;
next();
-
expect(LPAREN);
-
- // Get exception ident.
final IdentNode exception = getIdent();
// ECMA 12.4.1 strict mode restrictions
@@ -1795,28 +1566,23 @@ loop:
// Check for conditional catch.
Node ifExpression = null;
-
if (type == IF) {
next();
-
// Get the exception condition.
ifExpression = expression();
}
expect(RPAREN);
- final Block catchBlock = newBlock();
+ Block catchBlock = newBlock();
try {
-
// Get CATCH body.
final Block catchBody = getBlock(true);
-
- // Create and add catch.
final CatchNode catchNode = new CatchNode(source, catchToken, finish, exception, ifExpression, catchBody);
- getBlock().addStatement(catchNode);
- catchBlocks.add(catchBlock);
+ appendStatement(catchNode);
} finally {
- restoreBlock(catchBlock);
+ catchBlock = restoreBlock(catchBlock);
+ catchBlocks.add(catchBlock);
}
// If unconditional catch then should to be the end.
@@ -1825,38 +1591,32 @@ loop:
}
}
- popControlNode();
-
// Prepare to capture finally statement.
Block finallyStatements = null;
if (type == FINALLY) {
next();
-
- // Get FINALLY body.
finallyStatements = getBlock(true);
}
// Need at least one catch or a finally.
if (catchBlocks.isEmpty() && finallyStatements == null) {
- error(AbstractParser.message("missing.catch.or.finally"), tryToken);
+ throw error(AbstractParser.message("missing.catch.or.finally"), tryToken);
}
- tryNode.setBody(tryBody);
- tryNode.setCatchBlocks(catchBlocks);
- tryNode.setFinallyBody(finallyStatements);
+ final TryNode tryNode = new TryNode(source, tryToken, Token.descPosition(tryToken), tryBody, catchBlocks, finallyStatements);
+ // Add try.
+ assert lc.peek() == outer;
+ appendStatement(tryNode);
+
tryNode.setFinish(finish);
outer.setFinish(finish);
- // Add try.
- outer.addStatement(tryNode);
} finally {
- popControlNode(tryNode);
- restoreBlock(outer);
- popControlNode(outer);
+ outer = restoreBlock(outer);
}
- getBlock().addStatement(new ExecuteNode(source, outer.getToken(), outer.getFinish(), outer));
+ appendStatement(new ExecuteNode(source, outer.getToken(), outer.getFinish(), outer));
}
/**
@@ -1872,11 +1632,8 @@ loop:
final long debuggerToken = token;
// DEBUGGER tested in caller.
next();
-
endOfLine();
-
- final RuntimeNode runtimeNode = new RuntimeNode(source, debuggerToken, finish, RuntimeNode.Request.DEBUGGER, new ArrayList<Node>());
- getBlock().addStatement(runtimeNode);
+ appendStatement(new RuntimeNode(source, debuggerToken, finish, RuntimeNode.Request.DEBUGGER, new ArrayList<Node>()));
}
/**
@@ -1912,7 +1669,7 @@ loop:
return ident;
case OCTAL:
if (isStrictMode) {
- error(AbstractParser.message("strict.no.octal"), token);
+ throw error(AbstractParser.message("strict.no.octal"), token);
}
case STRING:
case ESCSTRING:
@@ -2036,7 +1793,7 @@ loop:
default:
if (!elision) {
- error(AbstractParser.message("expected.comma", type.getNameOrType()));
+ throw error(AbstractParser.message("expected.comma", type.getNameOrType()));
}
// Add expression element.
final Node expression = assignmentExpression(false);
@@ -2077,8 +1834,8 @@ loop:
// Object context.
// Prepare to accumulate elements.
- final List<Node> elements = new ArrayList<>();
- final Map<Object, PropertyNode> map = new HashMap<>();
+ // final List<Node> elements = new ArrayList<>();
+ final Map<String, PropertyNode> map = new LinkedHashMap<>();
// Create a block for the object literal.
boolean commaSeen = true;
@@ -2096,25 +1853,30 @@ loop:
default:
if (!commaSeen) {
- error(AbstractParser.message("expected.comma", type.getNameOrType()));
- }
+ throw error(AbstractParser.message("expected.comma", type.getNameOrType()));
+ }
- commaSeen = false;
- // Get and add the next property.
- final PropertyNode property = propertyAssignment();
- final Object key = property.getKeyName();
- final PropertyNode existingProperty = map.get(key);
+ commaSeen = false;
+ // Get and add the next property.
+ final PropertyNode property = propertyAssignment();
+ final String key = property.getKeyName();
+ final PropertyNode existingProperty = map.get(key);
+
+ if (existingProperty == null) {
+ map.put(key, property);
+ // elements.add(property);
+ break;
+ }
- if (existingProperty != null) {
// ECMA section 11.1.5 Object Initialiser
// point # 4 on property assignment production
- final Node value = property.getValue();
- final Node getter = property.getGetter();
- final Node setter = property.getSetter();
+ final Node value = property.getValue();
+ final FunctionNode getter = property.getGetter();
+ final FunctionNode setter = property.getSetter();
- final Node prevValue = existingProperty.getValue();
- final Node prevGetter = existingProperty.getGetter();
- final Node prevSetter = existingProperty.getSetter();
+ final Node prevValue = existingProperty.getValue();
+ final FunctionNode prevGetter = existingProperty.getGetter();
+ final FunctionNode prevSetter = existingProperty.getSetter();
boolean redefinitionOk = true;
// ECMA 11.1.5 strict mode restrictions
@@ -2125,7 +1887,7 @@ loop:
}
final boolean isPrevAccessor = prevGetter != null || prevSetter != null;
- final boolean isAccessor = getter != null || setter != null;
+ final boolean isAccessor = getter != null || setter != null;
// data property redefined as accessor property
if (prevValue != null && isAccessor) {
@@ -2145,40 +1907,33 @@ loop:
}
if (!redefinitionOk) {
- error(AbstractParser.message("property.redefinition", key.toString()), property.getToken());
+ throw error(AbstractParser.message("property.redefinition", key.toString()), property.getToken());
}
+ PropertyNode newProperty = existingProperty;
if (value != null) {
- final Node existingValue = existingProperty.getValue();
-
- if (existingValue == null) {
- existingProperty.setValue(value);
+ if (prevValue == null) {
+ map.put(key, newProperty = newProperty.setValue(value));
} else {
- final long propertyToken = Token.recast(existingProperty.getToken(), COMMARIGHT);
- existingProperty.setValue(new BinaryNode(source, propertyToken, existingValue, value));
+ final long propertyToken = Token.recast(newProperty.getToken(), COMMARIGHT);
+ map.put(key, newProperty = newProperty.setValue(new BinaryNode(source, propertyToken, prevValue, value)));
}
- existingProperty.setGetter(null);
- existingProperty.setSetter(null);
+ map.put(key, newProperty = newProperty.setGetter(null).setSetter(null));
}
if (getter != null) {
- existingProperty.setGetter(getter);
+ map.put(key, newProperty = newProperty.setGetter(getter));
}
if (setter != null) {
- existingProperty.setSetter(setter);
+ map.put(key, newProperty = newProperty.setSetter(setter));
}
- } else {
- map.put(key, property);
- elements.add(property);
- }
-
- break;
+ break;
}
}
- return new ObjectNode(source, objectToken, finish, elements);
+ return new ObjectNode(source, objectToken, finish, new ArrayList<Node>(map.values()));
}
/**
@@ -2198,7 +1953,7 @@ loop:
return getIdent();
case OCTAL:
if (isStrictMode) {
- error(AbstractParser.message("strict.no.octal"), token);
+ throw error(AbstractParser.message("strict.no.octal"), token);
}
case STRING:
case ESCSTRING:
@@ -2235,8 +1990,6 @@ loop:
final long propertyToken = token;
FunctionNode functionNode;
- List<IdentNode> parameters;
- PropertyNode propertyNode;
PropertyKey propertyName;
if (type == IDENT) {
@@ -2253,11 +2006,8 @@ loop:
final IdentNode getNameNode = new IdentNode(source, ((Node)getIdent).getToken(), finish, "get " + getterName);
expect(LPAREN);
expect(RPAREN);
- parameters = new ArrayList<>();
- functionNode = functionBody(getSetToken, getNameNode, parameters, FunctionNode.Kind.GETTER);
- propertyNode = new PropertyNode(source, propertyToken, finish, getIdent, null);
- propertyNode.setGetter(functionNode);
- return propertyNode;
+ functionNode = functionBody(getSetToken, getNameNode, new ArrayList<IdentNode>(), FunctionNode.Kind.GETTER);
+ return new PropertyNode(source, propertyToken, finish, getIdent, null, functionNode, null);
case "set":
final PropertyKey setIdent = propertyName();
@@ -2267,12 +2017,10 @@ loop:
final IdentNode argIdent = getIdent();
verifyStrictIdent(argIdent, "setter argument");
expect(RPAREN);
- parameters = new ArrayList<>();
+ List<IdentNode> parameters = new ArrayList<>();
parameters.add(argIdent);
functionNode = functionBody(getSetToken, setNameNode, parameters, FunctionNode.Kind.SETTER);
- propertyNode = new PropertyNode(source, propertyToken, finish, setIdent, null);
- propertyNode.setSetter(functionNode);
- return propertyNode;
+ return new PropertyNode(source, propertyToken, finish, setIdent, null, null, functionNode);
default:
break;
@@ -2286,9 +2034,7 @@ loop:
expect(COLON);
- final Node value = assignmentExpression(false);
- propertyNode = new PropertyNode(source, propertyToken, finish, propertyName, value);
- return propertyNode;
+ return new PropertyNode(source, propertyToken, finish, propertyName, assignmentExpression(false), null, null);
}
/**
@@ -2321,9 +2067,6 @@ loop:
}
lhs = new CallNode(source, callToken, finish, lhs, arguments);
- if (isInWithBlock()) {
- ((CallNode)lhs).setInWithBlock();
- }
}
loop:
@@ -2338,9 +2081,6 @@ loop:
// Create call node.
lhs = new CallNode(source, callToken, finish, lhs, arguments);
- if (isInWithBlock()) {
- ((CallNode)lhs).setInWithBlock();
- }
break;
@@ -2420,9 +2160,6 @@ loop:
}
final CallNode callNode = new CallNode(source, constructor.getToken(), finish, constructor, arguments);
- if (isInWithBlock()) {
- callNode.setInWithBlock();
- }
return new UnaryNode(source, newToken, callNode);
}
@@ -2482,8 +2219,7 @@ loop:
case PERIOD:
if (lhs == null) {
- error(AbstractParser.message("expected.operand", type.getNameOrType()));
- return null;
+ throw error(AbstractParser.message("expected.operand", type.getNameOrType()));
}
next();
@@ -2585,29 +2321,33 @@ loop:
}
expect(LPAREN);
-
final List<IdentNode> parameters = formalParameterList();
-
expect(RPAREN);
- final FunctionNode functionNode = functionBody(functionToken, name, parameters, FunctionNode.Kind.NORMAL);
+ FunctionNode functionNode = functionBody(functionToken, name, parameters, FunctionNode.Kind.NORMAL);
if (isStatement) {
- if(topLevel) {
- functionNode.setIsDeclared();
+ if (topLevel) {
+ functionNode = functionNode.setFlag(lc, FunctionNode.IS_DECLARED);
+ } else if (isStrictMode) {
+ throw error(JSErrorType.SYNTAX_ERROR, AbstractParser.message("strict.no.func.decl.here"), functionToken);
+ } else if (env._function_statement == ScriptEnvironment.FunctionStatementBehavior.ERROR) {
+ throw error(JSErrorType.SYNTAX_ERROR, AbstractParser.message("no.func.decl.here"), functionToken);
+ } else if (env._function_statement == ScriptEnvironment.FunctionStatementBehavior.WARNING) {
+ warning(JSErrorType.SYNTAX_ERROR, AbstractParser.message("no.func.decl.here.warn"), functionToken);
}
- if(ARGUMENTS.tag().equals(name.getName())) {
- getFunction().setDefinesArguments();
+ if (ARGUMENTS.symbolName().equals(name.getName())) {
+ lc.setFlag(lc.getCurrentFunction(), FunctionNode.DEFINES_ARGUMENTS);
}
}
if (isAnonymous) {
- functionNode.setIsAnonymous();
+ functionNode = functionNode.setFlag(lc, FunctionNode.IS_ANONYMOUS);
}
final int arity = parameters.size();
- final boolean strict = functionNode.isStrictMode();
+ final boolean strict = functionNode.isStrict();
if (arity > 1) {
final HashSet<String> parametersSet = new HashSet<>(arity);
@@ -2615,39 +2355,37 @@ loop:
final IdentNode parameter = parameters.get(i);
String parameterName = parameter.getName();
- if (ARGUMENTS.tag().equals(parameterName)) {
- functionNode.setDefinesArguments();
+ if (ARGUMENTS.symbolName().equals(parameterName)) {
+ functionNode = functionNode.setFlag(lc, FunctionNode.DEFINES_ARGUMENTS);
}
if (parametersSet.contains(parameterName)) {
// redefinition of parameter name
if (strict) {
- error(AbstractParser.message("strict.param.redefinition", parameterName), parameter.getToken());
- } else {
- // rename in non-strict mode
- parameterName = functionNode.uniqueName(parameterName);
- final long parameterToken = parameter.getToken();
- parameters.set(i, new IdentNode(source, parameterToken, Token.descPosition(parameterToken), functionNode.uniqueName(parameterName)));
+ throw error(AbstractParser.message("strict.param.redefinition", parameterName), parameter.getToken());
}
+ // rename in non-strict mode
+ parameterName = functionNode.uniqueName(parameterName);
+ final long parameterToken = parameter.getToken();
+ parameters.set(i, new IdentNode(source, parameterToken, Token.descPosition(parameterToken), functionNode.uniqueName(parameterName)));
}
parametersSet.add(parameterName);
}
} else if (arity == 1) {
- if (ARGUMENTS.tag().equals(parameters.get(0).getName())) {
- functionNode.setDefinesArguments();
+ if (ARGUMENTS.symbolName().equals(parameters.get(0).getName())) {
+ functionNode = functionNode.setFlag(lc, FunctionNode.DEFINES_ARGUMENTS);
}
}
if (isStatement) {
- final VarNode varNode = new VarNode(source, functionToken, finish, name, functionNode, true);
- if(topLevel) {
+ final VarNode varNode = new VarNode(source, functionToken, finish, name, functionNode, VarNode.IS_STATEMENT);
+ if (topLevel) {
functionDeclarations.add(lineNumber);
functionDeclarations.add(varNode);
} else {
- final Block block = getBlock();
- block.addStatement(lineNumber);
- block.addStatement(varNode);
+ appendStatement(lineNumber);
+ appendStatement(varNode);
}
}
@@ -2701,13 +2439,11 @@ loop:
*/
private FunctionNode functionBody(final long firstToken, final IdentNode ident, final List<IdentNode> parameters, final FunctionNode.Kind kind) {
FunctionNode functionNode = null;
+ long lastToken = 0L;
try {
// Create a new function block.
- functionNode = newFunctionBlock(ident);
- functionNode.setParameters(parameters);
- functionNode.setKind(kind);
- functionNode.setFirstToken(firstToken);
+ functionNode = newFunctionNode(firstToken, ident, parameters, kind);
// Nashorn extension: expression closures
if (!env._no_syntax_extensions && type != LBRACE) {
@@ -2720,14 +2456,12 @@ loop:
// just expression as function body
final Node expr = expression();
-
+ assert lc.getCurrentBlock() == lc.getFunctionBody(functionNode);
// create a return statement - this creates code in itself and does not need to be
// wrapped into an ExecuteNode
- final ReturnNode returnNode = new ReturnNode(source, expr.getToken(), finish, expr, null);
-
- // add the return statement
- functionNode.addStatement(returnNode);
- functionNode.setLastToken(token);
+ final ReturnNode returnNode = new ReturnNode(source, expr.getToken(), finish, expr);
+ appendStatement(returnNode);
+ lastToken = token;
functionNode.setFinish(Token.descPosition(token) + Token.descLength(token));
} else {
@@ -2738,23 +2472,35 @@ loop:
functionDeclarations = new ArrayList<>();
try {
sourceElements();
- functionNode.prependStatements(functionDeclarations);
+ addFunctionDeclarations(functionNode);
} finally {
functionDeclarations = prevFunctionDecls;
}
- functionNode.setLastToken(token);
+ lastToken = token;
expect(RBRACE);
functionNode.setFinish(finish);
}
} finally {
- restoreBlock(functionNode);
+ functionNode = restoreFunctionNode(functionNode, lastToken);
}
-
return functionNode;
}
+ private void addFunctionDeclarations(final FunctionNode functionNode) {
+ assert lc.peek() == lc.getFunctionBody(functionNode);
+ VarNode lastDecl = null;
+ for (int i = functionDeclarations.size() - 1; i >= 0; i--) {
+ Node decl = functionDeclarations.get(i);
+ if (lastDecl == null && decl instanceof VarNode) {
+ decl = lastDecl = ((VarNode)decl).setFlag(VarNode.IS_LAST_FUNCTION_DECLARATION);
+ lc.setFlag(functionNode, FunctionNode.HAS_FUNCTION_DECLARATIONS);
+ }
+ prependStatement(decl);
+ }
+ }
+
private RuntimeNode referenceError(final Node lhs, final Node rhs) {
final ArrayList<Node> args = new ArrayList<>();
args.add(lhs);
@@ -2764,9 +2510,7 @@ loop:
args.add(rhs);
}
args.add(LiteralNode.newInstance(source, lhs.getToken(), lhs.getFinish(), lhs.toString()));
- final RuntimeNode runtimeNode = new RuntimeNode(source, lhs.getToken(),
- lhs.getFinish(), RuntimeNode.Request.REFERENCE_ERROR, args);
- return runtimeNode;
+ return new RuntimeNode(source, lhs.getToken(), lhs.getFinish(), RuntimeNode.Request.REFERENCE_ERROR, args);
}
/*
@@ -2815,19 +2559,7 @@ loop:
case BIT_NOT:
case NOT:
next();
-
final Node expr = unaryExpression();
-
- /*
- // Not sure if "delete <ident>" is a compile-time error or a
- // runtime error in strict mode.
-
- if (isStrictMode) {
- if (unaryTokenType == DELETE && expr instanceof IdentNode) {
- error(message("strict.cant.delete.ident", ((IdentNode)expr).getName()), expr.getToken());
- }
- }
- */
return new UnaryNode(source, unaryToken, expr);
case INCPREFIX:
@@ -2890,7 +2622,7 @@ loop:
}
if (expression == null) {
- error(AbstractParser.message("expected.operand", type.getNameOrType()));
+ throw error(AbstractParser.message("expected.operand", type.getNameOrType()));
}
return expression;
@@ -2992,6 +2724,7 @@ loop:
// Include commas in expression parsing.
return expression(unaryExpression(), COMMARIGHT.getPrecedence(), false);
}
+
private Node expression(final Node exprLhs, final int minPrecedence, final boolean noIn) {
// Get the precedence of the next operator.
int precedence = type.getPrecedence();
@@ -3087,11 +2820,26 @@ loop:
return "[JavaScript Parsing]";
}
- private Block getBlock() {
- return lexicalContext.getCurrentBlock();
+ private static void markEval(final LexicalContext lc) {
+ final Iterator<FunctionNode> iter = lc.getFunctions();
+ boolean flaggedCurrentFn = false;
+ while (iter.hasNext()) {
+ final FunctionNode fn = iter.next();
+ if (!flaggedCurrentFn) {
+ lc.setFlag(fn, FunctionNode.HAS_EVAL);
+ flaggedCurrentFn = true;
+ } else {
+ lc.setFlag(fn, FunctionNode.HAS_NESTED_EVAL);
+ }
+ lc.setFlag(lc.getFunctionBody(fn), Block.NEEDS_SCOPE);
+ }
+ }
+
+ private void prependStatement(final Node statement) {
+ lc.prependStatement(statement);
}
- private FunctionNode getFunction() {
- return lexicalContext.getCurrentFunction();
+ private void appendStatement(final Node statement) {
+ lc.appendStatement(statement);
}
}
diff --git a/src/jdk/nashorn/internal/parser/TokenType.java b/src/jdk/nashorn/internal/parser/TokenType.java
index 9a2b7b2b..657c8c46 100644
--- a/src/jdk/nashorn/internal/parser/TokenType.java
+++ b/src/jdk/nashorn/internal/parser/TokenType.java
@@ -280,6 +280,11 @@ public enum TokenType {
return values;
}
+ @Override
+ public String toString() {
+ return name;
+ }
+
static {
// Avoid cloning of enumeration.
values = TokenType.values();
diff --git a/src/jdk/nashorn/internal/runtime/AccessorProperty.java b/src/jdk/nashorn/internal/runtime/AccessorProperty.java
index 996bea35..b0ca46f7 100644
--- a/src/jdk/nashorn/internal/runtime/AccessorProperty.java
+++ b/src/jdk/nashorn/internal/runtime/AccessorProperty.java
@@ -54,10 +54,24 @@ import jdk.nashorn.internal.lookup.MethodHandleFactory;
* @see SpillProperty
*/
public class AccessorProperty extends Property {
+ private static final MethodHandles.Lookup lookup = MethodHandles.lookup();
private static final MethodHandle REPLACE_MAP = findOwnMH("replaceMap", Object.class, Object.class, PropertyMap.class, String.class, Class.class, Class.class);
private static final int NOOF_TYPES = getNumberOfAccessorTypes();
+ /**
+ * Properties in different maps for the same structure class will share their field getters and setters. This could
+ * be further extended to other method handles that are looked up in the AccessorProperty constructor, but right now
+ * these are the most frequently retrieved ones, and lookup of method handle natives only registers in the profiler
+ * for them.
+ */
+ private static ClassValue<GettersSetters> GETTERS_SETTERS = new ClassValue<GettersSetters>() {
+ @Override
+ protected GettersSetters computeValue(Class<?> structure) {
+ return new GettersSetters(structure);
+ }
+ };
+
/** Property getter cache */
private MethodHandle[] getters = new MethodHandle[NOOF_TYPES];
@@ -152,6 +166,22 @@ public class AccessorProperty extends Property {
setCurrentType(getterType);
}
+ private static class GettersSetters {
+ final MethodHandle[] getters;
+ final MethodHandle[] setters;
+
+ public GettersSetters(Class<?> structure) {
+ final int fieldCount = ObjectClassGenerator.getFieldCount(structure);
+ getters = new MethodHandle[fieldCount];
+ setters = new MethodHandle[fieldCount];
+ for(int i = 0; i < fieldCount; ++i) {
+ final String fieldName = ObjectClassGenerator.getFieldName(i, Type.OBJECT);
+ getters[i] = MH.getter(lookup, structure, fieldName, Type.OBJECT.getTypeClass());
+ setters[i] = MH.setter(lookup, structure, fieldName, Type.OBJECT.getTypeClass());
+ }
+ }
+ }
+
/**
* Constructor for dual field AccessorPropertys.
*
@@ -171,22 +201,19 @@ public class AccessorProperty extends Property {
primitiveGetter = null;
primitiveSetter = null;
- final MethodHandles.Lookup lookup = MethodHandles.lookup();
-
if (isParameter() && hasArguments()) {
- final MethodHandle arguments = MH.getter(MethodHandles.lookup(), structure, "arguments", Object.class);
+ final MethodHandle arguments = MH.getter(lookup, structure, "arguments", Object.class);
final MethodHandle argumentsSO = MH.asType(arguments, arguments.type().changeReturnType(ScriptObject.class));
objectGetter = MH.insertArguments(MH.filterArguments(ScriptObject.GET_ARGUMENT.methodHandle(), 0, argumentsSO), 1, slot);
objectSetter = MH.insertArguments(MH.filterArguments(ScriptObject.SET_ARGUMENT.methodHandle(), 0, argumentsSO), 1, slot);
} else {
- final String fieldNameObject = ObjectClassGenerator.getFieldName(slot, Type.OBJECT);
- final String fieldNamePrimitive = ObjectClassGenerator.getFieldName(slot, ObjectClassGenerator.PRIMITIVE_TYPE);
-
- objectGetter = MH.getter(lookup, structure, fieldNameObject, Type.OBJECT.getTypeClass());
- objectSetter = MH.setter(lookup, structure, fieldNameObject, Type.OBJECT.getTypeClass());
+ final GettersSetters gs = GETTERS_SETTERS.get(structure);
+ objectGetter = gs.getters[slot];
+ objectSetter = gs.setters[slot];
if (!OBJECT_FIELDS_ONLY) {
+ final String fieldNamePrimitive = ObjectClassGenerator.getFieldName(slot, ObjectClassGenerator.PRIMITIVE_TYPE);
primitiveGetter = MH.getter(lookup, structure, fieldNamePrimitive, PRIMITIVE_TYPE.getTypeClass());
primitiveSetter = MH.setter(lookup, structure, fieldNamePrimitive, PRIMITIVE_TYPE.getTypeClass());
}
@@ -365,7 +392,7 @@ public class AccessorProperty extends Property {
}
private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
- return MH.findStatic(MethodHandles.lookup(), AccessorProperty.class, name, MH.type(rtype, types));
+ return MH.findStatic(lookup, AccessorProperty.class, name, MH.type(rtype, types));
}
}
diff --git a/src/jdk/nashorn/internal/runtime/Context.java b/src/jdk/nashorn/internal/runtime/Context.java
index 448df143..40fe1ba1 100644
--- a/src/jdk/nashorn/internal/runtime/Context.java
+++ b/src/jdk/nashorn/internal/runtime/Context.java
@@ -54,9 +54,7 @@ import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.debug.ASTWriter;
import jdk.nashorn.internal.ir.debug.PrintVisitor;
import jdk.nashorn.internal.parser.Parser;
-import jdk.nashorn.internal.runtime.linker.JavaAdapterFactory;
import jdk.nashorn.internal.runtime.options.Options;
-import sun.reflect.Reflection;
/**
* This class manages the global state of execution. Context is immutable.
@@ -114,22 +112,8 @@ public final class Context {
* @return the current global scope
*/
public static ScriptObject getGlobal() {
- final SecurityManager sm = System.getSecurityManager();
- if (sm != null) {
- // skip getCallerClass and getGlobal and get to the real caller
- Class<?> caller = Reflection.getCallerClass(2);
- ClassLoader callerLoader = caller.getClassLoader();
-
- // Allow this method only for nashorn's own classes, objects
- // package classes and Java adapter classes. Rest should
- // have the necessary security permission.
- if (callerLoader != myLoader &&
- !(callerLoader instanceof StructureLoader) &&
- !(JavaAdapterFactory.isAdapterClass(caller))) {
- sm.checkPermission(new RuntimePermission("nashorn.getGlobal"));
- }
- }
-
+ // This class in a package.access protected package.
+ // Trusted code only can call this method.
return getGlobalTrusted();
}
@@ -397,7 +381,7 @@ public final class Context {
// We need to get strict mode flag from compiled class. This is
// because eval code may start with "use strict" directive.
try {
- strictFlag = clazz.getField(STRICT_MODE.tag()).getBoolean(null);
+ strictFlag = clazz.getField(STRICT_MODE.symbolName()).getBoolean(null);
} catch (final NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
//ignored
strictFlag = false;
@@ -430,6 +414,28 @@ public final class Context {
return ScriptRuntime.apply(func, evalThis);
}
+ private Source loadInternal(final String srcStr, final String prefix, final String resourcePath) {
+ if (srcStr.startsWith(prefix)) {
+ final String resource = resourcePath + srcStr.substring(prefix.length());
+ // NOTE: even sandbox scripts should be able to load scripts in nashorn: scheme
+ // These scripts are always available and are loaded from nashorn.jar's resources.
+ return AccessController.doPrivileged(
+ new PrivilegedAction<Source>() {
+ @Override
+ public Source run() {
+ try {
+ final URL resURL = Context.class.getResource(resource);
+ return (resURL != null)? new Source(srcStr, resURL) : null;
+ } catch (final IOException exp) {
+ return null;
+ }
+ }
+ });
+ }
+
+ return null;
+ }
+
/**
* Implementation of {@code load} Nashorn extension. Load a script file from a source
* expression
@@ -442,33 +448,18 @@ public final class Context {
* @throws IOException if source cannot be found or loaded
*/
public Object load(final ScriptObject scope, final Object from) throws IOException {
- Object src = (from instanceof ConsString)? from.toString() : from;
+ final Object src = (from instanceof ConsString)? from.toString() : from;
Source source = null;
// load accepts a String (which could be a URL or a file name), a File, a URL
// or a ScriptObject that has "name" and "source" (string valued) properties.
if (src instanceof String) {
final String srcStr = (String)src;
- final File file = new File(srcStr);
+ final File file = new File(srcStr);
if (srcStr.indexOf(':') != -1) {
- if (srcStr.startsWith("nashorn:")) {
- final String resource = "resources/" + srcStr.substring("nashorn:".length());
- // NOTE: even sandbox scripts should be able to load scripts in nashorn: scheme
- // These scripts are always available and are loaded from nashorn.jar's resources.
- source = AccessController.doPrivileged(
- new PrivilegedAction<Source>() {
- @Override
- public Source run() {
- try {
- final URL resURL = Context.class.getResource(resource);
- return (resURL != null)? new Source(srcStr, resURL) : null;
- } catch (final IOException exp) {
- return null;
- }
- }
- });
- } else {
- URL url = null;
+ if ((source = loadInternal(srcStr, "nashorn:", "resources/")) == null &&
+ (source = loadInternal(srcStr, "fx:", "resources/fx/")) == null) {
+ URL url;
try {
//check for malformed url. if malformed, it may still be a valid file
url = new URL(srcStr);
@@ -711,7 +702,7 @@ public final class Context {
MH.findStatic(
MethodHandles.lookup(),
script,
- RUN_SCRIPT.tag(),
+ RUN_SCRIPT.symbolName(),
MH.type(
Object.class,
ScriptFunction.class,
@@ -720,13 +711,13 @@ public final class Context {
boolean strict;
try {
- strict = script.getField(STRICT_MODE.tag()).getBoolean(null);
+ strict = script.getField(STRICT_MODE.symbolName()).getBoolean(null);
} catch (final NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
strict = false;
}
// Package as a JavaScript function and pass function back to shell.
- return ((GlobalObject)Context.getGlobalTrusted()).newScriptFunction(RUN_SCRIPT.tag(), runMethodHandle, scope, strict);
+ return ((GlobalObject)Context.getGlobalTrusted()).newScriptFunction(RUN_SCRIPT.symbolName(), runMethodHandle, scope, strict);
}
private ScriptFunction compileScript(final Source source, final ScriptObject scope, final ErrorManager errMan) {
@@ -744,13 +735,13 @@ public final class Context {
global = (GlobalObject)Context.getGlobalTrusted();
script = global.findCachedClass(source);
if (script != null) {
- Compiler.LOG.fine("Code cache hit for " + source + " avoiding recompile.");
+ Compiler.LOG.fine("Code cache hit for ", source, " avoiding recompile.");
return script;
}
}
final FunctionNode functionNode = new Parser(env, source, errMan, strict).parse();
- if (errors.hasErrors() || env._parse_only) {
+ if (errors.hasErrors()) {
return null;
}
@@ -762,6 +753,10 @@ public final class Context {
getErr().println(new PrintVisitor(functionNode));
}
+ if (env._parse_only) {
+ return null;
+ }
+
final URL url = source.getURL();
final ScriptLoader loader = env._loader_per_compile ? createNewLoader() : scriptLoader;
final CodeSource cs = url == null ? null : new CodeSource(url, (CodeSigner[])null);
diff --git a/src/jdk/nashorn/internal/runtime/DebugLogger.java b/src/jdk/nashorn/internal/runtime/DebugLogger.java
index aa7f05d4..dda24bbf 100644
--- a/src/jdk/nashorn/internal/runtime/DebugLogger.java
+++ b/src/jdk/nashorn/internal/runtime/DebugLogger.java
@@ -135,7 +135,16 @@ public final class DebugLogger {
* @param str the string to log
*/
public void finest(final String str) {
- log(str, Level.FINEST);
+ log(Level.FINEST, str);
+ }
+
+ /**
+ * Shorthand for outputting a log string as log level
+ * {@link java.util.logging.Level#FINEST} on this logger
+ * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead
+ */
+ public void finest(final Object... objs) {
+ log(Level.FINEST, objs);
}
/**
@@ -144,7 +153,16 @@ public final class DebugLogger {
* @param str the string to log
*/
public void finer(final String str) {
- log(str, Level.FINER);
+ log(Level.FINER, str);
+ }
+
+ /**
+ * Shorthand for outputting a log string as log level
+ * {@link java.util.logging.Level#FINER} on this logger
+ * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead
+ */
+ public void finer(final Object... objs) {
+ log(Level.FINER, objs);
}
/**
@@ -153,7 +171,16 @@ public final class DebugLogger {
* @param str the string to log
*/
public void fine(final String str) {
- log(str, Level.FINE);
+ log(Level.FINE, str);
+ }
+
+ /**
+ * Shorthand for outputting a log string as log level
+ * {@link java.util.logging.Level#FINE} on this logger
+ * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead
+ */
+ public void fine(final Object... objs) {
+ log(Level.FINE, objs);
}
/**
@@ -162,7 +189,16 @@ public final class DebugLogger {
* @param str the string to log
*/
public void config(final String str) {
- log(str, Level.CONFIG);
+ log(Level.CONFIG, str);
+ }
+
+ /**
+ * Shorthand for outputting a log string as log level
+ * {@link java.util.logging.Level#CONFIG} on this logger
+ * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead
+ */
+ public void config(final Object... objs) {
+ log(Level.CONFIG, objs);
}
/**
@@ -171,7 +207,16 @@ public final class DebugLogger {
* @param str the string to log
*/
public void info(final String str) {
- log(str, Level.INFO);
+ log(Level.INFO, str);
+ }
+
+ /**
+ * Shorthand for outputting a log string as log level
+ * {@link java.util.logging.Level#FINE} on this logger
+ * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead
+ */
+ public void info(final Object... objs) {
+ log(Level.INFO, objs);
}
/**
@@ -180,7 +225,16 @@ public final class DebugLogger {
* @param str the string to log
*/
public void warning(final String str) {
- log(str, Level.WARNING);
+ log(Level.WARNING, str);
+ }
+
+ /**
+ * Shorthand for outputting a log string as log level
+ * {@link java.util.logging.Level#FINE} on this logger
+ * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead
+ */
+ public void warning(final Object... objs) {
+ log(Level.WARNING, objs);
}
/**
@@ -189,20 +243,28 @@ public final class DebugLogger {
* @param str the string to log
*/
public void severe(final String str) {
- log(str, Level.SEVERE);
+ log(Level.SEVERE, str);
+ }
+
+ /**
+ * Shorthand for outputting a log string as log level
+ * {@link java.util.logging.Level#FINE} on this logger
+ * @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead
+ */
+ public void severe(final Object... objs) {
+ log(Level.SEVERE, objs);
}
/**
* Output log line on this logger at a given level of verbosity
* @see java.util.logging.Level
*
- * @param str string to log
* @param level minimum log level required for logging to take place
+ * @param str string to log
*/
- public void log(final String str, final Level level) {
+ public void log(final Level level, final String str) {
if (isEnabled) {
final StringBuilder sb = new StringBuilder();
-
for (int i = 0 ; i < indent ; i++) {
sb.append(' ');
}
@@ -210,4 +272,24 @@ public final class DebugLogger {
logger.log(level, sb.toString());
}
}
+
+ /**
+ * Output log line on this logger at a given level of verbosity
+ * @see java.util.logging.Level
+ *
+ * @param level minimum log level required for logging to take place
+ * @param objs objects for which to invoke toString and concatenate to log
+ */
+ public void log(final Level level, final Object... objs) {
+ if (isEnabled) {
+ final StringBuilder sb = new StringBuilder();
+ for (int i = 0 ; i < indent ; i++) {
+ sb.append(' ');
+ }
+ for (final Object obj : objs) {
+ sb.append(obj);
+ }
+ logger.log(level, sb.toString());
+ }
+ }
}
diff --git a/src/jdk/nashorn/internal/runtime/FindProperty.java b/src/jdk/nashorn/internal/runtime/FindProperty.java
index 35ffed93..903d1d5d 100644
--- a/src/jdk/nashorn/internal/runtime/FindProperty.java
+++ b/src/jdk/nashorn/internal/runtime/FindProperty.java
@@ -105,6 +105,22 @@ public final class FindProperty {
}
/**
+ * Return the appropriate receiver for a getter.
+ * @return appropriate receiver
+ */
+ public ScriptObject getGetterReceiver() {
+ return property != null && property.hasGetterFunction() ? self : prototype;
+ }
+
+ /**
+ * Return the appropriate receiver for a setter.
+ * @return appropriate receiver
+ */
+ public ScriptObject getSetterReceiver() {
+ return property != null && property.hasSetterFunction() ? self : prototype;
+ }
+
+ /**
* Return the property that was found
* @return property
*/
diff --git a/src/jdk/nashorn/internal/runtime/JSONFunctions.java b/src/jdk/nashorn/internal/runtime/JSONFunctions.java
index ba486be8..729df58c 100644
--- a/src/jdk/nashorn/internal/runtime/JSONFunctions.java
+++ b/src/jdk/nashorn/internal/runtime/JSONFunctions.java
@@ -105,9 +105,7 @@ public final class JSONFunctions {
// This is the abstract "Walk" operation from the spec.
private static Object walk(final ScriptObject holder, final Object name, final ScriptFunction reviver) {
final Object val = holder.get(name);
- if (val == ScriptRuntime.UNDEFINED) {
- return val;
- } else if (val instanceof ScriptObject) {
+ if (val instanceof ScriptObject) {
final ScriptObject valueObj = (ScriptObject)val;
final boolean strict = valueObj.isStrictContext();
final Iterator<String> iter = valueObj.propertyIterator();
@@ -122,33 +120,15 @@ public final class JSONFunctions {
valueObj.set(key, newElement, strict);
}
}
+ }
- return valueObj;
- } else if (isArray(val)) {
- final ScriptObject valueArray = (ScriptObject)val;
- final boolean strict = valueArray.isStrictContext();
- final Iterator<String> iter = valueArray.propertyIterator();
-
- while (iter.hasNext()) {
- final String key = iter.next();
- final Object newElement = walk(valueArray, valueArray.get(key), reviver);
-
- if (newElement == ScriptRuntime.UNDEFINED) {
- valueArray.delete(key, strict);
- } else {
- valueArray.set(key, newElement, strict);
- }
- }
- return valueArray;
- } else {
- try {
- // Object.class, ScriptFunction.class, ScriptObject.class, String.class, Object.class);
- return REVIVER_INVOKER.invokeExact(reviver, holder, JSType.toString(name), val);
- } catch(Error|RuntimeException t) {
- throw t;
- } catch(final Throwable t) {
- throw new RuntimeException(t);
- }
+ try {
+ // Object.class, ScriptFunction.class, ScriptObject.class, String.class, Object.class);
+ return REVIVER_INVOKER.invokeExact(reviver, holder, JSType.toString(name), val);
+ } catch(Error|RuntimeException t) {
+ throw t;
+ } catch(final Throwable t) {
+ throw new RuntimeException(t);
}
}
diff --git a/src/jdk/nashorn/internal/runtime/JSType.java b/src/jdk/nashorn/internal/runtime/JSType.java
index 21977816..e972a99e 100644
--- a/src/jdk/nashorn/internal/runtime/JSType.java
+++ b/src/jdk/nashorn/internal/runtime/JSType.java
@@ -102,6 +102,8 @@ public enum JSType {
/** JavaScript compliant conversion function from Object to primitive */
public static final Call TO_PRIMITIVE = staticCall(JSType.class, "toPrimitive", Object.class, Object.class);
+ private static final double INT32_LIMIT = 4294967296.0;
+
/**
* The external type name as returned by ECMAScript "typeof" operator
*
@@ -612,10 +614,7 @@ public enum JSType {
* @return an int32
*/
public static int toInt32(final double num) {
- if (Double.isInfinite(num)) {
- return 0;
- }
- return (int)(long)num;
+ return (int)doubleToInt32(num);
}
/**
@@ -658,10 +657,7 @@ public enum JSType {
* @return a uint32
*/
public static long toUint32(final double num) {
- if (Double.isInfinite(num)) {
- return 0L;
- }
- return ((long)num) & 0xffff_ffffL;
+ return doubleToInt32(num) & MAX_UINT;
}
/**
@@ -702,10 +698,22 @@ public enum JSType {
* @return a uint16
*/
public static int toUint16(final double num) {
- if (Double.isInfinite(num)) {
+ return ((int)doubleToInt32(num)) & 0xffff;
+ }
+
+ private static long doubleToInt32(final double num) {
+ final int exponent = Math.getExponent(num);
+ if (exponent < 31) {
+ return (long) num; // Fits into 32 bits
+ }
+ if (exponent >= 84) {
+ // Either infinite or NaN or so large that shift / modulo will produce 0
+ // (52 bit mantissa + 32 bit target width).
return 0;
}
- return ((int)(long)num) & 0xffff;
+ // This is rather slow and could probably be sped up using bit-fiddling.
+ final double d = (num >= 0) ? Math.floor(num) : Math.ceil(num);
+ return (long)(d % INT32_LIMIT);
}
/**
diff --git a/src/jdk/nashorn/internal/runtime/NativeJavaPackage.java b/src/jdk/nashorn/internal/runtime/NativeJavaPackage.java
index d75abd35..b10b72df 100644
--- a/src/jdk/nashorn/internal/runtime/NativeJavaPackage.java
+++ b/src/jdk/nashorn/internal/runtime/NativeJavaPackage.java
@@ -170,7 +170,7 @@ public final class NativeJavaPackage extends ScriptObject {
Class<?> javaClass = null;
try {
javaClass = context.findClass(fullName);
- } catch (final ClassNotFoundException e) {
+ } catch (final NoClassDefFoundError | ClassNotFoundException e) {
//ignored
}
diff --git a/src/jdk/nashorn/internal/runtime/PropertyHashMap.java b/src/jdk/nashorn/internal/runtime/PropertyHashMap.java
index 9759caa2..6e41fd56 100644
--- a/src/jdk/nashorn/internal/runtime/PropertyHashMap.java
+++ b/src/jdk/nashorn/internal/runtime/PropertyHashMap.java
@@ -104,10 +104,10 @@ import java.util.Set;
*/
public final class PropertyHashMap implements Map <String, Property> {
/** Number of initial bins. Power of 2. */
- private static final int INITIAL_BINS = 16;
+ private static final int INITIAL_BINS = 32;
/** Threshold before using bins. */
- private static final int LIST_THRESHOLD = 4;
+ private static final int LIST_THRESHOLD = 8;
/** Initial map. */
public static final PropertyHashMap EMPTY_MAP = new PropertyHashMap();
@@ -300,8 +300,8 @@ public final class PropertyHashMap implements Map <String, Property> {
* @return Number of bins required.
*/
private static int binsNeeded(final int n) {
- // Allow for 25% padding.
- return 1 << (32 - Integer.numberOfLeadingZeros((n + oneQuarter(n)) | (INITIAL_BINS - 1)));
+ // 50% padding
+ return 1 << (32 - Integer.numberOfLeadingZeros((n + (n >>> 1)) | (INITIAL_BINS - 1)));
}
/**
@@ -316,27 +316,15 @@ public final class PropertyHashMap implements Map <String, Property> {
}
/**
- * Used to calculate the current capacity of the bins.
- *
- * @param n Number of bin slots.
- *
- * @return 25% of n.
- */
- private static int oneQuarter(final int n) {
- return n >>> 2;
- }
-
- /**
* Regenerate the bin table after changing the number of bins.
*
* @param list // List of all properties.
- * @param newSize // New size of {@link PropertyHashMap}.
+ * @param binSize // New size of bins.
*
* @return Populated bins.
*/
- private static Element[] rehash(final Element list, final int newSize) {
- final int binsNeeded = binsNeeded(newSize);
- final Element[] newBins = new Element[binsNeeded];
+ private static Element[] rehash(final Element list, final int binSize) {
+ final Element[] newBins = new Element[binSize];
for (Element element = list; element != null; element = element.getLink()) {
final Property property = element.getProperty();
final String key = property.getKey();
@@ -390,7 +378,7 @@ public final class PropertyHashMap implements Map <String, Property> {
if (bins == null && newSize <= LIST_THRESHOLD) {
newBins = null;
} else if (newSize > threshold) {
- newBins = rehash(list, newSize);
+ newBins = rehash(list, binsNeeded(newSize));
} else {
newBins = bins.clone();
}
diff --git a/src/jdk/nashorn/internal/runtime/PropertyListenerManager.java b/src/jdk/nashorn/internal/runtime/PropertyListenerManager.java
index 970ccca6..a0c2f823 100644
--- a/src/jdk/nashorn/internal/runtime/PropertyListenerManager.java
+++ b/src/jdk/nashorn/internal/runtime/PropertyListenerManager.java
@@ -25,20 +25,20 @@
package jdk.nashorn.internal.runtime;
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
+import java.util.Map;
+import java.util.WeakHashMap;
/**
* Helper class to manage property listeners and notification.
*/
public class PropertyListenerManager implements PropertyListener {
+ /** property listeners for this object. */
+ private Map<PropertyListener,Boolean> listeners;
+
// These counters are updated in debug mode
private static int listenersAdded;
private static int listenersRemoved;
- private static int listenersDead;
/**
* @return the listenersAdded
@@ -54,16 +54,6 @@ public class PropertyListenerManager implements PropertyListener {
return listenersRemoved;
}
- /**
- * @return the listenersDead
- */
- public static int getListenersDead() {
- return listenersDead;
- }
-
- /** property listeners for this object. */
- private List<WeakReference<PropertyListener>> listeners;
-
// Property listener management methods
/**
@@ -73,12 +63,13 @@ public class PropertyListenerManager implements PropertyListener {
*/
public final void addPropertyListener(final PropertyListener listener) {
if (listeners == null) {
- listeners = new ArrayList<>();
+ listeners = new WeakHashMap<>();
}
+
if (Context.DEBUG) {
listenersAdded++;
}
- listeners.add(new WeakReference<>(listener));
+ listeners.put(listener, Boolean.TRUE);
}
/**
@@ -88,15 +79,10 @@ public class PropertyListenerManager implements PropertyListener {
*/
public final void removePropertyListener(final PropertyListener listener) {
if (listeners != null) {
- final Iterator<WeakReference<PropertyListener>> iter = listeners.iterator();
- while (iter.hasNext()) {
- if (iter.next().get() == listener) {
- if (Context.DEBUG) {
- listenersRemoved++;
- }
- iter.remove();
- }
+ if (Context.DEBUG) {
+ listenersRemoved++;
}
+ listeners.remove(listener);
}
}
@@ -108,18 +94,8 @@ public class PropertyListenerManager implements PropertyListener {
*/
protected final void notifyPropertyAdded(final ScriptObject object, final Property prop) {
if (listeners != null) {
- final Iterator<WeakReference<PropertyListener>> iter = listeners.iterator();
- while (iter.hasNext()) {
- final WeakReference<PropertyListener> weakRef = iter.next();
- final PropertyListener listener = weakRef.get();
- if (listener == null) {
- if (Context.DEBUG) {
- listenersDead++;
- }
- iter.remove();
- } else {
- listener.propertyAdded(object, prop);
- }
+ for (PropertyListener listener : listeners.keySet()) {
+ listener.propertyAdded(object, prop);
}
}
}
@@ -132,18 +108,8 @@ public class PropertyListenerManager implements PropertyListener {
*/
protected final void notifyPropertyDeleted(final ScriptObject object, final Property prop) {
if (listeners != null) {
- final Iterator<WeakReference<PropertyListener>> iter = listeners.iterator();
- while (iter.hasNext()) {
- final WeakReference<PropertyListener> weakRef = iter.next();
- final PropertyListener listener = weakRef.get();
- if (listener == null) {
- if (Context.DEBUG) {
- listenersDead++;
- }
- iter.remove();
- } else {
- listener.propertyDeleted(object, prop);
- }
+ for (PropertyListener listener : listeners.keySet()) {
+ listener.propertyDeleted(object, prop);
}
}
}
@@ -157,18 +123,8 @@ public class PropertyListenerManager implements PropertyListener {
*/
protected final void notifyPropertyModified(final ScriptObject object, final Property oldProp, final Property newProp) {
if (listeners != null) {
- final Iterator<WeakReference<PropertyListener>> iter = listeners.iterator();
- while (iter.hasNext()) {
- final WeakReference<PropertyListener> weakRef = iter.next();
- final PropertyListener listener = weakRef.get();
- if (listener == null) {
- if (Context.DEBUG) {
- listenersDead++;
- }
- iter.remove();
- } else {
- listener.propertyModified(object, oldProp, newProp);
- }
+ for (PropertyListener listener : listeners.keySet()) {
+ listener.propertyModified(object, oldProp, newProp);
}
}
}
diff --git a/src/jdk/nashorn/internal/runtime/PropertyMap.java b/src/jdk/nashorn/internal/runtime/PropertyMap.java
index 19c12b7a..b5154c85 100644
--- a/src/jdk/nashorn/internal/runtime/PropertyMap.java
+++ b/src/jdk/nashorn/internal/runtime/PropertyMap.java
@@ -540,11 +540,13 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener {
* @param newMap Modified {@link PropertyMap}.
*/
private void addToHistory(final Property property, final PropertyMap newMap) {
- if (history == null) {
- history = new LinkedHashMap<>();
- }
+ if (!properties.isEmpty()) {
+ if (history == null) {
+ history = new LinkedHashMap<>();
+ }
- history.put(property, newMap);
+ history.put(property, newMap);
+ }
}
/**
diff --git a/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java b/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java
index 03f0ab5d..c2259896 100644
--- a/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java
+++ b/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java
@@ -25,10 +25,11 @@
package jdk.nashorn.internal.runtime;
+import static jdk.nashorn.internal.lookup.Lookup.MH;
+
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
-
import jdk.nashorn.internal.codegen.Compiler;
import jdk.nashorn.internal.codegen.CompilerConstants;
import jdk.nashorn.internal.codegen.FunctionSignature;
@@ -37,8 +38,6 @@ import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.parser.Token;
import jdk.nashorn.internal.parser.TokenType;
-import static jdk.nashorn.internal.lookup.Lookup.MH;
-
/**
* This is a subclass that represents a script function that may be regenerated,
* for example with specialization based on call site types, or lazily generated.
@@ -47,7 +46,7 @@ import static jdk.nashorn.internal.lookup.Lookup.MH;
*/
public final class RecompilableScriptFunctionData extends ScriptFunctionData {
- private final FunctionNode functionNode;
+ private FunctionNode functionNode;
private final PropertyMap allocatorMap;
private final CodeInstaller<ScriptEnvironment> installer;
private final String allocatorClassName;
@@ -70,7 +69,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
"" :
functionNode.getIdent().getName(),
functionNode.getParameters().size(),
- functionNode.isStrictMode(),
+ functionNode.isStrict(),
false,
true);
@@ -129,7 +128,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
private void ensureHasAllocator() throws ClassNotFoundException {
if (allocator == null && allocatorClassName != null) {
- this.allocator = MH.findStatic(LOOKUP, Context.forStructureClass(allocatorClassName), CompilerConstants.ALLOCATE.tag(), MH.type(ScriptObject.class, PropertyMap.class));
+ this.allocator = MH.findStatic(LOOKUP, Context.forStructureClass(allocatorClassName), CompilerConstants.ALLOCATE.symbolName(), MH.type(ScriptObject.class, PropertyMap.class));
}
}
@@ -148,8 +147,11 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
// therefore, currently method specialization is disabled. TODO
if (functionNode.isLazy()) {
- Compiler.LOG.info("Trampoline hit: need to do lazy compilation of '" + functionNode.getName() + "'");
- new Compiler(installer, functionNode).compile().install();
+ Compiler.LOG.info("Trampoline hit: need to do lazy compilation of '", functionNode.getName(), "'");
+ final Compiler compiler = new Compiler(installer, functionNode);
+ functionNode = compiler.compile();
+ assert !functionNode.isLazy();
+ compiler.install();
// we don't need to update any flags - varArgs and needsCallee are instrincic
// in the function world we need to get a destination node from the compile instead
@@ -159,7 +161,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
// we can't get here unless we have bytecode, either from eager compilation or from
// running a lazy compile on the lines above
- assert functionNode.hasState(CompilationState.INSTALLED);
+ assert functionNode.hasState(CompilationState.EMITTED) : functionNode.getName() + " " + functionNode.getState() + " " + Debug.id(functionNode);
// code exists - look it up and add it into the automatically sorted invoker list
code.add(
diff --git a/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java b/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java
index 36a1d2ac..74fab0ce 100644
--- a/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java
+++ b/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java
@@ -82,6 +82,36 @@ public final class ScriptEnvironment {
/** Show full Nashorn version */
public final boolean _fullversion;
+ /** Launch using as fx application */
+ public final boolean _fx;
+
+ /**
+ * Behavior when encountering a function declaration in a lexical context where only statements are acceptable
+ * (function declarations are source elements, but not statements).
+ */
+ public enum FunctionStatementBehavior {
+ /**
+ * Accept the function declaration silently and treat it as if it were a function expression assigned to a local
+ * variable.
+ */
+ ACCEPT,
+ /**
+ * Log a parser warning, but accept the function declaration and treat it as if it were a function expression
+ * assigned to a local variable.
+ */
+ WARNING,
+ /**
+ * Raise a {@code SyntaxError}.
+ */
+ ERROR
+ }
+
+ /**
+ * Behavior when encountering a function declaration in a lexical context where only statements are acceptable
+ * (function declarations are source elements, but not statements).
+ */
+ public final FunctionStatementBehavior _function_statement;
+
/** Should lazy compilation take place */
public final boolean _lazy_compilation;
@@ -158,6 +188,14 @@ public final class ScriptEnvironment {
_early_lvalue_error = options.getBoolean("early.lvalue.error");
_empty_statements = options.getBoolean("empty.statements");
_fullversion = options.getBoolean("fullversion");
+ if(options.getBoolean("function.statement.error")) {
+ _function_statement = FunctionStatementBehavior.ERROR;
+ } else if(options.getBoolean("function.statement.warning")) {
+ _function_statement = FunctionStatementBehavior.WARNING;
+ } else {
+ _function_statement = FunctionStatementBehavior.ACCEPT;
+ }
+ _fx = options.getBoolean("fx");
_lazy_compilation = options.getBoolean("lazy.compilation");
_loader_per_compile = options.getBoolean("loader.per.compile");
_no_syntax_extensions = options.getBoolean("no.syntax.extensions");
diff --git a/src/jdk/nashorn/internal/runtime/ScriptFunction.java b/src/jdk/nashorn/internal/runtime/ScriptFunction.java
index 512a0b16..26e6270a 100644
--- a/src/jdk/nashorn/internal/runtime/ScriptFunction.java
+++ b/src/jdk/nashorn/internal/runtime/ScriptFunction.java
@@ -71,9 +71,6 @@ public abstract class ScriptFunction extends ScriptObject {
private static final MethodHandle IS_NONSTRICT_FUNCTION = findOwnMH("isNonStrictFunction", boolean.class, Object.class, Object.class, ScriptFunctionData.class);
- /** Reference to constructor prototype. */
- protected Object prototype;
-
/** The parent scope. */
private final ScriptObject scope;
@@ -221,6 +218,7 @@ public abstract class ScriptFunction extends ScriptObject {
final ScriptObject object = data.allocate();
if (object != null) {
+ Object prototype = getPrototype();
if (prototype instanceof ScriptObject) {
object.setProto((ScriptObject)prototype);
}
@@ -282,24 +280,18 @@ public abstract class ScriptFunction extends ScriptObject {
* Get the prototype object for this function
* @return prototype
*/
- public final Object getPrototype() {
- return prototype;
- }
+ public abstract Object getPrototype();
/**
* Set the prototype object for this function
* @param prototype new prototype object
- * @return the prototype parameter
*/
- public final Object setPrototype(final Object prototype) {
- this.prototype = prototype;
- return prototype;
- }
+ public abstract void setPrototype(Object prototype);
/**
* Return the most appropriate invoke handle if there are specializations
* @param type most specific method type to look for invocation with
- * @param callsite args for trampoline invocation
+ * @param args args for trampoline invocation
* @return invoke method handle
*/
private MethodHandle getBestInvoker(final MethodType type, final Object[] args) {
diff --git a/src/jdk/nashorn/internal/runtime/ScriptObject.java b/src/jdk/nashorn/internal/runtime/ScriptObject.java
index 1942ec22..93517201 100644
--- a/src/jdk/nashorn/internal/runtime/ScriptObject.java
+++ b/src/jdk/nashorn/internal/runtime/ScriptObject.java
@@ -28,6 +28,7 @@ package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCall;
import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
+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.PropertyDescriptor.CONFIGURABLE;
@@ -39,7 +40,6 @@ import static jdk.nashorn.internal.runtime.PropertyDescriptor.WRITABLE;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.getArrayIndexNoThrow;
import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex;
-import static jdk.nashorn.internal.lookup.Lookup.MH;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
@@ -61,12 +61,13 @@ import jdk.internal.dynalink.linker.LinkRequest;
import jdk.internal.dynalink.support.CallSiteDescriptorFactory;
import jdk.nashorn.internal.codegen.CompilerConstants.Call;
import jdk.nashorn.internal.codegen.ObjectClassGenerator;
+import jdk.nashorn.internal.lookup.Lookup;
+import jdk.nashorn.internal.lookup.MethodHandleFactory;
import jdk.nashorn.internal.objects.AccessorPropertyDescriptor;
import jdk.nashorn.internal.objects.DataPropertyDescriptor;
import jdk.nashorn.internal.runtime.arrays.ArrayData;
import jdk.nashorn.internal.runtime.linker.Bootstrap;
-import jdk.nashorn.internal.lookup.Lookup;
-import jdk.nashorn.internal.lookup.MethodHandleFactory;
+import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
import jdk.nashorn.internal.runtime.linker.NashornGuards;
@@ -901,7 +902,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
final MethodHandle getter = find.getGetter(int.class);
if (getter != null) {
try {
- return (int)getter.invokeExact((Object)find.getOwner());
+ return (int)getter.invokeExact((Object)find.getGetterReceiver());
} catch (final Error|RuntimeException e) {
throw e;
} catch (final Throwable e) {
@@ -916,7 +917,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
final MethodHandle getter = find.getGetter(long.class);
if (getter != null) {
try {
- return (long)getter.invokeExact((Object)find.getOwner());
+ return (long)getter.invokeExact((Object)find.getGetterReceiver());
} catch (final Error|RuntimeException e) {
throw e;
} catch (final Throwable e) {
@@ -931,7 +932,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
final MethodHandle getter = find.getGetter(double.class);
if (getter != null) {
try {
- return (double)getter.invokeExact((Object)find.getOwner());
+ return (double)getter.invokeExact((Object)find.getGetterReceiver());
} catch (final Error|RuntimeException e) {
throw e;
} catch (final Throwable e) {
@@ -953,7 +954,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
final MethodHandle getter = find.getGetter(Object.class);
if (getter != null) {
try {
- return getter.invokeExact((Object)find.getOwner());
+ return getter.invokeExact((Object)find.getGetterReceiver());
} catch (final Error|RuntimeException e) {
throw e;
} catch (final Throwable e) {
@@ -1679,12 +1680,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
* @return GuardedInvocation to be invoked at call site.
*/
protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
- final String name = desc.getNameToken(2);
-
- if (request.isCallSiteUnstable()) {
- return findMegaMorphicGetMethod(desc, name);
- }
-
+ final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
final FindProperty find = findProperty(name, true);
MethodHandle methodHandle;
@@ -1700,6 +1696,10 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
throw new AssertionError(); // never invoked with any other operation
}
+ if (request.isCallSiteUnstable()) {
+ return findMegaMorphicGetMethod(desc, name);
+ }
+
final Class<?> returnType = desc.getMethodType().returnType();
final Property property = find.getProperty();
methodHandle = find.getGetter(returnType);
@@ -1727,7 +1727,9 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
}
private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name) {
- final GuardedInvocation inv = findGetIndexMethod(desc.getMethodType().insertParameterTypes(1, Object.class));
+ final MethodType mhType = desc.getMethodType().insertParameterTypes(1, Object.class);
+ final GuardedInvocation inv = findGetIndexMethod(mhType);
+
return inv.replaceMethods(MH.insertArguments(inv.getInvocation(), 1, name), inv.getGuard());
}
@@ -1890,8 +1892,8 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
}
private static GuardedInvocation findMegaMorphicSetMethod(final CallSiteDescriptor desc, final String name) {
- final GuardedInvocation inv = findSetIndexMethod(desc.getMethodType().insertParameterTypes(1, Object.class),
- NashornCallSiteDescriptor.isStrict(desc));
+ final MethodType type = desc.getMethodType().insertParameterTypes(1, Object.class);
+ final GuardedInvocation inv = findSetIndexMethod(type, NashornCallSiteDescriptor.isStrict(desc));
return inv.replaceMethods(MH.insertArguments(inv.getInvocation(), 1, name), inv.getGuard());
}
@@ -1949,7 +1951,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
* @return GuardedInvocation to be invoked at call site.
*/
public GuardedInvocation noSuchProperty(final CallSiteDescriptor desc, final LinkRequest request) {
- final String name = desc.getNameToken(2);
+ final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true);
final boolean scopeAccess = isScope() && NashornCallSiteDescriptor.isScope(desc);
@@ -1973,6 +1975,24 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return createEmptyGetter(desc, name);
}
+ /**
+ * Invoke fall back if a property is not found.
+ * @param name Name of property.
+ * @return Result from call.
+ */
+ private Object invokeNoSuchProperty(final String name) {
+ final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true);
+
+ if (find != null) {
+ final Object func = getObjectValue(find);
+
+ if (func instanceof ScriptFunction) {
+ return ScriptRuntime.apply((ScriptFunction)func, this, name);
+ }
+ }
+
+ return UNDEFINED;
+ }
private GuardedInvocation createEmptyGetter(final CallSiteDescriptor desc, final String name) {
return new GuardedInvocation(Lookup.emptyGetter(desc.getMethodType().returnType()), getMap().getProtoGetSwitchPoint(name), NashornGuards.getMapGuard(getMap()));
@@ -2120,8 +2140,11 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
*
* Make sure arguments are paired correctly.
* @param methodHandle MethodHandle to adjust.
- * @param callType MethodType of caller.
- * @param callerVarArg true if the caller is vararg, false otherwise, null if it should be inferred.
+ * @param callType MethodType of the call site.
+ * @param callerVarArg true if the caller is vararg, false otherwise, null if it should be inferred from the
+ * {@code callType}; basically, if the last parameter type of the call site is an array, it'll be considered a
+ * variable arity call site. These are ordinarily rare; Nashorn code generator creates variable arity call sites
+ * when the call has more than {@link LinkerCallSite#ARGLIMIT} parameters.
*
* @return method handle with adjusted arguments
*/
@@ -2136,7 +2159,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
final int callCount = callType.parameterCount();
final boolean isCalleeVarArg = parameterCount > 0 && methodType.parameterType(parameterCount - 1).isArray();
- final boolean isCallerVarArg = callerVarArg != null ? callerVarArg.booleanValue() : (callCount > 1 &&
+ final boolean isCallerVarArg = callerVarArg != null ? callerVarArg.booleanValue() : (callCount > 0 &&
callType.parameterType(callCount - 1).isArray());
if (callCount < parameterCount) {
@@ -2239,310 +2262,314 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
setArray(getArray().shrink(newLength));
getArray().setLength(newLength);
}
- }
+ }
- @Override
- public int getInt(final Object key) {
- final int index = getArrayIndexNoThrow(key);
+ private int getInt(final int index, final String key) {
+ if (isValidArrayIndex(index)) {
+ for (ScriptObject object = this; ; ) {
+ final FindProperty find = object.findProperty(key, false, false, this);
- if (getArray().has(index)) {
- return getArray().getInt(index);
- }
+ if (find != null) {
+ return getIntValue(find);
+ }
- final FindProperty find = findProperty(convertKey(key), false);
+ if ((object = object.getProto()) == null) {
+ break;
+ }
- if (find != null) {
- return getIntValue(find);
- }
+ final ArrayData array = object.getArray();
+
+ if (array.has(index)) {
+ return array.getInt(index);
+ }
+ }
+ } else {
+ final FindProperty find = findProperty(key, true);
- final ScriptObject proto = this.getProto();
+ if (find != null) {
+ return getIntValue(find);
+ }
+ }
- return proto != null ? proto.getInt(key) : 0;
+ return JSType.toInt32(invokeNoSuchProperty(key));
}
@Override
- public int getInt(final double key) {
+ public int getInt(final Object key) {
final int index = getArrayIndexNoThrow(key);
+ final ArrayData array = getArray();
- if (getArray().has(index)) {
- return getArray().getInt(index);
+ if (array.has(index)) {
+ return array.getInt(index);
}
- final FindProperty find = findProperty(convertKey(key), false);
+ return getInt(index, convertKey(key));
+ }
- if (find != null) {
- return getIntValue(find);
- }
+ @Override
+ public int getInt(final double key) {
+ final int index = getArrayIndexNoThrow(key);
+ final ArrayData array = getArray();
- final ScriptObject proto = this.getProto();
+ if (array.has(index)) {
+ return array.getInt(index);
+ }
- return proto != null ? proto.getInt(key) : 0;
+ return getInt(index, convertKey(key));
}
@Override
public int getInt(final long key) {
final int index = getArrayIndexNoThrow(key);
+ final ArrayData array = getArray();
- if (getArray().has(index)) {
- return getArray().getInt(index);
- }
-
- final FindProperty find = findProperty(convertKey(key), false);
-
- if (find != null) {
- return getIntValue(find);
+ if (array.has(index)) {
+ return array.getInt(index);
}
- final ScriptObject proto = this.getProto();
-
- return proto != null ? proto.getInt(key) : 0;
+ return getInt(index, convertKey(key));
}
@Override
public int getInt(final int key) {
- final int index = getArrayIndexNoThrow(key);
+ final ArrayData array = getArray();
- if (getArray().has(index)) {
- return getArray().getInt(index);
+ if (array.has(key)) {
+ return array.getInt(key);
}
- final FindProperty find = findProperty(convertKey(key), false);
+ return getInt(key, convertKey(key));
+ }
- if (find != null) {
- return getIntValue(find);
- }
+ private long getLong(final int index, final String key) {
+ if (isValidArrayIndex(index)) {
+ for (ScriptObject object = this; ; ) {
+ final FindProperty find = object.findProperty(key, false, false, this);
- final ScriptObject proto = this.getProto();
+ if (find != null) {
+ return getLongValue(find);
+ }
- return proto != null ? proto.getInt(key) : 0;
+ if ((object = object.getProto()) == null) {
+ break;
+ }
+
+ final ArrayData array = object.getArray();
+
+ if (array.has(index)) {
+ return array.getLong(index);
+ }
+ }
+ } else {
+ final FindProperty find = findProperty(key, true);
+
+ if (find != null) {
+ return getLongValue(find);
+ }
+ }
+
+ return JSType.toLong(invokeNoSuchProperty(key));
}
@Override
public long getLong(final Object key) {
final int index = getArrayIndexNoThrow(key);
+ final ArrayData array = getArray();
- if (getArray().has(index)) {
- return getArray().getLong(index);
- }
-
- final FindProperty find = findProperty(convertKey(key), false);
-
- if (find != null) {
- return getLongValue(find);
+ if (array.has(index)) {
+ return array.getLong(index);
}
- final ScriptObject proto = this.getProto();
-
- return proto != null ? proto.getLong(key) : 0L;
+ return getLong(index, convertKey(key));
}
@Override
public long getLong(final double key) {
final int index = getArrayIndexNoThrow(key);
+ final ArrayData array = getArray();
- if (getArray().has(index)) {
- return getArray().getLong(index);
- }
-
- final FindProperty find = findProperty(convertKey(key), false);
-
- if (find != null) {
- return getLongValue(find);
+ if (array.has(index)) {
+ return array.getLong(index);
}
- final ScriptObject proto = this.getProto();
-
- return proto != null ? proto.getLong(key) : 0L;
+ return getLong(index, convertKey(key));
}
@Override
public long getLong(final long key) {
final int index = getArrayIndexNoThrow(key);
+ final ArrayData array = getArray();
- if (getArray().has(index)) {
- return getArray().getLong(index);
- }
-
- final FindProperty find = findProperty(convertKey(key), false);
-
- if (find != null) {
- return getLongValue(find);
+ if (array.has(index)) {
+ return array.getLong(index);
}
- final ScriptObject proto = this.getProto();
-
- return proto != null ? proto.getLong(key) : 0L;
+ return getLong(index, convertKey(key));
}
@Override
public long getLong(final int key) {
- final int index = getArrayIndexNoThrow(key);
+ final ArrayData array = getArray();
- if (getArray().has(index)) {
- return getArray().getLong(index);
+ if (array.has(key)) {
+ return array.getLong(key);
}
- final FindProperty find = findProperty(convertKey(key), false);
+ return getLong(key, convertKey(key));
+ }
- if (find != null) {
- return getLongValue(find);
- }
+ private double getDouble(final int index, final String key) {
+ if (isValidArrayIndex(index)) {
+ for (ScriptObject object = this; ; ) {
+ final FindProperty find = object.findProperty(key, false, false, this);
- final ScriptObject proto = this.getProto();
+ if (find != null) {
+ return getDoubleValue(find);
+ }
+
+ if ((object = object.getProto()) == null) {
+ break;
+ }
+
+ final ArrayData array = object.getArray();
+
+ if (array.has(index)) {
+ return array.getDouble(index);
+ }
+ }
+ } else {
+ final FindProperty find = findProperty(key, true);
- return proto != null ? proto.getLong(key) : 0L;
+ if (find != null) {
+ return getDoubleValue(find);
+ }
+ }
+
+ return JSType.toNumber(invokeNoSuchProperty(key));
}
@Override
public double getDouble(final Object key) {
final int index = getArrayIndexNoThrow(key);
+ final ArrayData array = getArray();
- if (getArray().has(index)) {
- return getArray().getDouble(index);
- }
-
- final FindProperty find = findProperty(convertKey(key), false);
-
- if (find != null) {
- return getDoubleValue(find);
+ if (array.has(index)) {
+ return array.getDouble(index);
}
- final ScriptObject proto = this.getProto();
-
- return proto != null ? proto.getDouble(key) : Double.NaN;
+ return getDouble(index, convertKey(key));
}
@Override
public double getDouble(final double key) {
final int index = getArrayIndexNoThrow(key);
+ final ArrayData array = getArray();
- if (getArray().has(index)) {
- return getArray().getDouble(index);
- }
-
- final FindProperty find = findProperty(convertKey(key), false);
-
- if (find != null) {
- return getDoubleValue(find);
+ if (array.has(index)) {
+ return array.getDouble(index);
}
- final ScriptObject proto = this.getProto();
-
- return proto != null ? proto.getDouble(key) : Double.NaN;
+ return getDouble(index, convertKey(key));
}
@Override
public double getDouble(final long key) {
final int index = getArrayIndexNoThrow(key);
+ final ArrayData array = getArray();
- if (getArray().has(index)) {
- return getArray().getDouble(index);
- }
-
- final FindProperty find = findProperty(convertKey(key), false);
-
- if (find != null) {
- return getDoubleValue(find);
+ if (array.has(index)) {
+ return array.getDouble(index);
}
- final ScriptObject proto = this.getProto();
-
- return proto != null ? proto.getDouble(key) : Double.NaN;
+ return getDouble(index, convertKey(key));
}
@Override
public double getDouble(final int key) {
- final int index = getArrayIndexNoThrow(key);
+ final ArrayData array = getArray();
- if (getArray().has(index)) {
- return getArray().getDouble(index);
+ if (array.has(key)) {
+ return array.getDouble(key);
}
- final FindProperty find = findProperty(convertKey(key), false);
+ return getDouble(key, convertKey(key));
+ }
- if (find != null) {
- return getDoubleValue(find);
- }
+ private Object get(final int index, final String key) {
+ if (isValidArrayIndex(index)) {
+ for (ScriptObject object = this; ; ) {
+ final FindProperty find = object.findProperty(key, false, false, this);
+
+ if (find != null) {
+ return getObjectValue(find);
+ }
+
+ if ((object = object.getProto()) == null) {
+ break;
+ }
+
+ final ArrayData array = object.getArray();
+
+ if (array.has(index)) {
+ return array.getObject(index);
+ }
+ }
+ } else {
+ final FindProperty find = findProperty(key, true);
- final ScriptObject proto = this.getProto();
+ if (find != null) {
+ return getObjectValue(find);
+ }
+ }
- return proto != null ? proto.getDouble(key) : Double.NaN;
+ return invokeNoSuchProperty(key);
}
@Override
public Object get(final Object key) {
final int index = getArrayIndexNoThrow(key);
+ final ArrayData array = getArray();
- if (getArray().has(index)) {
- return getArray().getObject(index);
- }
-
- final FindProperty find = findProperty(convertKey(key), false);
-
- if (find != null) {
- return getObjectValue(find);
+ if (array.has(index)) {
+ return array.getObject(index);
}
- final ScriptObject proto = this.getProto();
-
- return proto != null ? proto.get(key) : UNDEFINED;
+ return get(index, convertKey(key));
}
@Override
public Object get(final double key) {
final int index = getArrayIndexNoThrow(key);
+ final ArrayData array = getArray();
- if (getArray().has(index)) {
- return getArray().getObject(index);
- }
-
- final FindProperty find = findProperty(convertKey(key), false);
-
- if (find != null) {
- return getObjectValue(find);
+ if (array.has(index)) {
+ return array.getObject(index);
}
- final ScriptObject proto = this.getProto();
-
- return proto != null ? proto.get(key) : UNDEFINED;
+ return get(index, convertKey(key));
}
@Override
public Object get(final long key) {
final int index = getArrayIndexNoThrow(key);
+ final ArrayData array = getArray();
- if (getArray().has(index)) {
- return getArray().getObject(index);
- }
-
- final FindProperty find = findProperty(convertKey(key), false);
-
- if (find != null) {
- return getObjectValue(find);
+ if (array.has(index)) {
+ return array.getObject(index);
}
- final ScriptObject proto = this.getProto();
-
- return proto != null ? proto.get(key) : UNDEFINED;
+ return get(index, convertKey(key));
}
@Override
public Object get(final int key) {
- final int index = getArrayIndexNoThrow(key);
-
- if (getArray().has(index)) {
- return getArray().getObject(index);
- }
-
- final FindProperty find = findProperty(convertKey(key), false);
+ final ArrayData array = getArray();
- if (find != null) {
- return getObjectValue(find);
+ if (array.has(key)) {
+ return array.getObject(key);
}
- final ScriptObject proto = this.getProto();
-
- return proto != null ? proto.get(key) : UNDEFINED;
+ return get(key, convertKey(key));
}
/**
@@ -2554,7 +2581,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
*/
private void doesNotHave(final int index, final Object value, final boolean strict) {
final long oldLength = getArray().length();
- final long longIndex = index & 0xffff_ffffL;
+ final long longIndex = index & JSType.MAX_UINT;
if (!getArray().has(index)) {
final String key = convertKey(longIndex);
@@ -2613,8 +2640,6 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
f = null;
}
- MethodHandle setter;
-
if (f != null) {
if (!f.getProperty().isWritable()) {
if (strict) {
@@ -2624,9 +2649,9 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return;
}
- setter = f.getSetter(Object.class, strict); //TODO specfields
try {
- setter.invokeExact((Object)f.getOwner(), value);
+ final MethodHandle setter = f.getSetter(Object.class, strict); //TODO specfields
+ setter.invokeExact((Object)f.getSetterReceiver(), value);
} catch (final Error|RuntimeException e) {
throw e;
} catch (final Throwable e) {
diff --git a/src/jdk/nashorn/internal/runtime/StructureLoader.java b/src/jdk/nashorn/internal/runtime/StructureLoader.java
index 35786b0b..db55fff9 100644
--- a/src/jdk/nashorn/internal/runtime/StructureLoader.java
+++ b/src/jdk/nashorn/internal/runtime/StructureLoader.java
@@ -47,7 +47,7 @@ import jdk.nashorn.internal.codegen.ObjectClassGenerator;
*
*/
final class StructureLoader extends NashornLoader {
- private static final String JS_OBJECT_PREFIX_EXTERNAL = binaryName(SCRIPTS_PACKAGE) + '.' + JS_OBJECT_PREFIX.tag();
+ private static final String JS_OBJECT_PREFIX_EXTERNAL = binaryName(SCRIPTS_PACKAGE) + '.' + JS_OBJECT_PREFIX.symbolName();
private static final String OBJECTS_PACKAGE_EXTERNAL = binaryName(OBJECTS_PACKAGE);
/**
@@ -110,7 +110,7 @@ final class StructureLoader extends NashornLoader {
@Override
protected Class<?> findClass(final String name) throws ClassNotFoundException {
if (name.startsWith(JS_OBJECT_PREFIX_EXTERNAL)) {
- final int start = name.indexOf(JS_OBJECT_PREFIX.tag()) + JS_OBJECT_PREFIX.tag().length();
+ final int start = name.indexOf(JS_OBJECT_PREFIX.symbolName()) + JS_OBJECT_PREFIX.symbolName().length();
return generateClass(name, name.substring(start, name.length()));
}
return super.findClass(name);
diff --git a/src/jdk/nashorn/internal/runtime/WithObject.java b/src/jdk/nashorn/internal/runtime/WithObject.java
index 5eaf5f50..ca3fcac9 100644
--- a/src/jdk/nashorn/internal/runtime/WithObject.java
+++ b/src/jdk/nashorn/internal/runtime/WithObject.java
@@ -29,6 +29,7 @@ import static jdk.nashorn.internal.lookup.Lookup.MH;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
@@ -42,9 +43,10 @@ import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
*/
public final class WithObject extends ScriptObject implements Scope {
- private static final MethodHandle WITHEXPRESSIONFILTER = findOwnMH("withFilterExpression", Object.class, Object.class);
- private static final MethodHandle WITHSCOPEFILTER = findOwnMH("withFilterScope", Object.class, Object.class);
- private static final MethodHandle BIND_TO_EXPRESSION = findOwnMH("bindToExpression", Object.class, Object.class, Object.class);
+ private static final MethodHandle WITHEXPRESSIONFILTER = findOwnMH("withFilterExpression", Object.class, Object.class);
+ private static final MethodHandle WITHSCOPEFILTER = findOwnMH("withFilterScope", Object.class, Object.class);
+ private static final MethodHandle BIND_TO_EXPRESSION_OBJ = findOwnMH("bindToExpression", Object.class, Object.class, Object.class);
+ private static final MethodHandle BIND_TO_EXPRESSION_FN = findOwnMH("bindToExpression", Object.class, ScriptFunction.class, Object.class);
/** With expression object. */
private final Object expression;
@@ -230,22 +232,35 @@ public final class WithObject extends ScriptObject implements Scope {
return (Scope) proto;
}
+ private static GuardedInvocation fixReceiverType(final GuardedInvocation link, final MethodHandle filter) {
+ // The receiver may be an Object or a ScriptObject.
+ final MethodType invType = link.getInvocation().type();
+ final MethodType newInvType = invType.changeParameterType(0, filter.type().returnType());
+ return link.asType(newInvType);
+ }
+
private static GuardedInvocation fixExpressionCallSite(final NashornCallSiteDescriptor desc, final GuardedInvocation link) {
// If it's not a getMethod, just add an expression filter that converts WithObject in "this" position to its
// expression.
if(!"getMethod".equals(desc.getFirstOperator())) {
- return link.filterArguments(0, WITHEXPRESSIONFILTER);
+ return fixReceiverType(link, WITHEXPRESSIONFILTER).filterArguments(0, WITHEXPRESSIONFILTER);
}
+ final MethodHandle linkInvocation = link.getInvocation();
+ final MethodType linkType = linkInvocation.type();
+ final boolean linkReturnsFunction = ScriptFunction.class.isAssignableFrom(linkType.returnType());
return link.replaceMethods(
// Make sure getMethod will bind the script functions it receives to WithObject.expression
- MH.foldArguments(BIND_TO_EXPRESSION, filter(link.getInvocation(), WITHEXPRESSIONFILTER)),
+ MH.foldArguments(linkReturnsFunction ? BIND_TO_EXPRESSION_FN : BIND_TO_EXPRESSION_OBJ,
+ filter(linkInvocation.asType(linkType.changeReturnType(
+ linkReturnsFunction ? ScriptFunction.class : Object.class)), WITHEXPRESSIONFILTER)),
// No clever things for the guard -- it is still identically filtered.
filterGuard(link, WITHEXPRESSIONFILTER));
}
private static GuardedInvocation fixScopeCallSite(final GuardedInvocation link) {
- return link.replaceMethods(filter(link.getInvocation(), WITHSCOPEFILTER), filterGuard(link, WITHSCOPEFILTER));
+ final GuardedInvocation newLink = fixReceiverType(link, WITHSCOPEFILTER);
+ return link.replaceMethods(filter(newLink.getInvocation(), WITHSCOPEFILTER), filterGuard(newLink, WITHSCOPEFILTER));
}
private static MethodHandle filterGuard(final GuardedInvocation link, final MethodHandle filter) {
@@ -269,7 +284,11 @@ public final class WithObject extends ScriptObject implements Scope {
@SuppressWarnings("unused")
private static Object bindToExpression(final Object fn, final Object receiver) {
- return fn instanceof ScriptFunction ? ((ScriptFunction) fn).makeBoundFunction(withFilterExpression(receiver), new Object[0]) : fn;
+ return fn instanceof ScriptFunction ? bindToExpression((ScriptFunction) fn, receiver) : fn;
+ }
+
+ private static Object bindToExpression(final ScriptFunction fn, final Object receiver) {
+ return fn.makeBoundFunction(withFilterExpression(receiver), new Object[0]);
}
/**
diff --git a/src/jdk/nashorn/internal/runtime/arrays/ArrayIndex.java b/src/jdk/nashorn/internal/runtime/arrays/ArrayIndex.java
index c7de94bf..531c56b5 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/ArrayIndex.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/ArrayIndex.java
@@ -84,7 +84,9 @@ public final class ArrayIndex {
* @return valid array index, or negative value if not valid
*/
public static int getArrayIndexNoThrow(final Object key) {
- if (key instanceof Number) {
+ if (key instanceof Integer) {
+ return getArrayIndexNoThrow(((Integer)key).intValue());
+ } else if (key instanceof Number) {
return getArrayIndexNoThrow(((Number)key).doubleValue());
} else if (key instanceof String) {
return (int)fromString((String)key);
diff --git a/src/jdk/nashorn/internal/runtime/arrays/MapIterator.java b/src/jdk/nashorn/internal/runtime/arrays/MapIterator.java
index be9bc23d..fdf9d3a4 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/MapIterator.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/MapIterator.java
@@ -66,8 +66,7 @@ class MapIterator extends ArrayLikeIterator<Object> {
bumpIndex();
}
- // special case - balk at iterating to infinity or MAX_UINT
- return (length != JSType.MAX_UINT) && indexInArray();
+ return indexInArray();
}
@Override
diff --git a/src/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java b/src/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java
index e5ba1cd9..04ac661f 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java
@@ -273,7 +273,7 @@ class SparseArrayData extends ArrayData {
}
private static Long indexToKey(final int index) {
- return Long.valueOf(index & 0xffff_ffffL);
+ return Long.valueOf(index & JSType.MAX_UINT);
}
@Override
diff --git a/src/jdk/nashorn/internal/runtime/linker/AdaptationException.java b/src/jdk/nashorn/internal/runtime/linker/AdaptationException.java
new file mode 100644
index 00000000..33429dd1
--- /dev/null
+++ b/src/jdk/nashorn/internal/runtime/linker/AdaptationException.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.runtime.linker;
+
+@SuppressWarnings("serial")
+class AdaptationException extends Exception {
+ private final AdaptationResult adaptationResult;
+
+ AdaptationException(final AdaptationResult.Outcome outcome, final String classList) {
+ this.adaptationResult = new AdaptationResult(outcome, classList);
+ }
+
+ AdaptationResult getAdaptationResult() {
+ return adaptationResult;
+ }
+}
diff --git a/src/jdk/nashorn/internal/runtime/linker/AdaptationResult.java b/src/jdk/nashorn/internal/runtime/linker/AdaptationResult.java
new file mode 100644
index 00000000..5185a955
--- /dev/null
+++ b/src/jdk/nashorn/internal/runtime/linker/AdaptationResult.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.runtime.linker;
+
+import jdk.nashorn.internal.runtime.ECMAErrors;
+import jdk.nashorn.internal.runtime.ECMAException;
+
+/**
+ * A result of generating an adapter for a class. A tuple of an outcome and - in case of an error outcome - a list of
+ * classes that caused the error.
+ */
+class AdaptationResult {
+ /**
+ * Contains various outcomes for attempting to generate an adapter class. These are stored in AdapterInfo instances.
+ * We have a successful outcome (adapter class was generated) and four possible error outcomes: superclass is final,
+ * superclass is not public, superclass has no public or protected constructor, more than one superclass was
+ * specified. We don't throw exceptions when we try to generate the adapter, but rather just record these error
+ * conditions as they are still useful as partial outcomes, as Nashorn's linker can still successfully check whether
+ * the class can be autoconverted from a script function even when it is not possible to generate an adapter for it.
+ */
+ enum Outcome {
+ SUCCESS,
+ ERROR_FINAL_CLASS,
+ ERROR_NON_PUBLIC_CLASS,
+ ERROR_NO_ACCESSIBLE_CONSTRUCTOR,
+ ERROR_MULTIPLE_SUPERCLASSES,
+ ERROR_NO_COMMON_LOADER
+ }
+
+ static final AdaptationResult SUCCESSFUL_RESULT = new AdaptationResult(Outcome.SUCCESS, "");
+
+ private final Outcome outcome;
+ private final String classList;
+
+ AdaptationResult(final Outcome outcome, final String classList) {
+ this.outcome = outcome;
+ this.classList = classList;
+ }
+
+ Outcome getOutcome() {
+ return outcome;
+ }
+
+ String getClassList() {
+ return classList;
+ }
+
+ ECMAException typeError() {
+ return ECMAErrors.typeError("extend." + outcome, classList);
+ }
+}
diff --git a/src/jdk/nashorn/internal/runtime/linker/ClassAndLoader.java b/src/jdk/nashorn/internal/runtime/linker/ClassAndLoader.java
new file mode 100644
index 00000000..d12df47a
--- /dev/null
+++ b/src/jdk/nashorn/internal/runtime/linker/ClassAndLoader.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.runtime.linker;
+
+import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A tuple of a class loader and a single class representative of the classes that can be loaded through it. Its
+ * equals/hashCode is defined in terms of the identity of the class loader. The rationale for this class is that it
+ * couples a class loader with a random representative class coming from that loader - this representative class is then
+ * used to determine if one loader can see the other loader's classes.
+ */
+final class ClassAndLoader {
+ private final Class<?> representativeClass;
+ // Don't access this directly; most of the time, use getRetrievedLoader(), or if you know what you're doing,
+ // getLoader().
+ private ClassLoader loader;
+ // We have mild affinity against eagerly retrieving the loader, as we need to do it in a privileged block. For
+ // the most basic case of looking up an already-generated adapter info for a single type, we avoid it.
+ private boolean loaderRetrieved;
+
+ ClassAndLoader(final Class<?> representativeClass, final boolean retrieveLoader) {
+ this.representativeClass = representativeClass;
+ if(retrieveLoader) {
+ retrieveLoader();
+ }
+ }
+
+ Class<?> getRepresentativeClass() {
+ return representativeClass;
+ }
+
+ boolean canSee(ClassAndLoader other) {
+ try {
+ final Class<?> otherClass = other.getRepresentativeClass();
+ return Class.forName(otherClass.getName(), false, getLoader()) == otherClass;
+ } catch (final ClassNotFoundException e) {
+ return false;
+ }
+ }
+
+ ClassLoader getLoader() {
+ if(!loaderRetrieved) {
+ retrieveLoader();
+ }
+ return getRetrievedLoader();
+ }
+
+ ClassLoader getRetrievedLoader() {
+ assert loaderRetrieved;
+ return loader;
+ }
+
+ private void retrieveLoader() {
+ loader = representativeClass.getClassLoader();
+ loaderRetrieved = true;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ return obj instanceof ClassAndLoader && ((ClassAndLoader)obj).getRetrievedLoader() == getRetrievedLoader();
+ }
+
+ @Override
+ public int hashCode() {
+ return System.identityHashCode(getRetrievedLoader());
+ }
+
+ /**
+ * Given a list of types that define the superclass/interfaces for an adapter class, returns a single type from the
+ * list that will be used to attach the adapter to its ClassValue. The first type in the array that is defined in a
+ * class loader that can also see all other types is returned. If there is no such loader, an exception is thrown.
+ * @param types the input types
+ * @return the first type from the array that is defined in a class loader that can also see all other types.
+ */
+ static ClassAndLoader getDefiningClassAndLoader(final Class<?>[] types) {
+ // Short circuit the cheap case
+ if(types.length == 1) {
+ return new ClassAndLoader(types[0], false);
+ }
+
+ return AccessController.doPrivileged(new PrivilegedAction<ClassAndLoader>() {
+ @Override
+ public ClassAndLoader run() {
+ return getDefiningClassAndLoaderPrivileged(types);
+ }
+ });
+ }
+
+ static ClassAndLoader getDefiningClassAndLoaderPrivileged(final Class<?>[] types) {
+ final Collection<ClassAndLoader> maximumVisibilityLoaders = getMaximumVisibilityLoaders(types);
+
+ final Iterator<ClassAndLoader> it = maximumVisibilityLoaders.iterator();
+ if(maximumVisibilityLoaders.size() == 1) {
+ // Fortunate case - single maximally specific class loader; return its representative class.
+ return it.next();
+ }
+
+ // Ambiguity; throw an error.
+ assert maximumVisibilityLoaders.size() > 1; // basically, can't be zero
+ final StringBuilder b = new StringBuilder();
+ b.append(it.next().getRepresentativeClass().getCanonicalName());
+ while(it.hasNext()) {
+ b.append(", ").append(it.next().getRepresentativeClass().getCanonicalName());
+ }
+ throw typeError("extend.ambiguous.defining.class", b.toString());
+ }
+
+ /**
+ * Given an array of types, return a subset of their class loaders that are maximal according to the
+ * "can see other loaders' classes" relation, which is presumed to be a partial ordering.
+ * @param types types
+ * @return a collection of maximum visibility class loaders. It is guaranteed to have at least one element.
+ */
+ private static Collection<ClassAndLoader> getMaximumVisibilityLoaders(final Class<?>[] types) {
+ final List<ClassAndLoader> maximumVisibilityLoaders = new LinkedList<>();
+ outer: for(final ClassAndLoader maxCandidate: getClassLoadersForTypes(types)) {
+ final Iterator<ClassAndLoader> it = maximumVisibilityLoaders.iterator();
+ while(it.hasNext()) {
+ final ClassAndLoader existingMax = it.next();
+ final boolean candidateSeesExisting = maxCandidate.canSee(existingMax);
+ final boolean exitingSeesCandidate = existingMax.canSee(maxCandidate);
+ if(candidateSeesExisting) {
+ if(!exitingSeesCandidate) {
+ // The candidate sees the the existing maximum, so drop the existing one as it's no longer maximal.
+ it.remove();
+ }
+ // NOTE: there's also the anomalous case where both loaders see each other. Not sure what to do
+ // about that one, as two distinct class loaders both seeing each other's classes is weird and
+ // violates the assumption that the relation "sees others' classes" is a partial ordering. We'll
+ // just not do anything, and treat them as incomparable; hopefully some later class loader that
+ // comes along can eliminate both of them, if it can not, we'll end up with ambiguity anyway and
+ // throw an error at the end.
+ } else if(exitingSeesCandidate) {
+ // Existing sees the candidate, so drop the candidate.
+ continue outer;
+ }
+ }
+ // If we get here, no existing maximum visibility loader could see the candidate, so the candidate is a new
+ // maximum.
+ maximumVisibilityLoaders.add(maxCandidate);
+ }
+ return maximumVisibilityLoaders;
+ }
+
+ private static Collection<ClassAndLoader> getClassLoadersForTypes(final Class<?>[] types) {
+ final Map<ClassAndLoader, ClassAndLoader> classesAndLoaders = new LinkedHashMap<>();
+ for(final Class<?> c: types) {
+ final ClassAndLoader cl = new ClassAndLoader(c, true);
+ if(!classesAndLoaders.containsKey(cl)) {
+ classesAndLoaders.put(cl, cl);
+ }
+ }
+ return classesAndLoaders.keySet();
+ }
+}
diff --git a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java
new file mode 100644
index 00000000..37786b1e
--- /dev/null
+++ b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java
@@ -0,0 +1,882 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.runtime.linker;
+
+import static jdk.internal.org.objectweb.asm.Opcodes.ACC_FINAL;
+import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PRIVATE;
+import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
+import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC;
+import static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER;
+import static jdk.internal.org.objectweb.asm.Opcodes.ACC_VARARGS;
+import static jdk.internal.org.objectweb.asm.Opcodes.ACONST_NULL;
+import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD;
+import static jdk.internal.org.objectweb.asm.Opcodes.ASTORE;
+import static jdk.internal.org.objectweb.asm.Opcodes.DUP;
+import static jdk.internal.org.objectweb.asm.Opcodes.IFNONNULL;
+import static jdk.internal.org.objectweb.asm.Opcodes.ILOAD;
+import static jdk.internal.org.objectweb.asm.Opcodes.ISTORE;
+import static jdk.internal.org.objectweb.asm.Opcodes.POP;
+import static jdk.internal.org.objectweb.asm.Opcodes.RETURN;
+import static jdk.nashorn.internal.lookup.Lookup.MH;
+import static jdk.nashorn.internal.runtime.linker.AdaptationResult.Outcome.ERROR_NO_ACCESSIBLE_CONSTRUCTOR;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+import jdk.internal.org.objectweb.asm.ClassWriter;
+import jdk.internal.org.objectweb.asm.Label;
+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.internal.runtime.Context;
+import jdk.nashorn.internal.runtime.ScriptFunction;
+import jdk.nashorn.internal.runtime.ScriptObject;
+
+/**
+ * Generates bytecode for a Java adapter class. Used by the {@link JavaAdapterFactory}.
+ * </p><p>
+ * For every protected or public constructor in the extended class, the adapter class will have between one to three
+ * public constructors (visibility of protected constructors in the extended class is promoted to public).
+ * <ul>
+ * <li>In every case, a constructor taking a trailing ScriptObject argument preceded by original constructor arguments
+ * is always created on the adapter class. When such a constructor is invoked, the passed ScriptObject's member
+ * functions are used to implement and/or override methods on the original class, dispatched by name. A single
+ * JavaScript function will act as the implementation for all overloaded methods of the same name. When methods on an
+ * adapter instance are invoked, the functions are invoked having the ScriptObject passed in the instance constructor as
+ * their "this". Subsequent changes to the ScriptObject (reassignment or removal of its functions) are not reflected in
+ * the adapter instance; the method implementations are bound to functions at constructor invocation time.
+ * {@code java.lang.Object} methods {@code equals}, {@code hashCode}, and {@code toString} can also be overridden. The
+ * only restriction is that since every JavaScript object already has a {@code toString} function through the
+ * {@code Object.prototype}, the {@code toString} in the adapter is only overridden if the passed ScriptObject has a
+ * {@code toString} function as its own property, and not inherited from a prototype. All other adapter methods can be
+ * implemented or overridden through a prototype-inherited function of the ScriptObject passed to the constructor too.
+ * </li>
+ * <li>
+ * If the original types collectively have only one abstract method, or have several of them, but all share the
+ * same name, an additional constructor is provided for every original constructor; this one takes a ScriptFunction as
+ * its last argument preceded by original constructor arguments. This constructor will use the passed function as the
+ * implementation for all abstract methods. For consistency, any concrete methods sharing the single abstract method
+ * name will also be overridden by the function. When methods on the adapter instance are invoked, the ScriptFunction is
+ * invoked with {@code null} as its "this".
+ * </li>
+ * <li>
+ * If the adapter being generated can have class-level overrides, constructors taking same arguments as the superclass
+ * constructors are also created. These constructors simply delegate to the superclass constructor. They are used to
+ * create instances of the adapter class with no instance-level overrides.
+ * </li>
+ * </ul>
+ * </p><p>
+ * For adapter methods that return values, all the JavaScript-to-Java conversions supported by Nashorn will be in effect
+ * to coerce the JavaScript function return value to the expected Java return type.
+ * </p><p>
+ * Since we are adding a trailing argument to the generated constructors in the adapter class, they will never be
+ * declared as variable arity, even if the original constructor in the superclass was declared as variable arity. The
+ * reason we are passing the additional argument at the end of the argument list instead at the front is that the
+ * source-level script expression <code>new X(a, b) { ... }</code> (which is a proprietary syntax extension Nashorn uses
+ * to resemble Java anonymous classes) is actually equivalent to <code>new X(a, b, { ... })</code>.
+ * </p><p>
+ * It is possible to create two different classes: those that can have both class-level and instance-level overrides,
+ * and those that can only have instance-level overrides. When
+ * {@link JavaAdapterFactory#getAdapterClassFor(Class[], ScriptObject)} is invoked with non-null {@code classOverrides}
+ * parameter, an adapter class is created that can have class-level overrides, and the passed script object will be used
+ * as the implementations for its methods, just as in the above case of the constructor taking a script object. Note
+ * that in the case of class-level overrides, a new adapter class is created on every invocation, and the implementation
+ * object is bound to the class, not to any instance. All created instances will share these functions. Of course, when
+ * instances of such a class are being created, they can still take another object (or possibly a function) in their
+ * constructor's trailing position and thus provide further instance-specific overrides. The order of invocation is
+ * always instance-specified method, then a class-specified method, and finally the superclass method.
+ */
+final class JavaAdapterBytecodeGenerator extends JavaAdapterGeneratorBase {
+ 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);
+ private static final Type METHOD_HANDLE_TYPE = Type.getType(MethodHandle.class);
+ private static final String GET_HANDLE_OBJECT_DESCRIPTOR = Type.getMethodDescriptor(METHOD_HANDLE_TYPE,
+ OBJECT_TYPE, STRING_TYPE, METHOD_TYPE_TYPE);
+ private static final String GET_HANDLE_FUNCTION_DESCRIPTOR = Type.getMethodDescriptor(METHOD_HANDLE_TYPE,
+ SCRIPT_FUNCTION_TYPE, METHOD_TYPE_TYPE);
+ private static final String GET_CLASS_INITIALIZER_DESCRIPTOR = Type.getMethodDescriptor(SCRIPT_OBJECT_TYPE);
+ private static final Type RUNTIME_EXCEPTION_TYPE = Type.getType(RuntimeException.class);
+ private static final Type THROWABLE_TYPE = Type.getType(Throwable.class);
+ private static final Type UNSUPPORTED_OPERATION_TYPE = Type.getType(UnsupportedOperationException.class);
+
+ private static final String SERVICES_CLASS_TYPE_NAME = Type.getInternalName(JavaAdapterServices.class);
+ private static final String RUNTIME_EXCEPTION_TYPE_NAME = RUNTIME_EXCEPTION_TYPE.getInternalName();
+ private static final String ERROR_TYPE_NAME = Type.getInternalName(Error.class);
+ private static final String THROWABLE_TYPE_NAME = THROWABLE_TYPE.getInternalName();
+ private static final String UNSUPPORTED_OPERATION_TYPE_NAME = UNSUPPORTED_OPERATION_TYPE.getInternalName();
+
+ private static final String METHOD_HANDLE_TYPE_DESCRIPTOR = METHOD_HANDLE_TYPE.getDescriptor();
+ private static final String GET_GLOBAL_METHOD_DESCRIPTOR = Type.getMethodDescriptor(SCRIPT_OBJECT_TYPE);
+ private static final String GET_CLASS_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.getType(Class.class));
+
+ // Package used when the adapter can't be defined in the adaptee's package (either because it's sealed, or because
+ // it's a java.* package.
+ private static final String ADAPTER_PACKAGE_PREFIX = "jdk/nashorn/internal/javaadapters/";
+ // Class name suffix used to append to the adaptee class name, when it can be defined in the adaptee's package.
+ private static final String ADAPTER_CLASS_NAME_SUFFIX = "$$NashornJavaAdapter";
+ private static final String JAVA_PACKAGE_PREFIX = "java/";
+ private static final int MAX_GENERATED_TYPE_NAME_LENGTH = 238; //255 - 17; 17 is the maximum possible length for the global setter inner class suffix
+
+ private static final String CLASS_INIT = "<clinit>";
+ private static final String STATIC_GLOBAL_FIELD_NAME = "staticGlobal";
+
+ /**
+ * Collection of methods we never override: Object.clone(), Object.finalize().
+ */
+ private static final Collection<MethodInfo> EXCLUDED = getExcludedMethods();
+
+ private static final Random random = new SecureRandom();
+
+ // This is the superclass for our generated adapter.
+ private final Class<?> superClass;
+ // Class loader used as the parent for the class loader we'll create to load the generated class. It will be a class
+ // loader that has the visibility of all original types (class to extend and interfaces to implement) and of the
+ // Nashorn classes.
+ private final ClassLoader commonLoader;
+ // Is this a generator for the version of the class that can have overrides on the class level?
+ private final boolean classOverride;
+ // Binary name of the superClass
+ private final String superClassName;
+ // Binary name of the generated class.
+ private final String generatedClassName;
+ // Binary name of the PrivilegedAction inner class that is used to
+ private final String globalSetterClassName;
+ private final Set<String> usedFieldNames = new HashSet<>();
+ private final Set<String> abstractMethodNames = new HashSet<>();
+ private final String samName;
+ private final Set<MethodInfo> finalMethods = new HashSet<>(EXCLUDED);
+ private final Set<MethodInfo> methodInfos = new HashSet<>();
+ private boolean autoConvertibleFromFunction = false;
+
+ private final ClassWriter cw;
+
+ /**
+ * Creates a generator for the bytecode for the adapter for the specified superclass and interfaces.
+ * @param superClass the superclass the adapter will extend.
+ * @param interfaces the interfaces the adapter will implement.
+ * @param commonLoader the class loader that can see all of superClass, interfaces, and Nashorn classes.
+ * @param classOverride true to generate the bytecode for the adapter that has both class-level and instance-level
+ * overrides, false to generate the bytecode for the adapter that only has instance-level overrides.
+ * @throws AdaptationException if the adapter can not be generated for some reason.
+ */
+ JavaAdapterBytecodeGenerator(final Class<?> superClass, final List<Class<?>> interfaces,
+ final ClassLoader commonLoader, final boolean classOverride) throws AdaptationException {
+ assert superClass != null && !superClass.isInterface();
+ assert interfaces != null;
+
+ this.superClass = superClass;
+ this.classOverride = classOverride;
+ this.commonLoader = commonLoader;
+ cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS) {
+ @Override
+ protected String getCommonSuperClass(final String type1, final String type2) {
+ // We need to override ClassWriter.getCommonSuperClass to use this factory's commonLoader as a class
+ // loader to find the common superclass of two types when needed.
+ return JavaAdapterBytecodeGenerator.this.getCommonSuperClass(type1, type2);
+ }
+ };
+ superClassName = Type.getInternalName(superClass);
+ generatedClassName = getGeneratedClassName(superClass, interfaces);
+
+ // Randomize the name of the privileged global setter, to make it non-feasible to find.
+ final long l;
+ synchronized(random) {
+ l = random.nextLong();
+ }
+
+ // NOTE: they way this class name is calculated affects the value of MAX_GENERATED_TYPE_NAME_LENGTH constant. If
+ // you change the calculation of globalSetterClassName, adjust the constant too.
+ globalSetterClassName = generatedClassName.concat("$" + Long.toHexString(l & Long.MAX_VALUE));
+ cw.visit(Opcodes.V1_7, ACC_PUBLIC | ACC_SUPER | ACC_FINAL, generatedClassName, null, superClassName, getInternalTypeNames(interfaces));
+
+ generateGlobalFields();
+
+ gatherMethods(superClass);
+ gatherMethods(interfaces);
+ samName = abstractMethodNames.size() == 1 ? abstractMethodNames.iterator().next() : null;
+ generateHandleFields();
+ if(classOverride) {
+ generateClassInit();
+ }
+ generateConstructors();
+ generateMethods();
+ // }
+ cw.visitEnd();
+ }
+
+ private void generateGlobalFields() {
+ cw.visitField(ACC_PRIVATE | ACC_FINAL, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR, null, null).visitEnd();
+ usedFieldNames.add(GLOBAL_FIELD_NAME);
+ if(classOverride) {
+ cw.visitField(ACC_PRIVATE | ACC_FINAL | ACC_STATIC, STATIC_GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR, null, null).visitEnd();
+ usedFieldNames.add(STATIC_GLOBAL_FIELD_NAME);
+ }
+ }
+
+ JavaAdapterClassLoader createAdapterClassLoader() {
+ return new JavaAdapterClassLoader(generatedClassName, cw.toByteArray(), globalSetterClassName);
+ }
+
+ boolean isAutoConvertibleFromFunction() {
+ return autoConvertibleFromFunction;
+ }
+
+ private static String getGeneratedClassName(final Class<?> superType, final List<Class<?>> interfaces) {
+ // The class we use to primarily name our adapter is either the superclass, or if it is Object (meaning we're
+ // just implementing interfaces or extending Object), then the first implemented interface or Object.
+ final Class<?> namingType = superType == Object.class ? (interfaces.isEmpty()? Object.class : interfaces.get(0)) : superType;
+ final Package pkg = namingType.getPackage();
+ final String namingTypeName = Type.getInternalName(namingType);
+ final StringBuilder buf = new StringBuilder();
+ if (namingTypeName.startsWith(JAVA_PACKAGE_PREFIX) || pkg == null || pkg.isSealed()) {
+ // Can't define new classes in java.* packages
+ buf.append(ADAPTER_PACKAGE_PREFIX).append(namingTypeName);
+ } else {
+ buf.append(namingTypeName).append(ADAPTER_CLASS_NAME_SUFFIX);
+ }
+ final Iterator<Class<?>> it = interfaces.iterator();
+ if(superType == Object.class && it.hasNext()) {
+ it.next(); // Skip first interface, it was used to primarily name the adapter
+ }
+ // Append interface names to the adapter name
+ while(it.hasNext()) {
+ buf.append("$$").append(it.next().getSimpleName());
+ }
+ return buf.toString().substring(0, Math.min(MAX_GENERATED_TYPE_NAME_LENGTH, buf.length()));
+ }
+
+ /**
+ * Given a list of class objects, return an array with their binary names. Used to generate the array of interface
+ * names to implement.
+ * @param classes the classes
+ * @return an array of names
+ */
+ private static String[] getInternalTypeNames(final List<Class<?>> classes) {
+ final int interfaceCount = classes.size();
+ final String[] interfaceNames = new String[interfaceCount];
+ for(int i = 0; i < interfaceCount; ++i) {
+ interfaceNames[i] = Type.getInternalName(classes.get(i));
+ }
+ return interfaceNames;
+ }
+
+ private void generateHandleFields() {
+ for (final MethodInfo mi: methodInfos) {
+ cw.visitField(ACC_PRIVATE | ACC_FINAL, mi.methodHandleInstanceFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR, null, null).visitEnd();
+ if(classOverride) {
+ cw.visitField(ACC_PRIVATE | ACC_FINAL | ACC_STATIC, mi.methodHandleClassFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR, null, null).visitEnd();
+ }
+ }
+ }
+
+ private void generateClassInit() {
+ final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_STATIC, CLASS_INIT,
+ Type.getMethodDescriptor(Type.VOID_TYPE), null, null));
+
+ mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getClassOverrides", GET_CLASS_INITIALIZER_DESCRIPTOR);
+ // Assign MethodHandle fields through invoking getHandle()
+ for (final MethodInfo mi : methodInfos) {
+ mv.dup();
+ mv.aconst(mi.getName());
+ mv.aconst(Type.getMethodType(mi.type.toMethodDescriptorString()));
+ mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getHandle", GET_HANDLE_OBJECT_DESCRIPTOR);
+ mv.putstatic(generatedClassName, mi.methodHandleClassFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR);
+ }
+
+ // Assign "staticGlobal = Context.getGlobal()"
+ invokeGetGlobalWithNullCheck(mv);
+ mv.putstatic(generatedClassName, STATIC_GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
+
+ endInitMethod(mv);
+ }
+
+ private static void invokeGetGlobalWithNullCheck(final InstructionAdapter mv) {
+ invokeGetGlobal(mv);
+ mv.dup();
+ mv.invokevirtual(OBJECT_TYPE_NAME, "getClass", GET_CLASS_METHOD_DESCRIPTOR); // check against null Context
+ mv.pop();
+ }
+
+ private void generateConstructors() throws AdaptationException {
+ boolean gotCtor = false;
+ for (final Constructor<?> ctor: superClass.getDeclaredConstructors()) {
+ final int modifier = ctor.getModifiers();
+ if((modifier & (Modifier.PUBLIC | Modifier.PROTECTED)) != 0) {
+ generateConstructors(ctor);
+ gotCtor = true;
+ }
+ }
+ if(!gotCtor) {
+ throw new AdaptationException(ERROR_NO_ACCESSIBLE_CONSTRUCTOR, superClass.getCanonicalName());
+ }
+ }
+
+ private void generateConstructors(final Constructor<?> ctor) {
+ if(classOverride) {
+ // Generate a constructor that just delegates to ctor. This is used with class-level overrides, when we want
+ // to create instances without further per-instance overrides.
+ generateDelegatingConstructor(ctor);
+ }
+
+ // Generate a constructor that delegates to ctor, but takes an additional ScriptObject parameter at the
+ // beginning of its parameter list.
+ generateOverridingConstructor(ctor, false);
+
+ if (samName != null) {
+ if (!autoConvertibleFromFunction && ctor.getParameterTypes().length == 0) {
+ // If the original type only has a single abstract method name, as well as a default ctor, then it can
+ // be automatically converted from JS function.
+ autoConvertibleFromFunction = true;
+ }
+ // If all our abstract methods have a single name, generate an additional constructor, one that takes a
+ // ScriptFunction as its first parameter and assigns it as the implementation for all abstract methods.
+ generateOverridingConstructor(ctor, true);
+ }
+ }
+
+ private void generateDelegatingConstructor(final Constructor<?> ctor) {
+ final Type originalCtorType = Type.getType(ctor);
+ final Type[] argTypes = originalCtorType.getArgumentTypes();
+
+ // All constructors must be public, even if in the superclass they were protected.
+ final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_PUBLIC, INIT,
+ Type.getMethodDescriptor(originalCtorType.getReturnType(), argTypes), null, null));
+
+ mv.visitCode();
+ // Invoke super constructor with the same arguments.
+ mv.visitVarInsn(ALOAD, 0);
+ int offset = 1; // First arg is at position 1, after this.
+ for (Type argType: argTypes) {
+ mv.load(offset, argType);
+ offset += argType.getSize();
+ }
+ mv.invokespecial(superClassName, INIT, originalCtorType.getDescriptor());
+
+ endInitMethod(mv);
+ }
+
+ /**
+ * Generates a constructor for the adapter class. This constructor will take the same arguments as the supertype
+ * constructor passed as the argument here, and delegate to it. However, it will take an additional argument of
+ * either ScriptObject or ScriptFunction type (based on the value of the "fromFunction" parameter), and initialize
+ * all the method handle fields of the adapter instance with functions from the script object (or the script
+ * function itself, if that's what's passed). There is one method handle field in the adapter class for every method
+ * that can be implemented or overridden; the name of every field is same as the name of the method, with a number
+ * suffix that makes it unique in case of overloaded methods. The generated constructor will invoke
+ * {@link #getHandle(ScriptFunction, MethodType, boolean)} or {@link #getHandle(Object, String, MethodType,
+ * boolean)} to obtain the method handles; these methods make sure to add the necessary conversions and arity
+ * adjustments so that the resulting method handles can be invoked from generated methods using {@code invokeExact}.
+ * The constructor that takes a script function will only initialize the methods with the same name as the single
+ * abstract method. The constructor will also store the Nashorn global that was current at the constructor
+ * invocation time in a field named "global". The generated constructor will be public, regardless of whether the
+ * supertype constructor was public or protected. The generated constructor will not be variable arity, even if the
+ * supertype constructor was.
+ * @param ctor the supertype constructor that is serving as the base for the generated constructor.
+ * @param fromFunction true if we're generating a constructor that initializes SAM types from a single
+ * ScriptFunction passed to it, false if we're generating a constructor that initializes an arbitrary type from a
+ * ScriptObject passed to it.
+ */
+ private void generateOverridingConstructor(final Constructor<?> ctor, final boolean fromFunction) {
+ final Type originalCtorType = Type.getType(ctor);
+ final Type[] originalArgTypes = originalCtorType.getArgumentTypes();
+ 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;
+ newArgTypes[argLen] = extraArgumentType;
+ System.arraycopy(originalArgTypes, 0, newArgTypes, 0, argLen);
+
+ // All constructors must be public, even if in the superclass they were protected.
+ // Existing super constructor <init>(this, args...) triggers generating <init>(this, scriptObj, args...).
+ final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_PUBLIC, INIT,
+ Type.getMethodDescriptor(originalCtorType.getReturnType(), newArgTypes), null, null));
+
+ mv.visitCode();
+ // First, invoke super constructor with original arguments. If the form of the constructor we're generating is
+ // <init>(this, args..., scriptFn), then we're invoking super.<init>(this, args...).
+ 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 < argLen; ++i) {
+ final Type argType = Type.getType(argTypes[i]);
+ mv.load(offset, argType);
+ offset += argType.getSize();
+ }
+ mv.invokespecial(superClassName, INIT, originalCtorType.getDescriptor());
+
+ // Get a descriptor to the appropriate "JavaAdapterFactory.getHandle" method.
+ final String getHandleDescriptor = fromFunction ? GET_HANDLE_FUNCTION_DESCRIPTOR : GET_HANDLE_OBJECT_DESCRIPTOR;
+
+ // Assign MethodHandle fields through invoking getHandle()
+ for (final MethodInfo mi : methodInfos) {
+ mv.visitVarInsn(ALOAD, 0);
+ if (fromFunction && !mi.getName().equals(samName)) {
+ // Constructors initializing from a ScriptFunction only initialize methods with the SAM name.
+ // NOTE: if there's a concrete overloaded method sharing the SAM name, it'll be overriden too. This
+ // is a deliberate design choice. All other method handles are initialized to null.
+ mv.visitInsn(ACONST_NULL);
+ } else {
+ mv.visitVarInsn(ALOAD, offset);
+ if(!fromFunction) {
+ mv.aconst(mi.getName());
+ }
+ mv.aconst(Type.getMethodType(mi.type.toMethodDescriptorString()));
+ mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getHandle", getHandleDescriptor);
+ }
+ mv.putfield(generatedClassName, mi.methodHandleInstanceFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR);
+ }
+
+ // Assign "this.global = Context.getGlobal()"
+ mv.visitVarInsn(ALOAD, 0);
+ invokeGetGlobalWithNullCheck(mv);
+ mv.putfield(generatedClassName, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
+
+ endInitMethod(mv);
+ }
+
+ private static void endInitMethod(final InstructionAdapter mv) {
+ mv.visitInsn(RETURN);
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+ }
+
+ private static void invokeGetGlobal(final InstructionAdapter mv) {
+ mv.invokestatic(CONTEXT_TYPE_NAME, "getGlobal", GET_GLOBAL_METHOD_DESCRIPTOR);
+ }
+
+ private void invokeSetGlobal(final InstructionAdapter mv) {
+ mv.invokestatic(globalSetterClassName, "setGlobal", SET_GLOBAL_METHOD_DESCRIPTOR);
+ }
+
+ /**
+ * Encapsulation of the information used to generate methods in the adapter classes. Basically, a wrapper around the
+ * reflective Method object, a cached MethodType, and the name of the field in the adapter class that will hold the
+ * method handle serving as the implementation of this method in adapter instances.
+ *
+ */
+ private static class MethodInfo {
+ private final Method method;
+ private final MethodType type;
+ private String methodHandleInstanceFieldName;
+ private String methodHandleClassFieldName;
+
+ private MethodInfo(final Class<?> clazz, final String name, final Class<?>... argTypes) throws NoSuchMethodException {
+ this(clazz.getDeclaredMethod(name, argTypes));
+ }
+
+ private MethodInfo(final Method method) {
+ this.method = method;
+ this.type = MH.type(method.getReturnType(), method.getParameterTypes());
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ return obj instanceof MethodInfo && equals((MethodInfo)obj);
+ }
+
+ private boolean equals(final MethodInfo other) {
+ // Only method name and type are used for comparison; method handle field name is not.
+ return getName().equals(other.getName()) && type.equals(other.type);
+ }
+
+ String getName() {
+ return method.getName();
+ }
+
+ @Override
+ public int hashCode() {
+ return getName().hashCode() ^ type.hashCode();
+ }
+
+ void setIsCanonical(final Set<String> usedFieldNames, boolean classOverride) {
+ methodHandleInstanceFieldName = nextName(usedFieldNames);
+ if(classOverride) {
+ methodHandleClassFieldName = nextName(usedFieldNames);
+ }
+ }
+
+ String nextName(final Set<String> usedFieldNames) {
+ int i = 0;
+ final String name = getName();
+ String nextName = name;
+ while (!usedFieldNames.add(nextName)) {
+ final String ordinal = String.valueOf(i++);
+ final int maxNameLen = 255 - ordinal.length();
+ nextName = (name.length() <= maxNameLen ? name : name.substring(0, maxNameLen)).concat(ordinal);
+ }
+ return nextName;
+ }
+
+ }
+
+ private void generateMethods() {
+ for(final MethodInfo mi: methodInfos) {
+ generateMethod(mi);
+ }
+ }
+
+ /**
+ * Generates a method in the adapter class that adapts a method from the original class. The generated methods will
+ * inspect the method handle field assigned to them. If it is null (the JS object doesn't provide an implementation
+ * for the method) then it will either invoke its version in the supertype, or if it is abstract, throw an
+ * {@link UnsupportedOperationException}. Otherwise, if the method handle field's value is not null, the handle is
+ * invoked using invokeExact (signature polymorphic invocation as per JLS 15.12.3). Before the invocation, the
+ * current Nashorn {@link Context} is checked, and if it is different than the global used to create the adapter
+ * instance, the creating global is set to be the current global. In this case, the previously current global is
+ * restored after the invocation. If invokeExact results in a Throwable that is not one of the method's declared
+ * exceptions, and is not an unchecked throwable, then it is wrapped into a {@link RuntimeException} and the runtime
+ * exception is thrown. The method handle retrieved from the field is guaranteed to exactly match the signature of
+ * the method; this is guaranteed by the way constructors of the adapter class obtain them using
+ * {@link #getHandle(Object, String, MethodType, boolean)}.
+ * @param mi the method info describing the method to be generated.
+ */
+ private void generateMethod(final MethodInfo mi) {
+ final Method method = mi.method;
+ final int mod = method.getModifiers();
+ final int access = ACC_PUBLIC | (method.isVarArgs() ? ACC_VARARGS : 0);
+ final Class<?>[] exceptions = method.getExceptionTypes();
+ final String[] exceptionNames = new String[exceptions.length];
+ for (int i = 0; i < exceptions.length; ++i) {
+ exceptionNames[i] = Type.getInternalName(exceptions[i]);
+ }
+ final MethodType type = mi.type;
+ final String methodDesc = type.toMethodDescriptorString();
+ final String name = mi.getName();
+
+ final Type asmType = Type.getMethodType(methodDesc);
+ final Type[] asmArgTypes = asmType.getArgumentTypes();
+
+ // Determine the first index for a local variable
+ int nextLocalVar = 1; // this
+ for(final Type t: asmArgTypes) {
+ nextLocalVar += t.getSize();
+ }
+
+ final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(access, name, methodDesc, null,
+ exceptionNames));
+ mv.visitCode();
+
+ final Label instanceHandleDefined = new Label();
+ final Label classHandleDefined = new Label();
+
+ final Type asmReturnType = Type.getType(type.returnType());
+
+ // See if we have instance handle defined
+ mv.visitVarInsn(ALOAD, 0);
+ mv.getfield(generatedClassName, mi.methodHandleInstanceFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR);
+ // stack: [instanceHandle]
+ jumpIfNonNullKeepOperand(mv, instanceHandleDefined);
+
+ if(classOverride) {
+ // See if we have the static handle
+ mv.getstatic(generatedClassName, mi.methodHandleClassFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR);
+ // stack: [classHandle]
+ jumpIfNonNullKeepOperand(mv, classHandleDefined);
+ }
+
+ // No handle is available, fall back to default behavior
+ if(Modifier.isAbstract(mod)) {
+ // If the super method is abstract, throw an exception
+ mv.anew(UNSUPPORTED_OPERATION_TYPE);
+ mv.dup();
+ mv.invokespecial(UNSUPPORTED_OPERATION_TYPE_NAME, INIT, VOID_NOARG_METHOD_DESCRIPTOR);
+ mv.athrow();
+ } else {
+ // If the super method is not abstract, delegate to it.
+ mv.visitVarInsn(ALOAD, 0);
+ int nextParam = 1;
+ for(final Type t: asmArgTypes) {
+ mv.load(nextParam, t);
+ nextParam += t.getSize();
+ }
+ mv.invokespecial(superClassName, name, methodDesc);
+ mv.areturn(asmReturnType);
+ }
+
+ final Label setupGlobal = new Label();
+
+ if(classOverride) {
+ mv.visitLabel(classHandleDefined);
+ // If class handle is defined, load the static defining global
+ mv.getstatic(generatedClassName, STATIC_GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
+ // stack: [creatingGlobal := classGlobal, classHandle]
+ mv.goTo(setupGlobal);
+ }
+
+ mv.visitLabel(instanceHandleDefined);
+ // If instance handle is defined, load the instance defining global
+ mv.visitVarInsn(ALOAD, 0);
+ mv.getfield(generatedClassName, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
+ // stack: [creatingGlobal := instanceGlobal, instanceHandle]
+
+ // fallthrough to setupGlobal
+
+ // stack: [creatingGlobal, someHandle]
+ mv.visitLabel(setupGlobal);
+
+ final int currentGlobalVar = nextLocalVar++;
+ final int globalsDifferVar = nextLocalVar++;
+
+ mv.dup();
+ // stack: [creatingGlobal, creatingGlobal, someHandle]
+
+ // Emit code for switching to the creating global
+ // ScriptObject currentGlobal = Context.getGlobal();
+ invokeGetGlobal(mv);
+ mv.dup();
+ mv.visitVarInsn(ASTORE, currentGlobalVar);
+ // stack: [currentGlobal, creatingGlobal, creatingGlobal, someHandle]
+ // if(definingGlobal == currentGlobal) {
+ final Label globalsDiffer = new Label();
+ mv.ifacmpne(globalsDiffer);
+ // stack: [someGlobal, someHandle]
+ // globalsDiffer = false
+ mv.pop();
+ // stack: [someHandle]
+ mv.iconst(0); // false
+ // stack: [false, someHandle]
+ final Label invokeHandle = new Label();
+ mv.goTo(invokeHandle);
+ mv.visitLabel(globalsDiffer);
+ // } else {
+ // Context.setGlobal(definingGlobal);
+ // stack: [someGlobal, someHandle]
+ invokeSetGlobal(mv);
+ // stack: [someHandle]
+ // globalsDiffer = true
+ mv.iconst(1);
+ // stack: [true, someHandle]
+
+ mv.visitLabel(invokeHandle);
+ mv.visitVarInsn(ISTORE, globalsDifferVar);
+ // stack: [someHandle]
+
+ // Load all parameters back on stack for dynamic invocation.
+ int varOffset = 1;
+ for (final Type t : asmArgTypes) {
+ mv.load(varOffset, t);
+ varOffset += t.getSize();
+ }
+
+ // Invoke the target method handle
+ final Label tryBlockStart = new Label();
+ mv.visitLabel(tryBlockStart);
+ mv.invokevirtual(METHOD_HANDLE_TYPE.getInternalName(), "invokeExact", type.toMethodDescriptorString());
+ final Label tryBlockEnd = new Label();
+ mv.visitLabel(tryBlockEnd);
+ emitFinally(mv, currentGlobalVar, globalsDifferVar);
+ mv.areturn(asmReturnType);
+
+ // If Throwable is not declared, we need an adapter from Throwable to RuntimeException
+ final boolean throwableDeclared = isThrowableDeclared(exceptions);
+ final Label throwableHandler;
+ if (!throwableDeclared) {
+ // Add "throw new RuntimeException(Throwable)" handler for Throwable
+ throwableHandler = new Label();
+ mv.visitLabel(throwableHandler);
+ mv.anew(RUNTIME_EXCEPTION_TYPE);
+ mv.dupX1();
+ mv.swap();
+ mv.invokespecial(RUNTIME_EXCEPTION_TYPE_NAME, INIT, Type.getMethodDescriptor(Type.VOID_TYPE, THROWABLE_TYPE));
+ // Fall through to rethrow handler
+ } else {
+ throwableHandler = null;
+ }
+ final Label rethrowHandler = new Label();
+ mv.visitLabel(rethrowHandler);
+ // Rethrow handler for RuntimeException, Error, and all declared exception types
+ emitFinally(mv, currentGlobalVar, globalsDifferVar);
+ mv.athrow();
+ final Label methodEnd = new Label();
+ mv.visitLabel(methodEnd);
+
+ mv.visitLocalVariable("currentGlobal", SCRIPT_OBJECT_TYPE_DESCRIPTOR, null, setupGlobal, methodEnd, currentGlobalVar);
+ mv.visitLocalVariable("globalsDiffer", Type.INT_TYPE.getDescriptor(), null, setupGlobal, methodEnd, globalsDifferVar);
+
+ if(throwableDeclared) {
+ mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, THROWABLE_TYPE_NAME);
+ assert throwableHandler == null;
+ } else {
+ mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, RUNTIME_EXCEPTION_TYPE_NAME);
+ mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, ERROR_TYPE_NAME);
+ for(final String excName: exceptionNames) {
+ mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, excName);
+ }
+ mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, throwableHandler, THROWABLE_TYPE_NAME);
+ }
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+ }
+
+ /**
+ * Emits code for jumping to a label if the top stack operand is not null. The operand is kept on the stack if it
+ * is not null (so is available to code at the jump address) and is popped if it is null.
+ * @param mv the instruction adapter being used to emit code
+ * @param label the label to jump to
+ */
+ private static void jumpIfNonNullKeepOperand(final InstructionAdapter mv, final Label label) {
+ mv.visitInsn(DUP);
+ mv.visitJumpInsn(IFNONNULL, label);
+ mv.visitInsn(POP);
+ }
+
+ /**
+ * Emit code to restore the previous Nashorn Context when needed.
+ * @param mv the instruction adapter
+ * @param currentGlobalVar index of the local variable holding the reference to the current global at method
+ * entry.
+ * @param globalsDifferVar index of the boolean local variable that is true if the global needs to be restored.
+ */
+ private void emitFinally(final InstructionAdapter mv, final int currentGlobalVar, final int globalsDifferVar) {
+ // Emit code to restore the previous Nashorn global if needed
+ mv.visitVarInsn(ILOAD, globalsDifferVar);
+ final Label skip = new Label();
+ mv.ifeq(skip);
+ mv.visitVarInsn(ALOAD, currentGlobalVar);
+ invokeSetGlobal(mv);
+ mv.visitLabel(skip);
+ }
+
+ private static boolean isThrowableDeclared(final Class<?>[] exceptions) {
+ for (final Class<?> exception : exceptions) {
+ if (exception == Throwable.class) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Gathers methods that can be implemented or overridden from the specified type into this factory's
+ * {@link #methodInfos} set. It will add all non-final, non-static methods that are either public or protected from
+ * the type if the type itself is public. If the type is a class, the method will recursively invoke itself for its
+ * superclass and the interfaces it implements, and add further methods that were not directly declared on the
+ * class.
+ * @param type the type defining the methods.
+ */
+ private void gatherMethods(final Class<?> type) {
+ if (Modifier.isPublic(type.getModifiers())) {
+ final Method[] typeMethods = type.isInterface() ? type.getMethods() : type.getDeclaredMethods();
+
+ for (final Method typeMethod: typeMethods) {
+ final int m = typeMethod.getModifiers();
+ if (Modifier.isStatic(m)) {
+ continue;
+ }
+ if (Modifier.isPublic(m) || Modifier.isProtected(m)) {
+ final MethodInfo mi = new MethodInfo(typeMethod);
+ if (Modifier.isFinal(m)) {
+ finalMethods.add(mi);
+ } else if (!finalMethods.contains(mi) && methodInfos.add(mi)) {
+ if (Modifier.isAbstract(m)) {
+ abstractMethodNames.add(mi.getName());
+ }
+ mi.setIsCanonical(usedFieldNames, classOverride);
+ }
+ }
+ }
+ }
+ // If the type is a class, visit its superclasses and declared interfaces. If it's an interface, we're done.
+ // Needing to invoke the method recursively for a non-interface Class object is the consequence of needing to
+ // see all declared protected methods, and Class.getDeclaredMethods() doesn't provide those declared in a
+ // superclass. For interfaces, we used Class.getMethods(), as we're only interested in public ones there, and
+ // getMethods() does provide those declared in a superinterface.
+ if (!type.isInterface()) {
+ final Class<?> superType = type.getSuperclass();
+ if (superType != null) {
+ gatherMethods(superType);
+ }
+ for (final Class<?> itf: type.getInterfaces()) {
+ gatherMethods(itf);
+ }
+ }
+ }
+
+ private void gatherMethods(final List<Class<?>> classes) {
+ for(final Class<?> c: classes) {
+ gatherMethods(c);
+ }
+ }
+
+ /**
+ * Creates a collection of methods that are not final, but we still never allow them to be overridden in adapters,
+ * as explicitly declaring them automatically is a bad idea. Currently, this means {@code Object.finalize()} and
+ * {@code Object.clone()}.
+ * @return a collection of method infos representing those methods that we never override in adapter classes.
+ */
+ private static Collection<MethodInfo> getExcludedMethods() {
+ return AccessController.doPrivileged(new PrivilegedAction<Collection<MethodInfo>>() {
+ @Override
+ public Collection<MethodInfo> run() {
+ try {
+ return Arrays.asList(
+ new MethodInfo(Object.class, "finalize"),
+ new MethodInfo(Object.class, "clone"));
+ } catch (final NoSuchMethodException e) {
+ throw new AssertionError(e);
+ }
+ }
+ });
+ }
+
+ private String getCommonSuperClass(final String type1, final String type2) {
+ try {
+ final Class<?> c1 = Class.forName(type1.replace('/', '.'), false, commonLoader);
+ final Class<?> c2 = Class.forName(type2.replace('/', '.'), false, commonLoader);
+ if (c1.isAssignableFrom(c2)) {
+ return type1;
+ }
+ if (c2.isAssignableFrom(c1)) {
+ return type2;
+ }
+ if (c1.isInterface() || c2.isInterface()) {
+ return OBJECT_TYPE_NAME;
+ }
+ return assignableSuperClass(c1, c2).getName().replace('.', '/');
+ } catch(final ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static Class<?> assignableSuperClass(final Class<?> c1, final Class<?> c2) {
+ final Class<?> superClass = c1.getSuperclass();
+ return superClass.isAssignableFrom(c2) ? superClass : assignableSuperClass(superClass, c2);
+ }
+}
diff --git a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterClassLoader.java b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterClassLoader.java
new file mode 100644
index 00000000..c791274e
--- /dev/null
+++ b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterClassLoader.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.runtime.linker;
+
+import static jdk.internal.org.objectweb.asm.Opcodes.ACC_FINAL;
+import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PRIVATE;
+import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
+import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC;
+import static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER;
+import static jdk.internal.org.objectweb.asm.Opcodes.ACONST_NULL;
+import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD;
+import static jdk.internal.org.objectweb.asm.Opcodes.ARETURN;
+import static jdk.internal.org.objectweb.asm.Opcodes.RETURN;
+
+import java.security.AccessController;
+import java.security.AllPermission;
+import java.security.CodeSigner;
+import java.security.CodeSource;
+import java.security.Permissions;
+import java.security.PrivilegedAction;
+import java.security.ProtectionDomain;
+import java.security.SecureClassLoader;
+import jdk.internal.dynalink.beans.StaticClass;
+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.internal.runtime.Context;
+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.
+ * It can be invoked repeatedly to create multiple adapter classes from the same bytecode; adapter classes that have
+ * class-level overrides must be re-created for every set of such overrides. Note that while this class is named
+ * "class loader", it does not, in fact, extend {@code ClassLoader}, but rather uses them internally. Instances of this
+ * class are normally created by {@link JavaAdapterBytecodeGenerator}.
+ */
+class JavaAdapterClassLoader extends JavaAdapterGeneratorBase {
+ private static final Type PRIVILEGED_ACTION_TYPE = Type.getType(PrivilegedAction.class);
+
+ private static final String PRIVILEGED_ACTION_TYPE_NAME = PRIVILEGED_ACTION_TYPE.getInternalName();
+ private static final String PRIVILEGED_RUN_METHOD_DESCRIPTOR = Type.getMethodDescriptor(OBJECT_TYPE);
+
+ private static final ProtectionDomain GENERATED_PROTECTION_DOMAIN = createGeneratedProtectionDomain();
+
+ private final String className;
+ private final byte[] classBytes;
+ private final String globalSetterClassName;
+
+ JavaAdapterClassLoader(String className, byte[] classBytes, String globalSetterClassName) {
+ this.className = className.replace('/', '.');
+ this.classBytes = classBytes;
+ this.globalSetterClassName = globalSetterClassName.replace('/', '.');
+ }
+
+ /**
+ * Loads the generated adapter class into the JVM.
+ * @param parentLoader the parent class loader for the generated class loader
+ * @return the generated adapter class
+ */
+ StaticClass generateClass(final ClassLoader parentLoader) {
+ return AccessController.doPrivileged(new PrivilegedAction<StaticClass>() {
+ @Override
+ public StaticClass run() {
+ try {
+ return StaticClass.forClass(Class.forName(className, true, createClassLoader(parentLoader)));
+ } catch (final ClassNotFoundException e) {
+ throw new AssertionError(e); // cannot happen
+ }
+ }
+ });
+ }
+
+ private static class AdapterLoader extends SecureClassLoader {
+ AdapterLoader(ClassLoader parent) {
+ super(parent);
+ }
+ }
+
+ static boolean isAdapterClass(Class<?> clazz) {
+ return clazz.getClassLoader() instanceof AdapterLoader;
+ }
+
+ // Note that the adapter class is created in the protection domain of the class/interface being
+ // extended/implemented, and only the privileged global setter action class is generated in the protection domain
+ // of Nashorn itself. Also note that the creation and loading of the global setter is deferred until it is
+ // required by JVM linker, which will only happen on first invocation of any of the adapted method. We could defer
+ // it even more by separating its invocation into a separate static method on the adapter class, but then someone
+ // with ability to introspect on the class and use setAccessible(true) on it could invoke the method. It's a
+ // security tradeoff...
+ private ClassLoader createClassLoader(final ClassLoader parentLoader) {
+ return new AdapterLoader(parentLoader) {
+ private final ClassLoader myLoader = getClass().getClassLoader();
+ private final ProtectionDomain myProtectionDomain = getClass().getProtectionDomain();
+
+ @Override
+ public Class<?> loadClass(final String name, final boolean resolve) throws ClassNotFoundException {
+ try {
+ return super.loadClass(name, resolve);
+ } catch (final SecurityException se) {
+ // we may be implementing an interface or extending a class that was
+ // loaded by a loader that prevents package.access. If so, it'd throw
+ // SecurityException for nashorn's classes!. For adapter's to work, we
+ // should be able to refer to nashorn classes.
+ if (name.startsWith("jdk.nashorn.internal.")) {
+ return myLoader.loadClass(name);
+ }
+ throw se;
+ }
+ }
+
+ @Override
+ protected Class<?> findClass(final String name) throws ClassNotFoundException {
+ if(name.equals(className)) {
+ return defineClass(name, classBytes, 0, classBytes.length, GENERATED_PROTECTION_DOMAIN);
+ } else if(name.equals(globalSetterClassName)) {
+ final byte[] bytes = generatePrivilegedActionClassBytes(globalSetterClassName.replace('.', '/'));
+ return defineClass(name, bytes, 0, bytes.length, myProtectionDomain);
+ } else {
+ throw new ClassNotFoundException(name);
+ }
+ }
+ };
+ }
+
+ private static ProtectionDomain createGeneratedProtectionDomain() {
+ // Generated classes need to have AllPermission. Since we require the "createClassLoader" RuntimePermission, we
+ // can create a class loader that'll load new classes with any permissions. Our generated classes are just
+ // delegating adapters, so having AllPermission can't cause anything wrong; the effective set of permissions for
+ // the executing script functions will still be limited by the permissions of the caller and the permissions of
+ // the script.
+ final Permissions permissions = new Permissions();
+ permissions.add(new AllPermission());
+ return new ProtectionDomain(new CodeSource(null, (CodeSigner[])null), permissions);
+ }
+
+ /**
+ * Generates a PrivilegedAction implementation class for invoking {@link Context#setGlobal(ScriptObject)} from the
+ * adapter class.
+ */
+ private static byte[] generatePrivilegedActionClassBytes(final String className) {
+ final ClassWriter w = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
+ // class GlobalSetter implements PrivilegedAction {
+ w.visit(Opcodes.V1_7, ACC_SUPER | ACC_FINAL, className, null, OBJECT_TYPE_NAME, new String[] {
+ PRIVILEGED_ACTION_TYPE_NAME
+ });
+
+ // private final ScriptObject global;
+ w.visitField(ACC_PRIVATE | ACC_FINAL, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR, null, null).visitEnd();
+
+ // private GlobalSetter(ScriptObject global) {
+ InstructionAdapter mv = new InstructionAdapter(w.visitMethod(ACC_PRIVATE, INIT,
+ SET_GLOBAL_METHOD_DESCRIPTOR, null, new String[0]));
+ mv.visitCode();
+ // super();
+ mv.visitVarInsn(ALOAD, 0);
+ mv.invokespecial(OBJECT_TYPE_NAME, INIT, VOID_NOARG_METHOD_DESCRIPTOR);
+ // this.global = global;
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(ALOAD, 1);
+ mv.putfield(className, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
+
+ mv.visitInsn(RETURN);
+ mv.visitEnd();
+ mv.visitMaxs(0, 0);
+
+ // public Object run() {
+ mv = new InstructionAdapter(w.visitMethod(ACC_PUBLIC, "run", PRIVILEGED_RUN_METHOD_DESCRIPTOR, null,
+ new String[0]));
+ mv.visitCode();
+ // Context.setGlobal(this.global);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.getfield(className, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
+ mv.invokestatic(CONTEXT_TYPE_NAME, "setGlobal", SET_GLOBAL_METHOD_DESCRIPTOR);
+ // return null;
+ mv.visitInsn(ACONST_NULL);
+ mv.visitInsn(ARETURN);
+
+ mv.visitEnd();
+ mv.visitMaxs(0, 0);
+
+ // static void setGlobal(ScriptObject global) {
+ mv = new InstructionAdapter(w.visitMethod(ACC_STATIC, "setGlobal", SET_GLOBAL_METHOD_DESCRIPTOR, null,
+ new String[0]));
+ mv.visitCode();
+ // new GlobalSetter(ScriptObject global)
+ mv.anew(Type.getType("L" + className + ";"));
+ mv.dup();
+ mv.visitVarInsn(ALOAD, 0);
+ mv.invokespecial(className, INIT, SET_GLOBAL_METHOD_DESCRIPTOR);
+ // AccessController.doPrivileged(...)
+ mv.invokestatic(Type.getInternalName(AccessController.class), "doPrivileged", Type.getMethodDescriptor(
+ OBJECT_TYPE, PRIVILEGED_ACTION_TYPE));
+ mv.pop();
+ mv.visitInsn(RETURN);
+
+ mv.visitEnd();
+ mv.visitMaxs(0, 0);
+
+ return w.toByteArray();
+ }
+}
diff --git a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java
index 7b0d6d76..f3f51fd8 100644
--- a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java
+++ b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java
@@ -25,110 +25,39 @@
package jdk.nashorn.internal.runtime.linker;
-import static jdk.internal.org.objectweb.asm.Opcodes.ACC_FINAL;
-import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PRIVATE;
-import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
-import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC;
-import static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER;
-import static jdk.internal.org.objectweb.asm.Opcodes.ACC_VARARGS;
-import static jdk.internal.org.objectweb.asm.Opcodes.ACONST_NULL;
-import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD;
-import static jdk.internal.org.objectweb.asm.Opcodes.ARETURN;
-import static jdk.internal.org.objectweb.asm.Opcodes.ASTORE;
-import static jdk.internal.org.objectweb.asm.Opcodes.DUP;
-import static jdk.internal.org.objectweb.asm.Opcodes.IFNONNULL;
-import static jdk.internal.org.objectweb.asm.Opcodes.ILOAD;
-import static jdk.internal.org.objectweb.asm.Opcodes.ISTORE;
-import static jdk.internal.org.objectweb.asm.Opcodes.RETURN;
-import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.lookup.Lookup.MH;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.AccessController;
-import java.security.AllPermission;
-import java.security.CodeSigner;
-import java.security.CodeSource;
-import java.security.Permissions;
import java.security.PrivilegedAction;
import java.security.PrivilegedExceptionAction;
-import java.security.ProtectionDomain;
-import java.security.SecureClassLoader;
-import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
-import java.util.Random;
-import java.util.Set;
import jdk.internal.dynalink.beans.StaticClass;
import jdk.internal.dynalink.support.LinkRequestImpl;
-import jdk.internal.org.objectweb.asm.ClassWriter;
-import jdk.internal.org.objectweb.asm.Label;
-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.internal.objects.NativeJava;
-import jdk.nashorn.internal.runtime.Context;
-import jdk.nashorn.internal.runtime.ECMAErrors;
import jdk.nashorn.internal.runtime.ECMAException;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
-import jdk.nashorn.internal.runtime.ScriptRuntime;
-import jdk.nashorn.internal.runtime.Undefined;
/**
* <p>A factory class that generates adapter classes. Adapter classes allow implementation of Java interfaces and
* extending of Java classes from JavaScript. For every combination of a superclass to extend and interfaces to
* implement (collectively: "original types"), exactly one adapter class is generated that extends the specified
- * superclass and implements the specified interfaces.
+ * superclass and implements the specified interfaces. (But see the discussion of class-based overrides for exceptions.)
* </p><p>
* The adapter class is generated in a new secure class loader that inherits Nashorn's protection domain, and has either
* one of the original types' class loader or the Nashorn's class loader as its parent - the parent class loader
* is chosen so that all the original types and the Nashorn core classes are visible from it (as the adapter will have
* constant pool references to ScriptObject and ScriptFunction classes). In case none of the candidate class loaders has
- * visibility of all the required types, an error is thrown.
- * </p><p>
- * For every protected or public constructor in the extended class, the adapter class will have one or two public
- * constructors (visibility of protected constructors in the extended class is promoted to public). In every case, for
- * every original constructor, a new constructor taking a trailing ScriptObject argument preceded by original
- * constructor arguments is present on the adapter class. When such a constructor is invoked, the passed ScriptObject's
- * member functions are used to implement and/or override methods on the original class, dispatched by name. A single
- * JavaScript function will act as the implementation for all overloaded methods of the same name. When methods on an
- * adapter instance are invoked, the functions are invoked having the ScriptObject passed in the instance constructor as
- * their "this". Subsequent changes to the ScriptObject (reassignment or removal of its functions) are not reflected in
- * the adapter instance; the method implementations are bound to functions at constructor invocation time.
- * {@code java.lang.Object} methods {@code equals}, {@code hashCode}, and {@code toString} can also be overridden. The
- * only restriction is that since every JavaScript object already has a {@code toString} function through the
- * {@code Object.prototype}, the {@code toString} in the adapter is only overridden if the passed ScriptObject has a
- * {@code toString} function as its own property, and not inherited from a prototype. All other adapter methods can be
- * implemented or overridden through a prototype-inherited function of the ScriptObject passed to the constructor too.
- * </p><p>
- * If the original types collectively have only one abstract method, or have several of them, but all share the
- * same name, an additional constructor is provided for every original constructor; this one takes a ScriptFunction as
- * its last argument preceded by original constructor arguments. This constructor will use the passed function as the
- * implementation for all abstract methods. For consistency, any concrete methods sharing the single abstract method
- * name will also be overridden by the function. When methods on the adapter instance are invoked, the ScriptFunction is
- * invoked with {@code null} as its "this".
- * </p><p>
- * For adapter methods that return values, all the JavaScript-to-Java conversions supported by Nashorn will be in effect
- * to coerce the JavaScript function return value to the expected Java return type.
- * </p><p>
- * Since we are adding a trailing argument to the generated constructors in the adapter class, they will never be
- * declared as variable arity, even if the original constructor in the superclass was declared as variable arity. The
- * reason we are passing the additional argument at the end of the argument list instead at the front is that the
- * source-level script expression <code>new X(a, b) { ... }</code> (which is a proprietary syntax extension Nashorn uses
- * to resemble Java anonymous classes) is actually equivalent to <code>new X(a, b, { ... })</code>.
+ * visibility of all the required types, an error is thrown. The class uses {@link JavaAdapterBytecodeGenerator} to
+ * generate the adapter class itself; see its documentation for details about the generated class.
* </p><p>
* You normally don't use this class directly, but rather either create adapters from script using
* {@link NativeJava#extend(Object, Object...)}, using the {@code new} operator on abstract classes and interfaces (see
@@ -138,72 +67,6 @@ import jdk.nashorn.internal.runtime.Undefined;
*/
public final class JavaAdapterFactory {
- private static final Type SCRIPT_FUNCTION_TYPE = Type.getType(ScriptFunction.class);
- private static final Type SCRIPT_OBJECT_TYPE = Type.getType(ScriptObject.class);
- private static final Type OBJECT_TYPE = Type.getType(Object.class);
- private static final Type STRING_TYPE = Type.getType(String.class);
- private static final Type CONTEXT_TYPE = Type.getType(Context.class);
- private static final Type METHOD_TYPE_TYPE = Type.getType(MethodType.class);
- private static final Type METHOD_HANDLE_TYPE = Type.getType(MethodHandle.class);
- private static final String GET_HANDLE_OBJECT_DESCRIPTOR = Type.getMethodDescriptor(METHOD_HANDLE_TYPE,
- OBJECT_TYPE, STRING_TYPE, METHOD_TYPE_TYPE, Type.BOOLEAN_TYPE);
- private static final String GET_HANDLE_FUNCTION_DESCRIPTOR = Type.getMethodDescriptor(METHOD_HANDLE_TYPE,
- SCRIPT_FUNCTION_TYPE, METHOD_TYPE_TYPE, Type.BOOLEAN_TYPE);
- private static final Type RUNTIME_EXCEPTION_TYPE = Type.getType(RuntimeException.class);
- private static final Type THROWABLE_TYPE = Type.getType(Throwable.class);
- private static final Type PRIVILEGED_ACTION_TYPE = Type.getType(PrivilegedAction.class);
- private static final Type UNSUPPORTED_OPERATION_TYPE = Type.getType(UnsupportedOperationException.class);
-
- private static final String THIS_CLASS_TYPE_NAME = Type.getInternalName(JavaAdapterFactory.class);
- private static final String RUNTIME_EXCEPTION_TYPE_NAME = RUNTIME_EXCEPTION_TYPE.getInternalName();
- private static final String ERROR_TYPE_NAME = Type.getInternalName(Error.class);
- private static final String THROWABLE_TYPE_NAME = THROWABLE_TYPE.getInternalName();
- private static final String CONTEXT_TYPE_NAME = CONTEXT_TYPE.getInternalName();
- private static final String OBJECT_TYPE_NAME = OBJECT_TYPE.getInternalName();
- private static final String PRIVILEGED_ACTION_TYPE_NAME = PRIVILEGED_ACTION_TYPE.getInternalName();
- private static final String UNSUPPORTED_OPERATION_TYPE_NAME = UNSUPPORTED_OPERATION_TYPE.getInternalName();
-
- private static final String METHOD_HANDLE_TYPE_DESCRIPTOR = METHOD_HANDLE_TYPE.getDescriptor();
- private static final String SCRIPT_OBJECT_TYPE_DESCRIPTOR = SCRIPT_OBJECT_TYPE.getDescriptor();
- private static final String GET_GLOBAL_METHOD_DESCRIPTOR = Type.getMethodDescriptor(SCRIPT_OBJECT_TYPE);
- private static final String SET_GLOBAL_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.VOID_TYPE, SCRIPT_OBJECT_TYPE);
- private static final String GET_CLASS_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.getType(Class.class));
- private static final String PRIVILEGED_RUN_METHOD_DESCRIPTOR = Type.getMethodDescriptor(OBJECT_TYPE);
-
- // Package used when the adapter can't be defined in the adaptee's package (either because it's sealed, or because
- // it's a java.* package.
- private static final String ADAPTER_PACKAGE_PREFIX = "jdk/nashorn/internal/javaadapters/";
- // Class name suffix used to append to the adaptee class name, when it can be defined in the adaptee's package.
- private static final String ADAPTER_CLASS_NAME_SUFFIX = "$$NashornJavaAdapter";
- private static final String JAVA_PACKAGE_PREFIX = "java/";
- private static final int MAX_GENERATED_TYPE_NAME_LENGTH = 238; //255 - 17; 17 is the maximum possible length for the global setter inner class suffix
-
- private static final String INIT = "<init>";
- private static final String VOID_NOARG = Type.getMethodDescriptor(Type.VOID_TYPE);
- private static final String GLOBAL_FIELD_NAME = "global";
-
- /**
- * Contains various outcomes for attempting to generate an adapter class. These are stored in AdapterInfo instances.
- * We have a successful outcome (adapter class was generated) and four possible error outcomes: superclass is final,
- * superclass is not public, superclass has no public or protected constructor, more than one superclass was
- * specified. We don't throw exceptions when we try to generate the adapter, but rather just record these error
- * conditions as they are still useful as partial outcomes, as Nashorn's linker can still successfully check whether
- * the class can be autoconverted from a script function even when it is not possible to generate an adapter for it.
- */
- private enum AdaptationOutcome {
- SUCCESS,
- ERROR_FINAL_CLASS,
- ERROR_NON_PUBLIC_CLASS,
- ERROR_NO_ACCESSIBLE_CONSTRUCTOR,
- ERROR_MULTIPLE_SUPERCLASSES,
- ERROR_NO_COMMON_LOADER
- }
-
- /**
- * Collection of methods we never override: Object.clone(), Object.finalize().
- */
- private static final Collection<MethodInfo> EXCLUDED = getExcludedMethods();
-
/**
* A mapping from an original Class object to AdapterInfo representing the adapter for the class it represents.
*/
@@ -214,127 +77,6 @@ public final class JavaAdapterFactory {
}
};
- private static final Random random = new SecureRandom();
- private static final ProtectionDomain GENERATED_PROTECTION_DOMAIN = createGeneratedProtectionDomain();
-
- // This is the superclass for our generated adapter.
- private final Class<?> superClass;
- // Class loader used as the parent for the class loader we'll create to load the generated class. It will be a class
- // loader that has the visibility of all original types (class to extend and interfaces to implement) and of the
- // Nashorn classes.
- private final ClassLoader commonLoader;
-
- // Binary name of the superClass
- private final String superClassName;
- // Binary name of the generated class.
- private final String generatedClassName;
- // Binary name of the PrivilegedAction inner class that is used to
- private final String globalSetterClassName;
- private final Set<String> usedFieldNames = new HashSet<>();
- private final Set<String> abstractMethodNames = new HashSet<>();
- private final String samName;
- private final Set<MethodInfo> finalMethods = new HashSet<>(EXCLUDED);
- private final Set<MethodInfo> methodInfos = new HashSet<>();
- private boolean autoConvertibleFromFunction = false;
-
- private final ClassWriter cw;
-
- /**
- * Creates a factory that will produce the adapter type for the specified original type.
- * @param originalType the type for which this factory will generate the adapter type.
- * @param definingClassAndLoader the class in whose ClassValue we'll store the generated adapter, and its class loader.
- * @throws AdaptationException if the adapter can not be generated for some reason.
- */
- private JavaAdapterFactory(final Class<?> superType, final List<Class<?>> interfaces, final ClassAndLoader definingClassAndLoader) throws AdaptationException {
- assert superType != null && !superType.isInterface();
- assert interfaces != null;
- assert definingClassAndLoader != null;
-
- this.superClass = superType;
- this.commonLoader = findCommonLoader(definingClassAndLoader);
- cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS) {
- @Override
- protected String getCommonSuperClass(final String type1, final String type2) {
- // We need to override ClassWriter.getCommonSuperClass to use this factory's commonLoader as a class
- // loader to find the common superclass of two types when needed.
- return JavaAdapterFactory.this.getCommonSuperClass(type1, type2);
- }
- };
- superClassName = Type.getInternalName(superType);
- generatedClassName = getGeneratedClassName(superType, interfaces);
-
- // Randomize the name of the privileged global setter, to make it non-feasible to find.
- final long l;
- synchronized(random) {
- l = random.nextLong();
- }
- // NOTE: they way this class name is calculated affects the value of MAX_GENERATED_TYPE_NAME_LENGTH constant. If
- // you change the calculation of globalSetterClassName, adjust the constant too.
- globalSetterClassName = generatedClassName.concat("$" + Long.toHexString(l & Long.MAX_VALUE));
- cw.visit(Opcodes.V1_7, ACC_PUBLIC | ACC_SUPER | ACC_FINAL, generatedClassName, null, superClassName, getInternalTypeNames(interfaces));
- cw.visitField(ACC_PRIVATE | ACC_FINAL, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR, null, null).visitEnd();
- usedFieldNames.add(GLOBAL_FIELD_NAME);
-
- gatherMethods(superType);
- gatherMethods(interfaces);
- samName = abstractMethodNames.size() == 1 ? abstractMethodNames.iterator().next() : null;
- generateFields();
- generateConstructors();
- generateMethods();
- // }
- cw.visitEnd();
- }
-
- private static String getGeneratedClassName(final Class<?> superType, final List<Class<?>> interfaces) {
- // The class we use to primarily name our adapter is either the superclass, or if it is Object (meaning we're
- // just implementing interfaces or extending Object), then the first implemented interface or Object.
- final Class<?> namingType = superType == Object.class ? (interfaces.isEmpty()? Object.class : interfaces.get(0)) : superType;
- final Package pkg = namingType.getPackage();
- final String namingTypeName = Type.getInternalName(namingType);
- final StringBuilder buf = new StringBuilder();
- if (namingTypeName.startsWith(JAVA_PACKAGE_PREFIX) || pkg == null || pkg.isSealed()) {
- // Can't define new classes in java.* packages
- buf.append(ADAPTER_PACKAGE_PREFIX).append(namingTypeName);
- } else {
- buf.append(namingTypeName).append(ADAPTER_CLASS_NAME_SUFFIX);
- }
- final Iterator<Class<?>> it = interfaces.iterator();
- if(superType == Object.class && it.hasNext()) {
- it.next(); // Skip first interface, it was used to primarily name the adapter
- }
- // Append interface names to the adapter name
- while(it.hasNext()) {
- buf.append("$$").append(it.next().getSimpleName());
- }
- return buf.toString().substring(0, Math.min(MAX_GENERATED_TYPE_NAME_LENGTH, buf.length()));
- }
-
- /**
- * Given a list of class objects, return an array with their binary names. Used to generate the array of interface
- * names to implement.
- * @param classes the classes
- * @return an array of names
- */
- private static String[] getInternalTypeNames(final List<Class<?>> classes) {
- final int interfaceCount = classes.size();
- final String[] interfaceNames = new String[interfaceCount];
- for(int i = 0; i < interfaceCount; ++i) {
- interfaceNames[i] = Type.getInternalName(classes.get(i));
- }
- return interfaceNames;
- }
-
- /**
- * Utility method used by few other places in the code. Tests if the class has the abstract modifier and is not an
- * array class. For some reason, array classes have the abstract modifier set in HotSpot JVM, and we don't want to
- * treat array classes as abstract.
- * @param clazz the inspected class
- * @return true if the class is abstract and is not an array type.
- */
- static boolean isAbstractClass(final Class<?> clazz) {
- return Modifier.isAbstract(clazz.getModifiers()) && !clazz.isArray();
- }
-
/**
* Returns an adapter class for the specified original types. The adapter class extends/implements the original
* class/interfaces.
@@ -346,55 +88,16 @@ public final class JavaAdapterFactory {
* in the list already implement/extend, or {@code java.lang.Object} in a list of types consisting purely of
* interfaces) will result in a different adapter class, even though those adapter classes are functionally
* identical; we deliberately don't want to incur the additional processing cost of canonicalizing type lists.
+ * @param classOverrides a JavaScript object with functions serving as the class-level overrides and
+ * implementations. These overrides are defined for all instances of the class, and can be further overridden on a
+ * per-instance basis by passing additional objects in the constructor.
* @return an adapter class. See this class' documentation for details on the generated adapter class.
* @throws ECMAException with a TypeError if the adapter class can not be generated because the original class is
* final, non-public, or has no public or protected constructors.
*/
- public static StaticClass getAdapterClassFor(final Class<?>[] types) {
+ public static StaticClass getAdapterClassFor(final Class<?>[] types, ScriptObject classOverrides) {
assert types != null && types.length > 0;
- final AdapterInfo adapterInfo = getAdapterInfo(types);
-
- final StaticClass clazz = adapterInfo.adapterClass;
- if (clazz != null) {
- return clazz;
- }
- adapterInfo.adaptationOutcome.typeError();
-
- throw new AssertionError();
- }
-
- private static AdapterInfo getAdapterInfo(final Class<?>[] types) {
- final ClassAndLoader definingClassAndLoader = getDefiningClassAndLoader(types);
-
- final Map<List<Class<?>>, AdapterInfo> adapterInfoMap = ADAPTER_INFO_MAPS.get(definingClassAndLoader.clazz);
- final List<Class<?>> typeList = types.length == 1 ? getSingletonClassList(types[0]) : Arrays.asList(types.clone());
- AdapterInfo adapterInfo;
- synchronized(adapterInfoMap) {
- adapterInfo = adapterInfoMap.get(typeList);
- if(adapterInfo == null) {
- adapterInfo = createAdapterInfo(types, definingClassAndLoader);
- adapterInfoMap.put(typeList, adapterInfo);
- }
- }
- return adapterInfo;
- }
-
- @SuppressWarnings({ "unchecked", "rawtypes" })
- private static List<Class<?>> getSingletonClassList(final Class<?> clazz) {
- return (List)Collections.singletonList(clazz);
- }
-
- /**
- * Returns whether an instance of the specified class/interface can be generated from a ScriptFunction. Returns true
- * iff: the adapter for the class/interface can be created, it is abstract (this includes interfaces), it has at
- * least one abstract method, all the abstract methods share the same name, and it has a public or protected default
- * constructor. Note that invoking this class will most likely result in the adapter class being defined in the JVM
- * if it hasn't been already.
- * @param clazz the inspected class
- * @return true iff an instance of the specified class/interface can be generated from a ScriptFunction.
- */
- static boolean isAutoConvertibleFromFunction(final Class<?> clazz) {
- return getAdapterInfo(new Class<?>[] { clazz }).autoConvertibleFromFunction;
+ return getAdapterInfo(types).getAdapterClassFor(classOverrides);
}
/**
@@ -410,7 +113,7 @@ public final class JavaAdapterFactory {
* @throws Exception if anything goes wrong
*/
public static MethodHandle getConstructor(final Class<?> sourceType, final Class<?> targetType) throws Exception {
- final StaticClass adapterClass = getAdapterClassFor(new Class<?>[] { targetType });
+ final StaticClass adapterClass = getAdapterClassFor(new Class<?>[] { targetType }, null);
return AccessController.doPrivileged(new PrivilegedExceptionAction<MethodHandle>() {
@Override
public MethodHandle run() throws Exception {
@@ -422,700 +125,46 @@ public final class JavaAdapterFactory {
}
/**
- * Finishes the bytecode generation for the adapter class that was started in the constructor, and loads the
- * bytecode as a new class into the JVM.
- * @return the generated adapter class
- */
- private Class<?> generateClass() {
- final String binaryName = generatedClassName.replace('/', '.');
- try {
- return Class.forName(binaryName, true, createClassLoader(commonLoader, binaryName, cw.toByteArray(),
- globalSetterClassName.replace('/', '.')));
- } catch (final ClassNotFoundException e) {
- throw new AssertionError(e); // cannot happen
- }
- }
-
- /**
* Tells if the given Class is an adapter or support class
* @param clazz Class object
* @return true if the Class given is adapter or support class
*/
public static boolean isAdapterClass(Class<?> clazz) {
- return clazz.getClassLoader() instanceof AdapterLoader;
- }
-
- private static class AdapterLoader extends SecureClassLoader {
- AdapterLoader(ClassLoader parent) {
- super(parent);
- }
- }
-
- // Creation of class loader is in a separate static method so that it doesn't retain a reference to the factory
- // instance. Note that the adapter class is created in the protection domain of the class/interface being
- // extended/implemented, and only the privileged global setter action class is generated in the protection domain
- // of Nashorn itself. Also note that the creation and loading of the global setter is deferred until it is
- // required by JVM linker, which will only happen on first invocation of any of the adapted method. We could defer
- // it even more by separating its invocation into a separate static method on the adapter class, but then someone
- // with ability to introspect on the class and use setAccessible(true) on it could invoke the method. It's a
- // security tradeoff...
- private static ClassLoader createClassLoader(final ClassLoader parentLoader, final String className,
- final byte[] classBytes, final String privilegedActionClassName) {
- return new AdapterLoader(parentLoader) {
- private final ClassLoader myLoader = getClass().getClassLoader();
- private final ProtectionDomain myProtectionDomain = getClass().getProtectionDomain();
-
- @Override
- public Class<?> loadClass(final String name, final boolean resolve) throws ClassNotFoundException {
- try {
- return super.loadClass(name, resolve);
- } catch (final SecurityException se) {
- // we may be implementing an interface or extending a class that was
- // loaded by a loader that prevents package.access. If so, it'd throw
- // SecurityException for nashorn's classes!. For adapter's to work, we
- // should be able to refer to nashorn classes.
- if (name.startsWith("jdk.nashorn.internal.")) {
- return myLoader.loadClass(name);
- }
- throw se;
- }
- }
-
- @Override
- protected Class<?> findClass(final String name) throws ClassNotFoundException {
- if(name.equals(className)) {
- final byte[] bytes = classBytes;
- return defineClass(name, bytes, 0, bytes.length, GENERATED_PROTECTION_DOMAIN);
- } else if(name.equals(privilegedActionClassName)) {
- final byte[] bytes = generatePrivilegedActionClassBytes(privilegedActionClassName.replace('.', '/'));
- return defineClass(name, bytes, 0, bytes.length, myProtectionDomain);
- } else {
- throw new ClassNotFoundException(name);
- }
- }
- };
- }
-
- /**
- * Generates a PrivilegedAction implementation class for invoking {@link Context#setGlobal(ScriptObject)} from the
- * adapter class.
- */
- private static byte[] generatePrivilegedActionClassBytes(final String className) {
- final ClassWriter w = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
- // class GlobalSetter implements PrivilegedAction {
- w.visit(Opcodes.V1_7, ACC_SUPER | ACC_FINAL, className, null, OBJECT_TYPE_NAME, new String[] {
- PRIVILEGED_ACTION_TYPE_NAME
- });
-
- // private final ScriptObject global;
- w.visitField(ACC_PRIVATE | ACC_FINAL, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR, null, null).visitEnd();
-
- // private GlobalSetter(ScriptObject global) {
- InstructionAdapter mv = new InstructionAdapter(w.visitMethod(ACC_PRIVATE, INIT,
- SET_GLOBAL_METHOD_DESCRIPTOR, null, new String[0]));
- mv.visitCode();
- // super();
- mv.visitVarInsn(ALOAD, 0);
- mv.invokespecial(OBJECT_TYPE_NAME, INIT, VOID_NOARG);
- // this.global = global;
- mv.visitVarInsn(ALOAD, 0);
- mv.visitVarInsn(ALOAD, 1);
- mv.putfield(className, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
-
- mv.visitInsn(RETURN);
- mv.visitEnd();
- mv.visitMaxs(0, 0);
-
- // public Object run() {
- mv = new InstructionAdapter(w.visitMethod(ACC_PUBLIC, "run", PRIVILEGED_RUN_METHOD_DESCRIPTOR, null,
- new String[0]));
- mv.visitCode();
- // Context.setGlobal(this.global);
- mv.visitVarInsn(ALOAD, 0);
- mv.getfield(className, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
- mv.invokestatic(CONTEXT_TYPE_NAME, "setGlobal", SET_GLOBAL_METHOD_DESCRIPTOR);
- // return null;
- mv.visitInsn(ACONST_NULL);
- mv.visitInsn(ARETURN);
-
- mv.visitEnd();
- mv.visitMaxs(0, 0);
-
- // static void setGlobal(ScriptObject global) {
- mv = new InstructionAdapter(w.visitMethod(ACC_STATIC, "setGlobal", SET_GLOBAL_METHOD_DESCRIPTOR, null,
- new String[0]));
- mv.visitCode();
- // new GlobalSetter(ScriptObject global)
- mv.anew(Type.getType("L" + className + ";"));
- mv.dup();
- mv.visitVarInsn(ALOAD, 0);
- mv.invokespecial(className, INIT, SET_GLOBAL_METHOD_DESCRIPTOR);
- // AccessController.doPrivileged(...)
- mv.invokestatic(Type.getInternalName(AccessController.class), "doPrivileged", Type.getMethodDescriptor(
- OBJECT_TYPE, PRIVILEGED_ACTION_TYPE));
- mv.pop();
- mv.visitInsn(RETURN);
-
- mv.visitEnd();
- mv.visitMaxs(0, 0);
-
- return w.toByteArray();
- }
-
- private void generateFields() {
- for (final MethodInfo mi: methodInfos) {
- cw.visitField(ACC_PRIVATE | ACC_FINAL, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR, null, null).visitEnd();
- }
- }
-
- private void generateConstructors() throws AdaptationException {
- boolean gotCtor = false;
- for (final Constructor<?> ctor: superClass.getDeclaredConstructors()) {
- final int modifier = ctor.getModifiers();
- if((modifier & (Modifier.PUBLIC | Modifier.PROTECTED)) != 0) {
- generateConstructor(ctor);
- gotCtor = true;
- }
- }
- if(!gotCtor) {
- throw new AdaptationException(AdaptationOutcome.ERROR_NO_ACCESSIBLE_CONSTRUCTOR, superClass.getCanonicalName());
- }
- }
-
- boolean isAutoConvertibleFromFunction() {
- return autoConvertibleFromFunction;
- }
-
- private void generateConstructor(final Constructor<?> ctor) {
- // Generate a constructor that delegates to ctor, but takes an additional ScriptObject parameter at the
- // beginning of its parameter list.
- generateConstructor(ctor, false);
-
- if (samName != null) {
- if (!autoConvertibleFromFunction && ctor.getParameterTypes().length == 0) {
- // If the original type only has a single abstract method name, as well as a default ctor, then it can
- // be automatically converted from JS function.
- autoConvertibleFromFunction = true;
- }
- // If all our abstract methods have a single name, generate an additional constructor, one that takes a
- // ScriptFunction as its first parameter and assigns it as the implementation for all abstract methods.
- generateConstructor(ctor, true);
- }
- }
-
- /**
- * Generates a constructor for the adapter class. This constructor will take the same arguments as the supertype
- * constructor passed as the argument here, and delegate to it. However, it will take an additional argument of
- * either ScriptObject or ScriptFunction type (based on the value of the "fromFunction" parameter), and initialize
- * all the method handle fields of the adapter instance with functions from the script object (or the script
- * function itself, if that's what's passed). There is one method handle field in the adapter class for every method
- * that can be implemented or overridden; the name of every field is same as the name of the method, with a number
- * suffix that makes it unique in case of overloaded methods. The generated constructor will invoke
- * {@link #getHandle(ScriptFunction, MethodType, boolean)} or {@link #getHandle(Object, String, MethodType,
- * boolean)} to obtain the method handles; these methods make sure to add the necessary conversions and arity
- * adjustments so that the resulting method handles can be invoked from generated methods using {@code invokeExact}.
- * The constructor that takes a script function will only initialize the methods with the same name as the single
- * abstract method. The constructor will also store the Nashorn global that was current at the constructor
- * invocation time in a field named "global". The generated constructor will be public, regardless of whether the
- * supertype constructor was public or protected. The generated constructor will not be variable arity, even if the
- * supertype constructor was.
- * @param ctor the supertype constructor that is serving as the base for the generated constructor.
- * @param fromFunction true if we're generating a constructor that initializes SAM types from a single
- * ScriptFunction passed to it, false if we're generating a constructor that initializes an arbitrary type from a
- * ScriptObject passed to it.
- */
- private void generateConstructor(final Constructor<?> ctor, final boolean fromFunction) {
- final Type originalCtorType = Type.getType(ctor);
- final Type[] originalArgTypes = originalCtorType.getArgumentTypes();
- 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;
- newArgTypes[argLen] = extraArgumentType;
- System.arraycopy(originalArgTypes, 0, newArgTypes, 0, argLen);
-
- // All constructors must be public, even if in the superclass they were protected.
- // Existing super constructor <init>(this, args...) triggers generating <init>(this, scriptObj, args...).
- final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_PUBLIC, INIT,
- Type.getMethodDescriptor(originalCtorType.getReturnType(), newArgTypes), null, null));
-
- mv.visitCode();
- // First, invoke super constructor with original arguments. If the form of the constructor we're generating is
- // <init>(this, args..., scriptFn), then we're invoking super.<init>(this, args...).
- 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 < argLen; ++i) {
- final Type argType = Type.getType(argTypes[i]);
- mv.load(offset, argType);
- offset += argType.getSize();
- }
- mv.invokespecial(superClassName, INIT, originalCtorType.getDescriptor());
-
- // Get a descriptor to the appropriate "JavaAdapterFactory.getHandle" method.
- final String getHandleDescriptor = fromFunction ? GET_HANDLE_FUNCTION_DESCRIPTOR : GET_HANDLE_OBJECT_DESCRIPTOR;
-
- // Assign MethodHandle fields through invoking getHandle()
- for (final MethodInfo mi : methodInfos) {
- mv.visitVarInsn(ALOAD, 0);
- if (fromFunction && !mi.getName().equals(samName)) {
- // Constructors initializing from a ScriptFunction only initialize methods with the SAM name.
- // NOTE: if there's a concrete overloaded method sharing the SAM name, it'll be overriden too. This
- // is a deliberate design choice. All other method handles are initialized to null.
- mv.visitInsn(ACONST_NULL);
- } else {
- mv.visitVarInsn(ALOAD, offset);
- if(!fromFunction) {
- mv.aconst(mi.getName());
- }
- mv.aconst(Type.getMethodType(mi.type.toMethodDescriptorString()));
- mv.iconst(mi.method.isVarArgs() ? 1 : 0);
- mv.invokestatic(THIS_CLASS_TYPE_NAME, "getHandle", getHandleDescriptor);
- }
- mv.putfield(generatedClassName, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR);
- }
-
- // Assign "this.global = Context.getGlobal()"
- mv.visitVarInsn(ALOAD, 0);
- invokeGetGlobal(mv);
- mv.dup();
- mv.invokevirtual(OBJECT_TYPE_NAME, "getClass", GET_CLASS_METHOD_DESCRIPTOR); // check against null Context
- mv.pop();
- mv.putfield(generatedClassName, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
-
- // Wrap up
- mv.visitInsn(RETURN);
- mv.visitMaxs(0, 0);
- mv.visitEnd();
- }
-
- private static void invokeGetGlobal(final InstructionAdapter mv) {
- mv.invokestatic(CONTEXT_TYPE_NAME, "getGlobal", GET_GLOBAL_METHOD_DESCRIPTOR);
- }
-
- private void invokeSetGlobal(final InstructionAdapter mv) {
- mv.invokestatic(globalSetterClassName, "setGlobal", SET_GLOBAL_METHOD_DESCRIPTOR);
- }
-
- /**
- * Given a JS script function, binds it to null JS "this", and adapts its parameter types, return types, and arity
- * to the specified type and arity. This method is public mainly for implementation reasons, so the adapter classes
- * can invoke it from their constructors that take a ScriptFunction in its first argument to obtain the method
- * handles for their abstract method implementations.
- * @param fn the script function
- * @param type the method type it has to conform to
- * @param varArg if the Java method for which the function is being adapted is a variable arity method
- * @return the appropriately adapted method handle for invoking the script function.
- */
- public static MethodHandle getHandle(final ScriptFunction fn, final MethodType type, final boolean varArg) {
- // JS "this" will be null for SAMs
- return adaptHandle(fn.getBoundInvokeHandle(null), type, varArg);
- }
-
- /**
- * Given a JS script object, retrieves a function from it by name, binds it to the script object as its "this", and
- * adapts its parameter types, return types, and arity to the specified type and arity. This method is public mainly
- * for implementation reasons, so the adapter classes can invoke it from their constructors that take a Object
- * in its first argument to obtain the method handles for their method implementations.
- * @param obj the script obj
- * @param name the name of the property that contains the function
- * @param type the method type it has to conform to
- * @param varArg if the Java method for which the function is being adapted is a variable arity method
- * @return the appropriately adapted method handle for invoking the script function, or null if the value of the
- * property is either null or undefined, or "toString" was requested as the name, but the object doesn't directly
- * define it but just inherits it through prototype.
- */
- public static MethodHandle getHandle(final Object obj, final String name, final MethodType type, final boolean varArg) {
- if (! (obj instanceof ScriptObject)) {
- throw typeError("not.an.object", ScriptRuntime.safeToString(obj));
- }
-
- final ScriptObject sobj = (ScriptObject)obj;
- // Since every JS Object has a toString, we only override "String toString()" it if it's explicitly specified
- if ("toString".equals(name) && !sobj.hasOwnProperty("toString")) {
- return null;
- }
-
- final Object fnObj = sobj.get(name);
- if (fnObj instanceof ScriptFunction) {
- return adaptHandle(((ScriptFunction)fnObj).getBoundInvokeHandle(sobj), type, varArg);
- } else if(fnObj == null || fnObj instanceof Undefined) {
- return null;
- } else {
- throw typeError("not.a.function", name);
- }
- }
-
- private static MethodHandle adaptHandle(final MethodHandle handle, final MethodType type, final boolean varArg) {
- return Bootstrap.getLinkerServices().asType(ScriptObject.pairArguments(handle, type, varArg), type);
- }
-
- /**
- * Encapsulation of the information used to generate methods in the adapter classes. Basically, a wrapper around the
- * reflective Method object, a cached MethodType, and the name of the field in the adapter class that will hold the
- * method handle serving as the implementation of this method in adapter instances.
- *
- */
- private static class MethodInfo {
- private final Method method;
- private final MethodType type;
- private String methodHandleFieldName;
-
- private MethodInfo(final Class<?> clazz, final String name, final Class<?>... argTypes) throws NoSuchMethodException {
- this(clazz.getDeclaredMethod(name, argTypes));
- }
-
- private MethodInfo(final Method method) {
- this.method = method;
- this.type = MH.type(method.getReturnType(), method.getParameterTypes());
- }
-
- @Override
- public boolean equals(final Object obj) {
- return obj instanceof MethodInfo && equals((MethodInfo)obj);
- }
-
- private boolean equals(final MethodInfo other) {
- // Only method name and type are used for comparison; method handle field name is not.
- return getName().equals(other.getName()) && type.equals(other.type);
- }
-
- String getName() {
- return method.getName();
- }
-
- @Override
- public int hashCode() {
- return getName().hashCode() ^ type.hashCode();
- }
-
- void setIsCanonical(final Set<String> usedFieldNames) {
- int i = 0;
- String fieldName = getName();
- while(!usedFieldNames.add(fieldName)) {
- fieldName = getName() + (i++);
- }
- methodHandleFieldName = fieldName;
- }
- }
-
- private void generateMethods() {
- for(final MethodInfo mi: methodInfos) {
- generateMethod(mi);
- }
+ return JavaAdapterClassLoader.isAdapterClass(clazz);
}
/**
- * Generates a method in the adapter class that adapts a method from the original class. The generated methods will
- * inspect the method handle field assigned to them. If it is null (the JS object doesn't provide an implementation
- * for the method) then it will either invoke its version in the supertype, or if it is abstract, throw an
- * {@link UnsupportedOperationException}. Otherwise, if the method handle field's value is not null, the handle is
- * invoked using invokeExact (signature polymorphic invocation as per JLS 15.12.3). Before the invocation, the
- * current Nashorn {@link Context} is checked, and if it is different than the global used to create the adapter
- * instance, the creating global is set to be the current global. In this case, the previously current global is
- * restored after the invocation. If invokeExact results in a Throwable that is not one of the method's declared
- * exceptions, and is not an unchecked throwable, then it is wrapped into a {@link RuntimeException} and the runtime
- * exception is thrown. The method handle retrieved from the field is guaranteed to exactly match the signature of
- * the method; this is guaranteed by the way constructors of the adapter class obtain them using
- * {@link #getHandle(Object, String, MethodType, boolean)}.
- * @param mi the method info describing the method to be generated.
- */
- private void generateMethod(final MethodInfo mi) {
- final Method method = mi.method;
- final int mod = method.getModifiers();
- final int access = ACC_PUBLIC | (method.isVarArgs() ? ACC_VARARGS : 0);
- final Class<?>[] exceptions = method.getExceptionTypes();
- final String[] exceptionNames = new String[exceptions.length];
- for (int i = 0; i < exceptions.length; ++i) {
- exceptionNames[i] = Type.getInternalName(exceptions[i]);
- }
- final MethodType type = mi.type;
- final String methodDesc = type.toMethodDescriptorString();
- final String name = mi.getName();
-
- final Type asmType = Type.getMethodType(methodDesc);
- final Type[] asmArgTypes = asmType.getArgumentTypes();
-
- // Determine the first index for a local variable
- int nextLocalVar = 1; // this
- for(final Type t: asmArgTypes) {
- nextLocalVar += t.getSize();
- }
-
- final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(access, name, methodDesc, null,
- exceptionNames));
- mv.visitCode();
-
- final Label methodHandleNotNull = new Label();
- final Label methodEnd = new Label();
-
- final Type returnType = Type.getType(type.returnType());
-
- // Get the method handle
- mv.visitVarInsn(ALOAD, 0);
- mv.getfield(generatedClassName, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR);
- mv.visitInsn(DUP); // It'll remain on the stack all the way until the invocation
- // Check if the method handle is null
- mv.visitJumpInsn(IFNONNULL, methodHandleNotNull);
- if(Modifier.isAbstract(mod)) {
- // If it's null, and the method is abstract, throw an exception
- mv.anew(UNSUPPORTED_OPERATION_TYPE);
- mv.dup();
- mv.invokespecial(UNSUPPORTED_OPERATION_TYPE_NAME, INIT, VOID_NOARG);
- mv.athrow();
- } else {
- // If it's null, and the method is not abstract, delegate to super method.
- mv.visitVarInsn(ALOAD, 0);
- int nextParam = 1;
- for(final Type t: asmArgTypes) {
- mv.load(nextParam, t);
- nextParam += t.getSize();
- }
- mv.invokespecial(superClassName, name, methodDesc);
- mv.areturn(returnType);
- }
-
- mv.visitLabel(methodHandleNotNull);
- final int currentGlobalVar = nextLocalVar++;
- final int globalsDifferVar = nextLocalVar++;
-
- // Emit code for switching to the creating global
- // ScriptObject currentGlobal = Context.getGlobal();
- invokeGetGlobal(mv);
- mv.dup();
- mv.visitVarInsn(ASTORE, currentGlobalVar);
- // if(this.global == currentGlobal) {
- loadGlobalOnStack(mv);
- final Label globalsDiffer = new Label();
- mv.ifacmpne(globalsDiffer);
- // globalsDiffer = false
- mv.iconst(0); // false
- final Label proceed = new Label();
- mv.goTo(proceed);
- mv.visitLabel(globalsDiffer);
- // } else {
- // Context.setGlobal(this.global);
- loadGlobalOnStack(mv);
- invokeSetGlobal(mv);
- // globalsDiffer = true
- mv.iconst(1);
-
- mv.visitLabel(proceed);
- mv.visitVarInsn(ISTORE, globalsDifferVar);
-
- // Load all parameters back on stack for dynamic invocation.
- int varOffset = 1;
- for (final Type t : asmArgTypes) {
- mv.load(varOffset, t);
- varOffset += t.getSize();
- }
-
- // Invoke the target method handle
- final Label tryBlockStart = new Label();
- mv.visitLabel(tryBlockStart);
- mv.invokevirtual(METHOD_HANDLE_TYPE.getInternalName(), "invokeExact", type.toMethodDescriptorString());
- final Label tryBlockEnd = new Label();
- mv.visitLabel(tryBlockEnd);
- emitFinally(mv, currentGlobalVar, globalsDifferVar);
- mv.areturn(returnType);
-
- // If Throwable is not declared, we need an adapter from Throwable to RuntimeException
- final boolean throwableDeclared = isThrowableDeclared(exceptions);
- final Label throwableHandler;
- if (!throwableDeclared) {
- // Add "throw new RuntimeException(Throwable)" handler for Throwable
- throwableHandler = new Label();
- mv.visitLabel(throwableHandler);
- mv.anew(RUNTIME_EXCEPTION_TYPE);
- mv.dupX1();
- mv.swap();
- mv.invokespecial(RUNTIME_EXCEPTION_TYPE_NAME, INIT, Type.getMethodDescriptor(Type.VOID_TYPE, THROWABLE_TYPE));
- // Fall through to rethrow handler
- } else {
- throwableHandler = null;
- }
- final Label rethrowHandler = new Label();
- mv.visitLabel(rethrowHandler);
- // Rethrow handler for RuntimeException, Error, and all declared exception types
- emitFinally(mv, currentGlobalVar, globalsDifferVar);
- mv.athrow();
- mv.visitLabel(methodEnd);
-
- mv.visitLocalVariable("currentGlobal", SCRIPT_OBJECT_TYPE_DESCRIPTOR, null, methodHandleNotNull, methodEnd, currentGlobalVar);
- mv.visitLocalVariable("globalsDiffer", Type.INT_TYPE.getDescriptor(), null, methodHandleNotNull, methodEnd, globalsDifferVar);
-
- if(throwableDeclared) {
- mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, THROWABLE_TYPE_NAME);
- assert throwableHandler == null;
- } else {
- mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, RUNTIME_EXCEPTION_TYPE_NAME);
- mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, ERROR_TYPE_NAME);
- for(final String excName: exceptionNames) {
- mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, excName);
- }
- mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, throwableHandler, THROWABLE_TYPE_NAME);
- }
- mv.visitMaxs(0, 0);
- mv.visitEnd();
- }
-
- /**
- * Emit code to restore the previous Nashorn Context when needed.
- * @param mv the instruction adapter
- * @param currentGlobalVar index of the local variable holding the reference to the current global at method
- * entry.
- * @param globalsDifferVar index of the boolean local variable that is true if the global needs to be restored.
- */
- private void emitFinally(final InstructionAdapter mv, final int currentGlobalVar, final int globalsDifferVar) {
- // Emit code to restore the previous Nashorn global if needed
- mv.visitVarInsn(ILOAD, globalsDifferVar);
- final Label skip = new Label();
- mv.ifeq(skip);
- mv.visitVarInsn(ALOAD, currentGlobalVar);
- invokeSetGlobal(mv);
- mv.visitLabel(skip);
- }
-
- private void loadGlobalOnStack(final InstructionAdapter mv) {
- mv.visitVarInsn(ALOAD, 0);
- mv.getfield(generatedClassName, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
- }
-
- private static boolean isThrowableDeclared(final Class<?>[] exceptions) {
- for (final Class<?> exception : exceptions) {
- if (exception == Throwable.class) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Gathers methods that can be implemented or overridden from the specified type into this factory's
- * {@link #methodInfos} set. It will add all non-final, non-static methods that are either public or protected from
- * the type if the type itself is public. If the type is a class, the method will recursively invoke itself for its
- * superclass and the interfaces it implements, and add further methods that were not directly declared on the
- * class.
- * @param type the type defining the methods.
+ * Returns whether an instance of the specified class/interface can be generated from a ScriptFunction. Returns true
+ * iff: the adapter for the class/interface can be created, it is abstract (this includes interfaces), it has at
+ * least one abstract method, all the abstract methods share the same name, and it has a public or protected default
+ * constructor. Note that invoking this class will most likely result in the adapter class being defined in the JVM
+ * if it hasn't been already.
+ * @param clazz the inspected class
+ * @return true iff an instance of the specified class/interface can be generated from a ScriptFunction.
*/
- private void gatherMethods(final Class<?> type) {
- if (Modifier.isPublic(type.getModifiers())) {
- final Method[] typeMethods = type.isInterface() ? type.getMethods() : type.getDeclaredMethods();
-
- for (final Method typeMethod: typeMethods) {
- final int m = typeMethod.getModifiers();
- if (Modifier.isStatic(m)) {
- continue;
- }
- if (Modifier.isPublic(m) || Modifier.isProtected(m)) {
- final MethodInfo mi = new MethodInfo(typeMethod);
- if (Modifier.isFinal(m)) {
- finalMethods.add(mi);
- } else if (!finalMethods.contains(mi) && methodInfos.add(mi)) {
- if (Modifier.isAbstract(m)) {
- abstractMethodNames.add(mi.getName());
- }
- mi.setIsCanonical(usedFieldNames);
- }
- }
- }
- }
- // If the type is a class, visit its superclasses and declared interfaces. If it's an interface, we're done.
- // Needing to invoke the method recursively for a non-interface Class object is the consequence of needing to
- // see all declared protected methods, and Class.getDeclaredMethods() doesn't provide those declared in a
- // superclass. For interfaces, we used Class.getMethods(), as we're only interested in public ones there, and
- // getMethods() does provide those declared in a superinterface.
- if (!type.isInterface()) {
- final Class<?> superType = type.getSuperclass();
- if (superType != null) {
- gatherMethods(superType);
- }
- for (final Class<?> itf: type.getInterfaces()) {
- gatherMethods(itf);
- }
- }
+ static boolean isAutoConvertibleFromFunction(final Class<?> clazz) {
+ return getAdapterInfo(new Class<?>[] { clazz }).autoConvertibleFromFunction;
}
- private void gatherMethods(final List<Class<?>> classes) {
- for(final Class<?> c: classes) {
- gatherMethods(c);
- }
- }
+ private static AdapterInfo getAdapterInfo(final Class<?>[] types) {
+ final ClassAndLoader definingClassAndLoader = ClassAndLoader.getDefiningClassAndLoader(types);
- /**
- * Creates a collection of methods that are not final, but we still never allow them to be overridden in adapters,
- * as explicitly declaring them automatically is a bad idea. Currently, this means {@code Object.finalize()} and
- * {@code Object.clone()}.
- * @return a collection of method infos representing those methods that we never override in adapter classes.
- */
- private static Collection<MethodInfo> getExcludedMethods() {
- return AccessController.doPrivileged(new PrivilegedAction<Collection<MethodInfo>>() {
- @Override
- public Collection<MethodInfo> run() {
- try {
- return Arrays.asList(
- new MethodInfo(Object.class, "finalize"),
- new MethodInfo(Object.class, "clone"));
- } catch (final NoSuchMethodException e) {
- throw new AssertionError(e);
- }
+ final Map<List<Class<?>>, AdapterInfo> adapterInfoMap = ADAPTER_INFO_MAPS.get(definingClassAndLoader.getRepresentativeClass());
+ final List<Class<?>> typeList = types.length == 1 ? getSingletonClassList(types[0]) : Arrays.asList(types.clone());
+ AdapterInfo adapterInfo;
+ synchronized(adapterInfoMap) {
+ adapterInfo = adapterInfoMap.get(typeList);
+ if(adapterInfo == null) {
+ adapterInfo = createAdapterInfo(types, definingClassAndLoader);
+ adapterInfoMap.put(typeList, adapterInfo);
}
- });
- }
-
- private static ProtectionDomain createGeneratedProtectionDomain() {
- // Generated classes need to have AllPermission. Since we require the "createClassLoader" RuntimePermission, we
- // can create a class loader that'll load new classes with any permissions. Our generated classes are just
- // delegating adapters, so having AllPermission can't cause anything wrong; the effective set of permissions for
- // the executing script functions will still be limited by the permissions of the caller and the permissions of
- // the script.
- final Permissions permissions = new Permissions();
- permissions.add(new AllPermission());
- return new ProtectionDomain(new CodeSource(null, (CodeSigner[])null), permissions);
- }
-
- private static class AdapterInfo {
- final StaticClass adapterClass;
- final boolean autoConvertibleFromFunction;
- final AnnotatedAdaptationOutcome adaptationOutcome;
-
- AdapterInfo(final StaticClass adapterClass, final boolean autoConvertibleFromFunction) {
- this.adapterClass = adapterClass;
- this.autoConvertibleFromFunction = autoConvertibleFromFunction;
- this.adaptationOutcome = AnnotatedAdaptationOutcome.SUCCESS;
- }
-
- AdapterInfo(final AdaptationOutcome outcome, final String classList) {
- this(new AnnotatedAdaptationOutcome(outcome, classList));
- }
-
- AdapterInfo(final AnnotatedAdaptationOutcome adaptationOutcome) {
- this.adapterClass = null;
- this.autoConvertibleFromFunction = false;
- this.adaptationOutcome = adaptationOutcome;
}
+ return adapterInfo;
}
- /**
- * An adaptation outcome accompanied with a name of a class (or a list of multiple class names) that are the reason
- * an adapter could not be generated.
- */
- private static class AnnotatedAdaptationOutcome {
- static final AnnotatedAdaptationOutcome SUCCESS = new AnnotatedAdaptationOutcome(AdaptationOutcome.SUCCESS, "");
-
- private final AdaptationOutcome adaptationOutcome;
- private final String classList;
-
- AnnotatedAdaptationOutcome(final AdaptationOutcome adaptationOutcome, final String classList) {
- this.adaptationOutcome = adaptationOutcome;
- this.classList = classList;
- }
-
- void typeError() {
- assert adaptationOutcome != AdaptationOutcome.SUCCESS;
- throw ECMAErrors.typeError("extend." + adaptationOutcome, classList);
- }
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ private static List<Class<?>> getSingletonClassList(final Class<?> clazz) {
+ return (List)Collections.singletonList(clazz);
}
/**
@@ -1130,17 +179,17 @@ public final class JavaAdapterFactory {
final int mod = t.getModifiers();
if(!t.isInterface()) {
if(superClass != null) {
- return new AdapterInfo(AdaptationOutcome.ERROR_MULTIPLE_SUPERCLASSES, t.getCanonicalName() + " and " + superClass.getCanonicalName());
+ return new AdapterInfo(AdaptationResult.Outcome.ERROR_MULTIPLE_SUPERCLASSES, t.getCanonicalName() + " and " + superClass.getCanonicalName());
}
if (Modifier.isFinal(mod)) {
- return new AdapterInfo(AdaptationOutcome.ERROR_FINAL_CLASS, t.getCanonicalName());
+ return new AdapterInfo(AdaptationResult.Outcome.ERROR_FINAL_CLASS, t.getCanonicalName());
}
superClass = t;
} else {
interfaces.add(t);
}
if(!Modifier.isPublic(mod)) {
- return new AdapterInfo(AdaptationOutcome.ERROR_NON_PUBLIC_CLASS, t.getCanonicalName());
+ return new AdapterInfo(AdaptationResult.Outcome.ERROR_NON_PUBLIC_CLASS, t.getCanonicalName());
}
}
final Class<?> effectiveSuperClass = superClass == null ? Object.class : superClass;
@@ -1148,211 +197,78 @@ public final class JavaAdapterFactory {
@Override
public AdapterInfo run() {
try {
- final JavaAdapterFactory factory = new JavaAdapterFactory(effectiveSuperClass, interfaces, definingClassAndLoader);
- return new AdapterInfo(StaticClass.forClass(factory.generateClass()),
- factory.isAutoConvertibleFromFunction());
+ return new AdapterInfo(effectiveSuperClass, interfaces, definingClassAndLoader);
} catch (final AdaptationException e) {
- return new AdapterInfo(e.outcome);
+ return new AdapterInfo(e.getAdaptationResult());
}
}
});
}
- @SuppressWarnings("serial")
- private static class AdaptationException extends Exception {
- private final AnnotatedAdaptationOutcome outcome;
- AdaptationException(final AdaptationOutcome outcome, final String classList) {
- this.outcome = new AnnotatedAdaptationOutcome(outcome, classList);
- }
- }
-
- private String getCommonSuperClass(final String type1, final String type2) {
- try {
- final Class<?> c1 = Class.forName(type1.replace('/', '.'), false, commonLoader);
- final Class<?> c2 = Class.forName(type2.replace('/', '.'), false, commonLoader);
- if (c1.isAssignableFrom(c2)) {
- return type1;
- }
- if (c2.isAssignableFrom(c1)) {
- return type2;
- }
- if (c1.isInterface() || c2.isInterface()) {
- return "java/lang/Object";
- }
- return assignableSuperClass(c1, c2).getName().replace('.', '/');
- } catch(final ClassNotFoundException e) {
- throw new RuntimeException(e);
- }
- }
-
- private static Class<?> assignableSuperClass(final Class<?> c1, final Class<?> c2) {
- final Class<?> superClass = c1.getSuperclass();
- return superClass.isAssignableFrom(c2) ? superClass : assignableSuperClass(superClass, c2);
- }
+ private static class AdapterInfo {
+ private static final ClassAndLoader SCRIPT_OBJECT_LOADER = new ClassAndLoader(ScriptObject.class, true);
- /**
- * Choose between the passed class loader and the class loader that defines the ScriptObject class, based on which
- * of the two can see the classes in both.
- * @param classAndLoader the loader and a representative class from it that will be used to add the generated
- * adapter to its ADAPTER_INFO_MAPS.
- * @return the class loader that sees both the specified class and Nashorn classes.
- * @throws IllegalStateException if no such class loader is found.
- */
- private static ClassLoader findCommonLoader(final ClassAndLoader classAndLoader) throws AdaptationException {
- final ClassLoader loader = classAndLoader.getLoader();
- if (canSeeClass(loader, ScriptObject.class)) {
- return loader;
- }
+ private final ClassLoader commonLoader;
+ private final JavaAdapterClassLoader adapterGenerator;
+ // Cacheable adapter class that is shared by all adapter instances that don't have class overrides, only
+ // instance overrides.
+ final StaticClass instanceAdapterClass;
+ final boolean autoConvertibleFromFunction;
+ final AdaptationResult adaptationResult;
- final ClassLoader nashornLoader = ScriptObject.class.getClassLoader();
- if(canSeeClass(nashornLoader, classAndLoader.clazz)) {
- return nashornLoader;
+ AdapterInfo(Class<?> superClass, List<Class<?>> interfaces, ClassAndLoader definingLoader) throws AdaptationException {
+ this.commonLoader = findCommonLoader(definingLoader);
+ final JavaAdapterBytecodeGenerator gen = new JavaAdapterBytecodeGenerator(superClass, interfaces, commonLoader, false);
+ this.autoConvertibleFromFunction = gen.isAutoConvertibleFromFunction();
+ this.instanceAdapterClass = gen.createAdapterClassLoader().generateClass(commonLoader);
+ this.adapterGenerator = new JavaAdapterBytecodeGenerator(superClass, interfaces, commonLoader, true).createAdapterClassLoader();
+ this.adaptationResult = AdaptationResult.SUCCESSFUL_RESULT;
}
- throw new AdaptationException(AdaptationOutcome.ERROR_NO_COMMON_LOADER, classAndLoader.clazz.getCanonicalName());
- }
-
- private static boolean canSeeClass(final ClassLoader cl, final Class<?> clazz) {
- try {
- return Class.forName(clazz.getName(), false, cl) == clazz;
- } catch (final ClassNotFoundException e) {
- return false;
+ AdapterInfo(final AdaptationResult.Outcome outcome, final String classList) {
+ this(new AdaptationResult(outcome, classList));
}
- }
- /**
- * Given a list of types that define the superclass/interfaces for an adapter class, returns a single type from the
- * list that will be used to attach the adapter to its ClassValue. The first type in the array that is defined in a
- * class loader that can also see all other types is returned. If there is no such loader, an exception is thrown.
- * @param types the input types
- * @return the first type from the array that is defined in a class loader that can also see all other types.
- */
- private static ClassAndLoader getDefiningClassAndLoader(final Class<?>[] types) {
- // Short circuit the cheap case
- if(types.length == 1) {
- return new ClassAndLoader(types[0], false);
+ AdapterInfo(final AdaptationResult adaptationResult) {
+ this.commonLoader = null;
+ this.adapterGenerator = null;
+ this.instanceAdapterClass = null;
+ this.autoConvertibleFromFunction = false;
+ this.adaptationResult = adaptationResult;
}
- return AccessController.doPrivileged(new PrivilegedAction<ClassAndLoader>() {
- @Override
- public ClassAndLoader run() {
- return getDefiningClassAndLoaderPrivileged(types);
+ StaticClass getAdapterClassFor(ScriptObject classOverrides) {
+ if(adaptationResult.getOutcome() != AdaptationResult.Outcome.SUCCESS) {
+ throw adaptationResult.typeError();
}
- });
- }
-
- private static ClassAndLoader getDefiningClassAndLoaderPrivileged(final Class<?>[] types) {
- final Collection<ClassAndLoader> maximumVisibilityLoaders = getMaximumVisibilityLoaders(types);
-
- final Iterator<ClassAndLoader> it = maximumVisibilityLoaders.iterator();
- if(maximumVisibilityLoaders.size() == 1) {
- // Fortunate case - single maximally specific class loader; return its representative class.
- return it.next();
- }
-
- // Ambiguity; throw an error.
- assert maximumVisibilityLoaders.size() > 1; // basically, can't be zero
- final StringBuilder b = new StringBuilder();
- b.append(it.next().clazz.getCanonicalName());
- while(it.hasNext()) {
- b.append(", ").append(it.next().clazz.getCanonicalName());
- }
- throw typeError("extend.ambiguous.defining.class", b.toString());
- }
-
- /**
- * Given an array of types, return a subset of their class loaders that are maximal according to the
- * "can see other loaders' classes" relation, which is presumed to be a partial ordering.
- * @param types types
- * @return a collection of maximum visibility class loaders. It is guaranteed to have at least one element.
- */
- private static Collection<ClassAndLoader> getMaximumVisibilityLoaders(final Class<?>[] types) {
- final List<ClassAndLoader> maximumVisibilityLoaders = new LinkedList<>();
- outer: for(final ClassAndLoader maxCandidate: getClassLoadersForTypes(types)) {
- final Iterator<ClassAndLoader> it = maximumVisibilityLoaders.iterator();
- while(it.hasNext()) {
- final ClassAndLoader existingMax = it.next();
- final boolean candidateSeesExisting = canSeeClass(maxCandidate.getRetrievedLoader(), existingMax.clazz);
- final boolean exitingSeesCandidate = canSeeClass(existingMax.getRetrievedLoader(), maxCandidate.clazz);
- if(candidateSeesExisting) {
- if(!exitingSeesCandidate) {
- // The candidate sees the the existing maximum, so drop the existing one as it's no longer maximal.
- it.remove();
- }
- // NOTE: there's also the anomalous case where both loaders see each other. Not sure what to do
- // about that one, as two distinct class loaders both seeing each other's classes is weird and
- // violates the assumption that the relation "sees others' classes" is a partial ordering. We'll
- // just not do anything, and treat them as incomparable; hopefully some later class loader that
- // comes along can eliminate both of them, if it can not, we'll end up with ambiguity anyway and
- // throw an error at the end.
- } else if(exitingSeesCandidate) {
- // Existing sees the candidate, so drop the candidate.
- continue outer;
- }
+ if(classOverrides == null) {
+ return instanceAdapterClass;
}
- // If we get here, no existing maximum visibility loader could see the candidate, so the candidate is a new
- // maximum.
- maximumVisibilityLoaders.add(maxCandidate);
- }
- return maximumVisibilityLoaders;
- }
-
- private static Collection<ClassAndLoader> getClassLoadersForTypes(final Class<?>[] types) {
- final Map<ClassAndLoader, ClassAndLoader> classesAndLoaders = new LinkedHashMap<>();
- for(final Class<?> c: types) {
- final ClassAndLoader cl = new ClassAndLoader(c, true);
- if(!classesAndLoaders.containsKey(cl)) {
- classesAndLoaders.put(cl, cl);
+ JavaAdapterServices.setClassOverrides(classOverrides);
+ try {
+ return adapterGenerator.generateClass(commonLoader);
+ } finally {
+ JavaAdapterServices.setClassOverrides(null);
}
}
- return classesAndLoaders.keySet();
- }
- /**
- * A tuple of a class loader and a single class representative of the classes that can be loaded through it. Its
- * equals/hashCode is defined in terms of the identity of the class loader.
- */
- private static final class ClassAndLoader {
- private final Class<?> clazz;
- // Don't access this directly; most of the time, use getRetrievedLoader(), or if you know what you're doing,
- // getLoader().
- private ClassLoader loader;
- // We have mild affinity against eagerly retrieving the loader, as we need to do it in a privileged block. For
- // the most basic case of looking up an already-generated adapter info for a single type, we avoid it.
- private boolean loaderRetrieved;
-
- ClassAndLoader(final Class<?> clazz, final boolean retrieveLoader) {
- this.clazz = clazz;
- if(retrieveLoader) {
- retrieveLoader();
+ /**
+ * Choose between the passed class loader and the class loader that defines the ScriptObject class, based on which
+ * of the two can see the classes in both.
+ * @param classAndLoader the loader and a representative class from it that will be used to add the generated
+ * adapter to its ADAPTER_INFO_MAPS.
+ * @return the class loader that sees both the specified class and Nashorn classes.
+ * @throws IllegalStateException if no such class loader is found.
+ */
+ private static ClassLoader findCommonLoader(final ClassAndLoader classAndLoader) throws AdaptationException {
+ if(classAndLoader.canSee(SCRIPT_OBJECT_LOADER)) {
+ return classAndLoader.getLoader();
}
- }
-
- ClassLoader getLoader() {
- if(!loaderRetrieved) {
- retrieveLoader();
+ if (SCRIPT_OBJECT_LOADER.canSee(classAndLoader)) {
+ return SCRIPT_OBJECT_LOADER.getLoader();
}
- return getRetrievedLoader();
- }
-
- ClassLoader getRetrievedLoader() {
- assert loaderRetrieved;
- return loader;
- }
-
- private void retrieveLoader() {
- loader = clazz.getClassLoader();
- loaderRetrieved = true;
- }
- @Override
- public boolean equals(final Object obj) {
- return obj instanceof ClassAndLoader && ((ClassAndLoader)obj).getRetrievedLoader() == getRetrievedLoader();
- }
-
- @Override
- public int hashCode() {
- return System.identityHashCode(getRetrievedLoader());
+ throw new AdaptationException(AdaptationResult.Outcome.ERROR_NO_COMMON_LOADER, classAndLoader.getRepresentativeClass().getCanonicalName());
}
}
}
diff --git a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterGeneratorBase.java b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterGeneratorBase.java
new file mode 100644
index 00000000..67499cbc
--- /dev/null
+++ b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterGeneratorBase.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.runtime.linker;
+
+import jdk.internal.org.objectweb.asm.Type;
+import jdk.nashorn.internal.runtime.Context;
+import jdk.nashorn.internal.runtime.ScriptObject;
+
+/**
+ * Base class for both {@link JavaAdapterBytecodeGenerator} and {@link JavaAdapterClassLoader}, containing those
+ * bytecode types, type names and method descriptor that are used by both.
+ */
+abstract class JavaAdapterGeneratorBase {
+ static final Type CONTEXT_TYPE = Type.getType(Context.class);
+ static final Type OBJECT_TYPE = Type.getType(Object.class);
+ static final Type SCRIPT_OBJECT_TYPE = Type.getType(ScriptObject.class);
+
+ static final String CONTEXT_TYPE_NAME = CONTEXT_TYPE.getInternalName();
+ static final String OBJECT_TYPE_NAME = OBJECT_TYPE.getInternalName();
+
+ static final String INIT = "<init>";
+
+ static final String GLOBAL_FIELD_NAME = "global";
+
+ static final String SCRIPT_OBJECT_TYPE_DESCRIPTOR = SCRIPT_OBJECT_TYPE.getDescriptor();
+
+ static final String SET_GLOBAL_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.VOID_TYPE, SCRIPT_OBJECT_TYPE);
+ static final String VOID_NOARG_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.VOID_TYPE);
+
+ protected JavaAdapterGeneratorBase() {
+ }
+}
diff --git a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterServices.java b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterServices.java
new file mode 100644
index 00000000..eb890b9b
--- /dev/null
+++ b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterServices.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.runtime.linker;
+
+import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodType;
+import jdk.nashorn.internal.runtime.ScriptFunction;
+import jdk.nashorn.internal.runtime.ScriptObject;
+import jdk.nashorn.internal.runtime.ScriptRuntime;
+import jdk.nashorn.internal.runtime.Undefined;
+
+/**
+ * Provides static utility services to generated Java adapter classes.
+ */
+public class JavaAdapterServices {
+ private static final ThreadLocal<ScriptObject> classOverrides = new ThreadLocal<>();
+
+ private JavaAdapterServices() {
+ }
+
+ /**
+ * Given a JS script function, binds it to null JS "this", and adapts its parameter types, return types, and arity
+ * to the specified type and arity. This method is public mainly for implementation reasons, so the adapter classes
+ * can invoke it from their constructors that take a ScriptFunction in its first argument to obtain the method
+ * handles for their abstract method implementations.
+ * @param fn the script function
+ * @param type the method type it has to conform to
+ * @return the appropriately adapted method handle for invoking the script function.
+ */
+ public static MethodHandle getHandle(final ScriptFunction fn, final MethodType type) {
+ // JS "this" will be null for SAMs
+ return adaptHandle(fn.getBoundInvokeHandle(null), type);
+ }
+
+ /**
+ * Given a JS script object, retrieves a function from it by name, binds it to the script object as its "this", and
+ * adapts its parameter types, return types, and arity to the specified type and arity. This method is public mainly
+ * for implementation reasons, so the adapter classes can invoke it from their constructors that take a Object
+ * in its first argument to obtain the method handles for their method implementations.
+ * @param obj the script obj
+ * @param name the name of the property that contains the function
+ * @param type the method type it has to conform to
+ * @return the appropriately adapted method handle for invoking the script function, or null if the value of the
+ * property is either null or undefined, or "toString" was requested as the name, but the object doesn't directly
+ * define it but just inherits it through prototype.
+ */
+ public static MethodHandle getHandle(final Object obj, final String name, final MethodType type) {
+ if (! (obj instanceof ScriptObject)) {
+ throw typeError("not.an.object", ScriptRuntime.safeToString(obj));
+ }
+
+ final ScriptObject sobj = (ScriptObject)obj;
+ // Since every JS Object has a toString, we only override "String toString()" it if it's explicitly specified
+ if ("toString".equals(name) && !sobj.hasOwnProperty("toString")) {
+ return null;
+ }
+
+ final Object fnObj = sobj.get(name);
+ if (fnObj instanceof ScriptFunction) {
+ return adaptHandle(((ScriptFunction)fnObj).getBoundInvokeHandle(sobj), type);
+ } else if(fnObj == null || fnObj instanceof Undefined) {
+ return null;
+ } else {
+ throw typeError("not.a.function", name);
+ }
+ }
+
+ /**
+ * Returns a thread-local JS object used to define methods for the adapter class being initialized on the current
+ * thread. This method is public solely for implementation reasons, so the adapter classes can invoke it from their
+ * static initializers.
+ * @return the thread-local JS object used to define methods for the class being initialized.
+ */
+ public static ScriptObject getClassOverrides() {
+ final ScriptObject overrides = classOverrides.get();
+ assert overrides != null;
+ return overrides;
+ }
+
+ static void setClassOverrides(ScriptObject overrides) {
+ classOverrides.set(overrides);
+ }
+
+ private static MethodHandle adaptHandle(final MethodHandle handle, final MethodType type) {
+ return Bootstrap.getLinkerServices().asType(ScriptObject.pairArguments(handle, type, false), type);
+ }
+}
diff --git a/src/jdk/nashorn/internal/runtime/linker/NashornCallSiteDescriptor.java b/src/jdk/nashorn/internal/runtime/linker/NashornCallSiteDescriptor.java
index a2513912..8cb360c4 100644
--- a/src/jdk/nashorn/internal/runtime/linker/NashornCallSiteDescriptor.java
+++ b/src/jdk/nashorn/internal/runtime/linker/NashornCallSiteDescriptor.java
@@ -43,9 +43,8 @@ public class NashornCallSiteDescriptor extends AbstractCallSiteDescriptor {
public static final int CALLSITE_SCOPE = 0x01;
/** Flags that the call site is in code that uses ECMAScript strict mode. */
public static final int CALLSITE_STRICT = 0x02;
- /** Flags that a property getter or setter call site references a scope variable that is not in the global scope
- * (it is in a function lexical scope), and the function's scope object class is fixed and known in advance. Such
- * getters and setters can often be linked more optimally using these assumptions. */
+ /** Flags that a property getter or setter call site references a scope variable that is located at a known distance
+ * in the scope chain. Such getters and setters can often be linked more optimally using these assumptions. */
public static final int CALLSITE_FAST_SCOPE = 0x400;
/** Flags that the call site is profiled; Contexts that have {@code "profile.callsites"} boolean property set emit
diff --git a/src/jdk/nashorn/internal/runtime/linker/NashornLinker.java b/src/jdk/nashorn/internal/runtime/linker/NashornLinker.java
index 3a4a5d77..e7cc78b0 100644
--- a/src/jdk/nashorn/internal/runtime/linker/NashornLinker.java
+++ b/src/jdk/nashorn/internal/runtime/linker/NashornLinker.java
@@ -29,6 +29,7 @@ import static jdk.nashorn.internal.lookup.Lookup.MH;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
+import java.lang.reflect.Modifier;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.linker.ConversionComparator;
import jdk.internal.dynalink.linker.GuardedInvocation;
@@ -131,10 +132,22 @@ final class NashornLinker implements TypeBasedGuardingDynamicLinker, GuardingTyp
}
private static boolean isAutoConvertibleFromFunction(final Class<?> clazz) {
- return JavaAdapterFactory.isAbstractClass(clazz) && !ScriptObject.class.isAssignableFrom(clazz) &&
+ return isAbstractClass(clazz) && !ScriptObject.class.isAssignableFrom(clazz) &&
JavaAdapterFactory.isAutoConvertibleFromFunction(clazz);
}
+ /**
+ * Utility method used by few other places in the code. Tests if the class has the abstract modifier and is not an
+ * array class. For some reason, array classes have the abstract modifier set in HotSpot JVM, and we don't want to
+ * treat array classes as abstract.
+ * @param clazz the inspected class
+ * @return true if the class is abstract and is not an array type.
+ */
+ static boolean isAbstractClass(final Class<?> clazz) {
+ return Modifier.isAbstract(clazz.getModifiers()) && !clazz.isArray();
+ }
+
+
@Override
public Comparison compareConversion(final Class<?> sourceType, final Class<?> targetType1, final Class<?> targetType2) {
if(ScriptObject.class.isAssignableFrom(sourceType)) {
diff --git a/src/jdk/nashorn/internal/runtime/linker/NashornStaticClassLinker.java b/src/jdk/nashorn/internal/runtime/linker/NashornStaticClassLinker.java
index 738162d5..67c1d8db 100644
--- a/src/jdk/nashorn/internal/runtime/linker/NashornStaticClassLinker.java
+++ b/src/jdk/nashorn/internal/runtime/linker/NashornStaticClassLinker.java
@@ -68,10 +68,10 @@ final class NashornStaticClassLinker implements TypeBasedGuardingDynamicLinker {
if ("new".equals(desc.getNameToken(CallSiteDescriptor.OPERATOR))) {
final Class<?> receiverClass = ((StaticClass) self).getRepresentedClass();
// Is the class abstract? (This includes interfaces.)
- if (JavaAdapterFactory.isAbstractClass(receiverClass)) {
+ if (NashornLinker.isAbstractClass(receiverClass)) {
// Change this link request into a link request on the adapter class.
final Object[] args = request.getArguments();
- args[0] = JavaAdapterFactory.getAdapterClassFor(new Class<?>[] { receiverClass });
+ args[0] = JavaAdapterFactory.getAdapterClassFor(new Class<?>[] { receiverClass }, null);
final LinkRequest adapterRequest = request.replaceArguments(request.getCallSiteDescriptor(), args);
final GuardedInvocation gi = checkNullConstructor(delegate(linkerServices, adapterRequest), receiverClass);
// Finally, modify the guard to test for the original abstract class.
diff --git a/src/jdk/nashorn/internal/runtime/options/Options.java b/src/jdk/nashorn/internal/runtime/options/Options.java
index 3e09fa57..16f3bc0f 100644
--- a/src/jdk/nashorn/internal/runtime/options/Options.java
+++ b/src/jdk/nashorn/internal/runtime/options/Options.java
@@ -243,7 +243,13 @@ public final class Options {
*/
public String getString(final String key) {
final Option<?> option = get(key);
- return option != null ? (String)option.getValue() : null;
+ if(option != null) {
+ final String value = (String)option.getValue();
+ if(value != null) {
+ return value.intern();
+ }
+ }
+ return null;
}
/**
diff --git a/src/jdk/nashorn/internal/runtime/regexp/RegExpFactory.java b/src/jdk/nashorn/internal/runtime/regexp/RegExpFactory.java
index 6ff66f21..4a7ac11b 100644
--- a/src/jdk/nashorn/internal/runtime/regexp/RegExpFactory.java
+++ b/src/jdk/nashorn/internal/runtime/regexp/RegExpFactory.java
@@ -40,7 +40,7 @@ public class RegExpFactory {
private final static String JONI = "joni";
static {
- final String impl = Options.getStringProperty("nashorn.regexp.impl", JDK);
+ final String impl = Options.getStringProperty("nashorn.regexp.impl", JONI);
switch (impl) {
case JONI:
instance = new JoniRegExp.Factory();
diff --git a/src/jdk/nashorn/internal/runtime/regexp/RegExpScanner.java b/src/jdk/nashorn/internal/runtime/regexp/RegExpScanner.java
index e8c60c4a..e651c44e 100644
--- a/src/jdk/nashorn/internal/runtime/regexp/RegExpScanner.java
+++ b/src/jdk/nashorn/internal/runtime/regexp/RegExpScanner.java
@@ -26,11 +26,10 @@
package jdk.nashorn.internal.runtime.regexp;
import java.util.HashMap;
-import java.util.LinkedHashSet;
+import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import java.util.regex.PatternSyntaxException;
import jdk.nashorn.internal.parser.Lexer;
@@ -48,9 +47,6 @@ final class RegExpScanner extends Scanner {
*/
private final StringBuilder sb;
- /** Is this the special case of a regexp that never matches anything */
- private boolean neverMatches;
-
/** Expected token table */
private final Map<Character, Integer> expected = new HashMap<>();
@@ -58,7 +54,7 @@ final class RegExpScanner extends Scanner {
private final List<Capture> caps = new LinkedList<>();
/** Forward references to capturing parenthesis to be resolved later.*/
- private final Set<Integer> forwardReferences = new LinkedHashSet<>();
+ private final LinkedList<Integer> forwardReferences = new LinkedList<>();
/** Current level of zero-width negative lookahead assertions. */
private int negativeLookaheadLevel;
@@ -100,14 +96,17 @@ final class RegExpScanner extends Scanner {
}
private void processForwardReferences() {
- if (neverMatches()) {
- return;
- }
- for (final Integer ref : forwardReferences) {
- if (ref.intValue() > caps.size()) {
- neverMatches = true;
- break;
+ Iterator<Integer> iterator = forwardReferences.descendingIterator();
+ while (iterator.hasNext()) {
+ final int pos = iterator.next();
+ final int num = iterator.next();
+ if (num > caps.size()) {
+ // Non-existing backreference. If the number begins with a valid octal convert it to
+ // Unicode escape and append the rest to a literal character sequence.
+ final StringBuilder buffer = new StringBuilder();
+ octalOrLiteral(Integer.toString(num), buffer);
+ sb.insert(pos, buffer);
}
}
@@ -131,9 +130,6 @@ final class RegExpScanner extends Scanner {
}
scanner.processForwardReferences();
- if (scanner.neverMatches()) {
- return null; // never matches
- }
// Throw syntax error unless we parsed the entire JavaScript regexp without syntax errors
if (scanner.position != string.length()) {
@@ -142,16 +138,6 @@ final class RegExpScanner extends Scanner {
}
return scanner;
- }
-
- /**
- * Does this regexp ever match anything? Use of e.g. [], which is legal in JavaScript,
- * is an example where we never match
- *
- * @return boolean
- */
- private boolean neverMatches() {
- return neverMatches;
}
final StringBuilder getStringBuilder() {
@@ -273,23 +259,16 @@ final class RegExpScanner extends Scanner {
}
if (atom()) {
- boolean emptyCharacterClass = false;
+ // Check for character classes that never or always match
if (sb.toString().endsWith("[]")) {
- emptyCharacterClass = true;
+ sb.setLength(sb.length() - 1);
+ sb.append("^\\s\\S]");
} else if (sb.toString().endsWith("[^]")) {
sb.setLength(sb.length() - 2);
sb.append("\\s\\S]");
}
- boolean quantifier = quantifier();
-
- if (emptyCharacterClass) {
- if (!quantifier) {
- neverMatches = true; //never matches ever.
- }
- // Note: we could check if quantifier has min zero to mark empty character class as dead.
- }
-
+ quantifier();
return true;
}
@@ -402,6 +381,10 @@ final class RegExpScanner extends Scanner {
if (ch0 == '}') {
pop('}');
commit(1);
+ } else {
+ // Bad quantifier should be rejected but is accepted by all major engines
+ restart(startIn, startOut);
+ return false;
}
return true;
@@ -613,13 +596,14 @@ final class RegExpScanner extends Scanner {
* ABCDEFGHIJKLMNOPQRSTUVWXYZ
*/
private boolean controlLetter() {
- final char c = Character.toUpperCase(ch0);
- if (c >= 'A' && c <= 'Z') {
+ // To match other engines we also accept '0'..'9' and '_' as control letters inside a character class.
+ if ((ch0 >= 'A' && ch0 <= 'Z') || (ch0 >= 'a' && ch0 <= 'z')
+ || (inCharClass && (isDecimalDigit(ch0) || ch0 == '_'))) {
// for some reason java regexps don't like control characters on the
// form "\\ca".match([string with ascii 1 at char0]). Translating
// them to unicode does it though.
sb.setLength(sb.length() - 1);
- unicode(c - 'A' + 1);
+ unicode(ch0 % 32, sb);
skip(1);
return true;
}
@@ -637,7 +621,9 @@ final class RegExpScanner extends Scanner {
throw new RuntimeException("\\ at end of pattern"); // will be converted to PatternSyntaxException
}
// ES 5.1 A.7 requires "not IdentifierPart" here but all major engines accept any character here.
- if (NON_IDENT_ESCAPES.indexOf(ch0) == -1) {
+ if (ch0 == 'c') {
+ sb.append('\\'); // Treat invalid \c control sequence as \\c
+ } else if (NON_IDENT_ESCAPES.indexOf(ch0) == -1) {
sb.setLength(sb.length() - 1);
}
return commit(1);
@@ -651,7 +637,7 @@ final class RegExpScanner extends Scanner {
final int startIn = position;
final int startOut = sb.length();
- if (ch0 == '0' && !isDecimalDigit(ch1)) {
+ if (ch0 == '0' && !isOctalDigit(ch1)) {
skip(1);
// DecimalEscape :: 0. If i is zero, return the EscapeValue consisting of a <NUL> character (Unicodevalue0000);
sb.append("\u0000");
@@ -659,49 +645,56 @@ final class RegExpScanner extends Scanner {
}
if (isDecimalDigit(ch0)) {
- final int num = ch0 - '0';
- // Single digit escape, treat as backreference.
- if (!isDecimalDigit(ch1)) {
- if (num <= caps.size() && caps.get(num - 1).getNegativeLookaheadLevel() > 0) {
- // Captures that live inside a negative lookahead are dead after the
- // lookahead and will be undefined if referenced from outside.
- if (caps.get(num - 1).getNegativeLookaheadLevel() > negativeLookaheadLevel) {
- sb.setLength(sb.length() - 1);
- } else {
- sb.append(ch0);
+ if (ch0 == '0') {
+ // We know this is an octal escape.
+ if (inCharClass) {
+ // Convert octal escape to unicode escape if inside character class.
+ int octalValue = 0;
+ while (isOctalDigit(ch0)) {
+ octalValue = octalValue * 8 + ch0 - '0';
+ skip(1);
}
- skip(1);
- return true;
- } else if (num > caps.size()) {
- // Forward reference to a capture group. Forward references are always undefined so we
- // can omit it from the output buffer. Additionally, if the capture group does not exist
- // the whole regexp becomes invalid, so register the reference for later processing.
- forwardReferences.add(num);
- sb.setLength(sb.length() - 1);
- skip(1);
- return true;
- }
- }
- if (inCharClass) {
- // Convert octal escape to unicode escape if inside character class.
- StringBuilder digit = new StringBuilder(4);
+ unicode(octalValue, sb);
+
+ } else {
+ // Copy decimal escape as-is
+ decimalDigits();
+ }
+ } else {
+ // This should be a backreference, but could also be an octal escape or even a literal string.
+ int decimalValue = 0;
while (isDecimalDigit(ch0)) {
- digit.append(ch0);
+ decimalValue = decimalValue * 10 + ch0 - '0';
skip(1);
}
- int value = Integer.parseInt(digit.toString(), 8); //throws exception that leads to SyntaxError if not octal
- if (value > 0xff) {
- throw new NumberFormatException(digit.toString());
- }
+ if (inCharClass) {
+ // No backreferences in character classes. Encode as unicode escape or literal char sequence
+ sb.setLength(sb.length() - 1);
+ octalOrLiteral(Integer.toString(decimalValue), sb);
- unicode(value);
+ } else if (decimalValue <= caps.size() && caps.get(decimalValue - 1).getNegativeLookaheadLevel() > 0) {
+ // Captures that live inside a negative lookahead are dead after the
+ // lookahead and will be undefined if referenced from outside.
+ if (caps.get(decimalValue - 1).getNegativeLookaheadLevel() > negativeLookaheadLevel) {
+ sb.setLength(sb.length() - 1);
+ } else {
+ sb.append(decimalValue);
+ }
+ } else if (decimalValue > caps.size()) {
+ // Forward reference to a capture group. Forward references are always undefined so we can omit
+ // it from the output buffer. However, if the target capture does not exist, we need to rewrite
+ // the reference as hex escape or literal string, so register the reference for later processing.
+ sb.setLength(sb.length() - 1);
+ forwardReferences.add(decimalValue);
+ forwardReferences.add(sb.length());
+ } else {
+ // Append as backreference
+ sb.append(decimalValue);
+ }
- } else {
- // Copy decimal escape as-is
- decimalDigits();
}
return true;
}
@@ -881,7 +874,6 @@ final class RegExpScanner extends Scanner {
switch (ch0) {
case ']':
case '-':
- case '\0':
return false;
case '[':
@@ -942,13 +934,41 @@ final class RegExpScanner extends Scanner {
return true;
}
- private void unicode(final int value) {
+ private void unicode(final int value, final StringBuilder buffer) {
final String hex = Integer.toHexString(value);
- sb.append('u');
+ buffer.append('u');
for (int i = 0; i < 4 - hex.length(); i++) {
- sb.append('0');
+ buffer.append('0');
}
- sb.append(hex);
+ buffer.append(hex);
+ }
+
+ // Convert what would have been a backreference into a unicode escape, or a number literal, or both.
+ private void octalOrLiteral(final String numberLiteral, final StringBuilder buffer) {
+ final int length = numberLiteral.length();
+ int octalValue = 0;
+ int pos = 0;
+ // Maximum value for octal escape is 0377 (255) so we stop the loop at 32
+ while (pos < length && octalValue < 0x20) {
+ final char ch = numberLiteral.charAt(pos);
+ if (isOctalDigit(ch)) {
+ octalValue = octalValue * 8 + ch - '0';
+ } else {
+ break;
+ }
+ pos++;
+ }
+ if (octalValue > 0) {
+ buffer.append('\\');
+ unicode(octalValue, buffer);
+ buffer.append(numberLiteral.substring(pos));
+ } else {
+ buffer.append(numberLiteral);
+ }
+ }
+
+ private static boolean isOctalDigit(final char ch) {
+ return ch >= '0' && ch <= '7';
}
private static boolean isDecimalDigit(final char ch) {
diff --git a/src/jdk/nashorn/internal/runtime/regexp/joni/Analyser.java b/src/jdk/nashorn/internal/runtime/regexp/joni/Analyser.java
index 65d3fcd3..49a2a925 100644
--- a/src/jdk/nashorn/internal/runtime/regexp/joni/Analyser.java
+++ b/src/jdk/nashorn/internal/runtime/regexp/joni/Analyser.java
@@ -156,9 +156,6 @@ final class Analyser extends Parser {
env.memNodes = null;
- new ArrayCompiler(this).compile();
- //new AsmCompiler(this).compile();
-
if (regex.numRepeat != 0 || regex.btMemEnd != 0) {
regex.stackPopLevel = StackPopLevel.ALL;
} else {
diff --git a/src/jdk/nashorn/internal/runtime/regexp/joni/Parser.java b/src/jdk/nashorn/internal/runtime/regexp/joni/Parser.java
index 1b5a5861..13d569b7 100644
--- a/src/jdk/nashorn/internal/runtime/regexp/joni/Parser.java
+++ b/src/jdk/nashorn/internal/runtime/regexp/joni/Parser.java
@@ -287,7 +287,10 @@ class Parser extends Lexer {
if (syntax.allowDoubleRangeOpInCC()) {
env.ccEscWarn("-");
- parseCharClassSbChar(cc, arg); // goto sb_char /* [0-9-a] is allowed as [0-9\-a] */
+ arg.inType = CCVALTYPE.SB;
+ arg.v = '-';
+ arg.vIsRaw = false;
+ parseCharClassValEntry2(cc, arg); // goto val_entry2 /* [0-9-a] is allowed as [0-9\-a] */
break;
}
newSyntaxException(ERR_UNMATCHED_RANGE_SPECIFIER_IN_CHAR_CLASS);
diff --git a/src/jdk/nashorn/internal/runtime/regexp/joni/Regex.java b/src/jdk/nashorn/internal/runtime/regexp/joni/Regex.java
index ae98cb56..da5b9827 100644
--- a/src/jdk/nashorn/internal/runtime/regexp/joni/Regex.java
+++ b/src/jdk/nashorn/internal/runtime/regexp/joni/Regex.java
@@ -55,8 +55,9 @@ public final class Regex implements RegexState {
int[]repeatRangeLo;
int[]repeatRangeHi;
- public WarnCallback warnings;
- public MatcherFactory factory;
+ WarnCallback warnings;
+ MatcherFactory factory;
+ private Analyser analyser;
int options;
int userOptions;
@@ -140,19 +141,33 @@ public final class Regex implements RegexState {
this.caseFoldFlag = caseFoldFlag;
this.warnings = warnings;
- new Analyser(new ScanEnvironment(this, syntax), chars, p, end).compile();
+ this.analyser = new Analyser(new ScanEnvironment(this, syntax), chars, p, end);
+ this.analyser.compile();
this.warnings = null;
}
+ public void compile() {
+ if (factory == null && analyser != null) {
+ Compiler compiler = new ArrayCompiler(analyser);
+ analyser = null; // only do this once
+ compiler.compile();
+ }
+ }
+
public Matcher matcher(char[] chars) {
return matcher(chars, 0, chars.length);
}
public Matcher matcher(char[] chars, int p, int end) {
+ compile();
return factory.create(this, chars, p, end);
}
+ public WarnCallback getWarnings() {
+ return warnings;
+ }
+
public int numberOfCaptures() {
return numMem;
}
diff --git a/src/jdk/nashorn/internal/runtime/regexp/joni/ast/QuantifierNode.java b/src/jdk/nashorn/internal/runtime/regexp/joni/ast/QuantifierNode.java
index 76a5868d..26cde985 100644
--- a/src/jdk/nashorn/internal/runtime/regexp/joni/ast/QuantifierNode.java
+++ b/src/jdk/nashorn/internal/runtime/regexp/joni/ast/QuantifierNode.java
@@ -231,12 +231,12 @@ public final class QuantifierNode extends StateNode {
break;
case DEL:
- env.reg.warnings.warn(new String(chars, p, end) +
+ env.reg.getWarnings().warn(new String(chars, p, end) +
" redundant nested repeat operator");
break;
default:
- env.reg.warnings.warn(new String(chars, p, end) +
+ env.reg.getWarnings().warn(new String(chars, p, end) +
" nested repeat operator " + Reduce.PopularQStr[targetQNum] +
" and " + Reduce.PopularQStr[nestQNum] + " was replaced with '" +
Reduce.ReduceQStr[Reduce.REDUCE_TABLE[targetQNum][nestQNum].ordinal()] + "'");
diff --git a/src/jdk/nashorn/internal/runtime/resources/Messages.properties b/src/jdk/nashorn/internal/runtime/resources/Messages.properties
index 43a7ff62..9f327521 100644
--- a/src/jdk/nashorn/internal/runtime/resources/Messages.properties
+++ b/src/jdk/nashorn/internal/runtime/resources/Messages.properties
@@ -113,6 +113,7 @@ type.error.cant.convert.to.java.string=Cannot convert object of type {0} to a Ja
type.error.cant.convert.to.java.number=Cannot convert object of type {0} to a Java argument of number type
type.error.cant.convert.to.javascript.array=Can only convert Java arrays and lists to JavaScript arrays. Can't convert object of type {0}.
type.error.extend.expects.at.least.one.argument=Java.extend needs at least one argument.
+type.error.extend.expects.at.least.one.type.argument=Java.extend needs at least one type argument.
type.error.extend.expects.java.types=Java.extend needs Java types as its arguments.
type.error.extend.ambiguous.defining.class=There is no class loader that can see all of {0} at once.
type.error.extend.ERROR_FINAL_CLASS=Can not extend final class {0}.
diff --git a/src/jdk/nashorn/internal/runtime/resources/Options.properties b/src/jdk/nashorn/internal/runtime/resources/Options.properties
index e63f7a37..2b52e8b9 100644
--- a/src/jdk/nashorn/internal/runtime/resources/Options.properties
+++ b/src/jdk/nashorn/internal/runtime/resources/Options.properties
@@ -89,7 +89,8 @@ nashorn.option.class.cache.size ={ \
short_name="--ccs", \
desc="Size of the Class cache size per global scope.", \
is_undocumented=true, \
- type=Integer \
+ type=Integer, \
+ default=50 \
}
nashorn.option.classpath ={ \
@@ -101,7 +102,7 @@ nashorn.option.classpath ={ \
}
nashorn.option.compile.only = { \
- name="--compile-only", \
+ name="--compile-only", \
short_name="-co", \
is_undocumented=true, \
desc="Compile without running.", \
@@ -117,10 +118,10 @@ nashorn.option.d = { \
type=String \
}
-nashorn.option.doe = { \
- name="-dump-on-error", \
- short_name="-doe", \
- desc="Dump a stack trace on errors."\
+nashorn.option.doe = { \
+ name="-dump-on-error", \
+ short_name="-doe", \
+ desc="Dump a stack trace on errors." \
}
nashorn.option.empty.statements = { \
@@ -143,6 +144,26 @@ nashorn.option.fullversion = { \
desc="Print full version info of Nashorn." \
}
+nashorn.option.function.statement.error= { \
+ name="--function-statement-error", \
+ desc="Report an error when function declaration is used as a statement.", \
+ is_undocumented=true, \
+ default=false \
+}
+
+nashorn.option.function.statement.warning = { \
+ name="--function-statement-warning", \
+ desc="Warn when function declaration is used as a statement.", \
+ is_undocumented=true, \
+ default=false \
+}
+
+nashorn.option.fx = { \
+ name="-fx", \
+ desc="Launch script as an fx application.", \
+ default=false \
+}
+
nashorn.option.log = { \
name="--log", \
is_undocumented=true, \
@@ -196,7 +217,7 @@ nashorn.option.package = { \
}
nashorn.option.parse.only = { \
- name="--parse-only", \
+ name="--parse-only", \
is_undocumented=true, \
desc="Parse without compiling." \
}
diff --git a/src/jdk/nashorn/internal/runtime/resources/fx/base.js b/src/jdk/nashorn/internal/runtime/resources/fx/base.js
new file mode 100644
index 00000000..00559129
--- /dev/null
+++ b/src/jdk/nashorn/internal/runtime/resources/fx/base.js
@@ -0,0 +1,226 @@
+/*
+ * 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.
+ */
+
+Scene = Java.type("javafx.scene.Scene");
+Group = Java.type("javafx.scene.Group");
+Stage = Java.type("javafx.stage.Stage");
+
+Binding = Java.type("javafx.beans.binding.Binding");
+Bindings = Java.type("javafx.beans.binding.Bindings");
+BooleanBinding = Java.type("javafx.beans.binding.BooleanBinding");
+BooleanExpression = Java.type("javafx.beans.binding.BooleanExpression");
+DoubleBinding = Java.type("javafx.beans.binding.DoubleBinding");
+DoubleExpression = Java.type("javafx.beans.binding.DoubleExpression");
+FloatBinding = Java.type("javafx.beans.binding.FloatBinding");
+FloatExpression = Java.type("javafx.beans.binding.FloatExpression");
+IntegerBinding = Java.type("javafx.beans.binding.IntegerBinding");
+IntegerExpression = Java.type("javafx.beans.binding.IntegerExpression");
+ListBinding = Java.type("javafx.beans.binding.ListBinding");
+ListExpression = Java.type("javafx.beans.binding.ListExpression");
+LongBinding = Java.type("javafx.beans.binding.LongBinding");
+LongExpression = Java.type("javafx.beans.binding.LongExpression");
+MapBinding = Java.type("javafx.beans.binding.MapBinding");
+MapExpression = Java.type("javafx.beans.binding.MapExpression");
+NumberBinding = Java.type("javafx.beans.binding.NumberBinding");
+NumberExpression = Java.type("javafx.beans.binding.NumberExpression");
+NumberExpressionBase = Java.type("javafx.beans.binding.NumberExpressionBase");
+ObjectBinding = Java.type("javafx.beans.binding.ObjectBinding");
+ObjectExpression = Java.type("javafx.beans.binding.ObjectExpression");
+SetBinding = Java.type("javafx.beans.binding.SetBinding");
+SetExpression = Java.type("javafx.beans.binding.SetExpression");
+StringBinding = Java.type("javafx.beans.binding.StringBinding");
+StringExpression = Java.type("javafx.beans.binding.StringExpression");
+When = Java.type("javafx.beans.binding.When");
+DefaultProperty = Java.type("javafx.beans.DefaultProperty");
+InvalidationListener = Java.type("javafx.beans.InvalidationListener");
+Observable = Java.type("javafx.beans.Observable");
+JavaBeanBooleanProperty = Java.type("javafx.beans.property.adapter.JavaBeanBooleanProperty");
+JavaBeanBooleanPropertyBuilder = Java.type("javafx.beans.property.adapter.JavaBeanBooleanPropertyBuilder");
+JavaBeanDoubleProperty = Java.type("javafx.beans.property.adapter.JavaBeanDoubleProperty");
+JavaBeanDoublePropertyBuilder = Java.type("javafx.beans.property.adapter.JavaBeanDoublePropertyBuilder");
+JavaBeanFloatProperty = Java.type("javafx.beans.property.adapter.JavaBeanFloatProperty");
+JavaBeanFloatPropertyBuilder = Java.type("javafx.beans.property.adapter.JavaBeanFloatPropertyBuilder");
+JavaBeanIntegerProperty = Java.type("javafx.beans.property.adapter.JavaBeanIntegerProperty");
+JavaBeanIntegerPropertyBuilder = Java.type("javafx.beans.property.adapter.JavaBeanIntegerPropertyBuilder");
+JavaBeanLongProperty = Java.type("javafx.beans.property.adapter.JavaBeanLongProperty");
+JavaBeanLongPropertyBuilder = Java.type("javafx.beans.property.adapter.JavaBeanLongPropertyBuilder");
+JavaBeanObjectProperty = Java.type("javafx.beans.property.adapter.JavaBeanObjectProperty");
+JavaBeanObjectPropertyBuilder = Java.type("javafx.beans.property.adapter.JavaBeanObjectPropertyBuilder");
+JavaBeanProperty = Java.type("javafx.beans.property.adapter.JavaBeanProperty");
+JavaBeanStringProperty = Java.type("javafx.beans.property.adapter.JavaBeanStringProperty");
+JavaBeanStringPropertyBuilder = Java.type("javafx.beans.property.adapter.JavaBeanStringPropertyBuilder");
+ReadOnlyJavaBeanBooleanProperty = Java.type("javafx.beans.property.adapter.ReadOnlyJavaBeanBooleanProperty");
+ReadOnlyJavaBeanBooleanPropertyBuilder = Java.type("javafx.beans.property.adapter.ReadOnlyJavaBeanBooleanPropertyBuilder");
+ReadOnlyJavaBeanDoubleProperty = Java.type("javafx.beans.property.adapter.ReadOnlyJavaBeanDoubleProperty");
+ReadOnlyJavaBeanDoublePropertyBuilder = Java.type("javafx.beans.property.adapter.ReadOnlyJavaBeanDoublePropertyBuilder");
+ReadOnlyJavaBeanFloatProperty = Java.type("javafx.beans.property.adapter.ReadOnlyJavaBeanFloatProperty");
+ReadOnlyJavaBeanFloatPropertyBuilder = Java.type("javafx.beans.property.adapter.ReadOnlyJavaBeanFloatPropertyBuilder");
+ReadOnlyJavaBeanIntegerProperty = Java.type("javafx.beans.property.adapter.ReadOnlyJavaBeanIntegerProperty");
+ReadOnlyJavaBeanIntegerPropertyBuilder = Java.type("javafx.beans.property.adapter.ReadOnlyJavaBeanIntegerPropertyBuilder");
+ReadOnlyJavaBeanLongProperty = Java.type("javafx.beans.property.adapter.ReadOnlyJavaBeanLongProperty");
+ReadOnlyJavaBeanLongPropertyBuilder = Java.type("javafx.beans.property.adapter.ReadOnlyJavaBeanLongPropertyBuilder");
+ReadOnlyJavaBeanObjectProperty = Java.type("javafx.beans.property.adapter.ReadOnlyJavaBeanObjectProperty");
+ReadOnlyJavaBeanObjectPropertyBuilder = Java.type("javafx.beans.property.adapter.ReadOnlyJavaBeanObjectPropertyBuilder");
+ReadOnlyJavaBeanProperty = Java.type("javafx.beans.property.adapter.ReadOnlyJavaBeanProperty");
+ReadOnlyJavaBeanStringProperty = Java.type("javafx.beans.property.adapter.ReadOnlyJavaBeanStringProperty");
+ReadOnlyJavaBeanStringPropertyBuilder = Java.type("javafx.beans.property.adapter.ReadOnlyJavaBeanStringPropertyBuilder");
+BooleanProperty = Java.type("javafx.beans.property.BooleanProperty");
+BooleanPropertyBase = Java.type("javafx.beans.property.BooleanPropertyBase");
+DoubleProperty = Java.type("javafx.beans.property.DoubleProperty");
+DoublePropertyBase = Java.type("javafx.beans.property.DoublePropertyBase");
+FloatProperty = Java.type("javafx.beans.property.FloatProperty");
+FloatPropertyBase = Java.type("javafx.beans.property.FloatPropertyBase");
+IntegerProperty = Java.type("javafx.beans.property.IntegerProperty");
+IntegerPropertyBase = Java.type("javafx.beans.property.IntegerPropertyBase");
+ListProperty = Java.type("javafx.beans.property.ListProperty");
+ListPropertyBase = Java.type("javafx.beans.property.ListPropertyBase");
+LongProperty = Java.type("javafx.beans.property.LongProperty");
+LongPropertyBase = Java.type("javafx.beans.property.LongPropertyBase");
+MapProperty = Java.type("javafx.beans.property.MapProperty");
+MapPropertyBase = Java.type("javafx.beans.property.MapPropertyBase");
+ObjectProperty = Java.type("javafx.beans.property.ObjectProperty");
+ObjectPropertyBase = Java.type("javafx.beans.property.ObjectPropertyBase");
+Property = Java.type("javafx.beans.property.Property");
+ReadOnlyBooleanProperty = Java.type("javafx.beans.property.ReadOnlyBooleanProperty");
+ReadOnlyBooleanPropertyBase = Java.type("javafx.beans.property.ReadOnlyBooleanPropertyBase");
+ReadOnlyBooleanWrapper = Java.type("javafx.beans.property.ReadOnlyBooleanWrapper");
+ReadOnlyDoubleProperty = Java.type("javafx.beans.property.ReadOnlyDoubleProperty");
+ReadOnlyDoublePropertyBase = Java.type("javafx.beans.property.ReadOnlyDoublePropertyBase");
+ReadOnlyDoubleWrapper = Java.type("javafx.beans.property.ReadOnlyDoubleWrapper");
+ReadOnlyFloatProperty = Java.type("javafx.beans.property.ReadOnlyFloatProperty");
+ReadOnlyFloatPropertyBase = Java.type("javafx.beans.property.ReadOnlyFloatPropertyBase");
+ReadOnlyFloatWrapper = Java.type("javafx.beans.property.ReadOnlyFloatWrapper");
+ReadOnlyIntegerProperty = Java.type("javafx.beans.property.ReadOnlyIntegerProperty");
+ReadOnlyIntegerPropertyBase = Java.type("javafx.beans.property.ReadOnlyIntegerPropertyBase");
+ReadOnlyIntegerWrapper = Java.type("javafx.beans.property.ReadOnlyIntegerWrapper");
+ReadOnlyListProperty = Java.type("javafx.beans.property.ReadOnlyListProperty");
+ReadOnlyListPropertyBase = Java.type("javafx.beans.property.ReadOnlyListPropertyBase");
+ReadOnlyListWrapper = Java.type("javafx.beans.property.ReadOnlyListWrapper");
+ReadOnlyLongProperty = Java.type("javafx.beans.property.ReadOnlyLongProperty");
+ReadOnlyLongPropertyBase = Java.type("javafx.beans.property.ReadOnlyLongPropertyBase");
+ReadOnlyLongWrapper = Java.type("javafx.beans.property.ReadOnlyLongWrapper");
+ReadOnlyMapProperty = Java.type("javafx.beans.property.ReadOnlyMapProperty");
+ReadOnlyMapPropertyBase = Java.type("javafx.beans.property.ReadOnlyMapPropertyBase");
+ReadOnlyMapWrapper = Java.type("javafx.beans.property.ReadOnlyMapWrapper");
+ReadOnlyObjectProperty = Java.type("javafx.beans.property.ReadOnlyObjectProperty");
+ReadOnlyObjectPropertyBase = Java.type("javafx.beans.property.ReadOnlyObjectPropertyBase");
+ReadOnlyObjectWrapper = Java.type("javafx.beans.property.ReadOnlyObjectWrapper");
+ReadOnlyProperty = Java.type("javafx.beans.property.ReadOnlyProperty");
+ReadOnlySetProperty = Java.type("javafx.beans.property.ReadOnlySetProperty");
+ReadOnlySetPropertyBase = Java.type("javafx.beans.property.ReadOnlySetPropertyBase");
+ReadOnlySetWrapper = Java.type("javafx.beans.property.ReadOnlySetWrapper");
+ReadOnlyStringProperty = Java.type("javafx.beans.property.ReadOnlyStringProperty");
+ReadOnlyStringPropertyBase = Java.type("javafx.beans.property.ReadOnlyStringPropertyBase");
+ReadOnlyStringWrapper = Java.type("javafx.beans.property.ReadOnlyStringWrapper");
+SetProperty = Java.type("javafx.beans.property.SetProperty");
+SetPropertyBase = Java.type("javafx.beans.property.SetPropertyBase");
+SimpleBooleanProperty = Java.type("javafx.beans.property.SimpleBooleanProperty");
+SimpleDoubleProperty = Java.type("javafx.beans.property.SimpleDoubleProperty");
+SimpleFloatProperty = Java.type("javafx.beans.property.SimpleFloatProperty");
+SimpleIntegerProperty = Java.type("javafx.beans.property.SimpleIntegerProperty");
+SimpleListProperty = Java.type("javafx.beans.property.SimpleListProperty");
+SimpleLongProperty = Java.type("javafx.beans.property.SimpleLongProperty");
+SimpleMapProperty = Java.type("javafx.beans.property.SimpleMapProperty");
+SimpleObjectProperty = Java.type("javafx.beans.property.SimpleObjectProperty");
+SimpleSetProperty = Java.type("javafx.beans.property.SimpleSetProperty");
+SimpleStringProperty = Java.type("javafx.beans.property.SimpleStringProperty");
+StringProperty = Java.type("javafx.beans.property.StringProperty");
+StringPropertyBase = Java.type("javafx.beans.property.StringPropertyBase");
+ChangeListener = Java.type("javafx.beans.value.ChangeListener");
+ObservableBooleanValue = Java.type("javafx.beans.value.ObservableBooleanValue");
+ObservableDoubleValue = Java.type("javafx.beans.value.ObservableDoubleValue");
+ObservableFloatValue = Java.type("javafx.beans.value.ObservableFloatValue");
+ObservableIntegerValue = Java.type("javafx.beans.value.ObservableIntegerValue");
+ObservableListValue = Java.type("javafx.beans.value.ObservableListValue");
+ObservableLongValue = Java.type("javafx.beans.value.ObservableLongValue");
+ObservableMapValue = Java.type("javafx.beans.value.ObservableMapValue");
+ObservableNumberValue = Java.type("javafx.beans.value.ObservableNumberValue");
+ObservableObjectValue = Java.type("javafx.beans.value.ObservableObjectValue");
+ObservableSetValue = Java.type("javafx.beans.value.ObservableSetValue");
+ObservableStringValue = Java.type("javafx.beans.value.ObservableStringValue");
+ObservableValue = Java.type("javafx.beans.value.ObservableValue");
+ObservableValueBase = Java.type("javafx.beans.value.ObservableValueBase");
+WeakChangeListener = Java.type("javafx.beans.value.WeakChangeListener");
+WritableBooleanValue = Java.type("javafx.beans.value.WritableBooleanValue");
+WritableDoubleValue = Java.type("javafx.beans.value.WritableDoubleValue");
+WritableFloatValue = Java.type("javafx.beans.value.WritableFloatValue");
+WritableIntegerValue = Java.type("javafx.beans.value.WritableIntegerValue");
+WritableListValue = Java.type("javafx.beans.value.WritableListValue");
+WritableLongValue = Java.type("javafx.beans.value.WritableLongValue");
+WritableMapValue = Java.type("javafx.beans.value.WritableMapValue");
+WritableNumberValue = Java.type("javafx.beans.value.WritableNumberValue");
+WritableObjectValue = Java.type("javafx.beans.value.WritableObjectValue");
+WritableSetValue = Java.type("javafx.beans.value.WritableSetValue");
+WritableStringValue = Java.type("javafx.beans.value.WritableStringValue");
+WritableValue = Java.type("javafx.beans.value.WritableValue");
+WeakInvalidationListener = Java.type("javafx.beans.WeakInvalidationListener");
+WeakListener = Java.type("javafx.beans.WeakListener");
+FXCollections = Java.type("javafx.collections.FXCollections");
+ListChangeListener = Java.type("javafx.collections.ListChangeListener");
+ListChangeListener$Change = Java.type("javafx.collections.ListChangeListener$Change");
+MapChangeListener = Java.type("javafx.collections.MapChangeListener");
+MapChangeListener$Change = Java.type("javafx.collections.MapChangeListener$Change");
+ModifiableObservableListBase = Java.type("javafx.collections.ModifiableObservableListBase");
+ObservableList = Java.type("javafx.collections.ObservableList");
+ObservableListBase = Java.type("javafx.collections.ObservableListBase");
+ObservableMap = Java.type("javafx.collections.ObservableMap");
+ObservableSet = Java.type("javafx.collections.ObservableSet");
+SetChangeListener = Java.type("javafx.collections.SetChangeListener");
+SetChangeListener$Change = Java.type("javafx.collections.SetChangeListener$Change");
+WeakListChangeListener = Java.type("javafx.collections.WeakListChangeListener");
+WeakMapChangeListener = Java.type("javafx.collections.WeakMapChangeListener");
+WeakSetChangeListener = Java.type("javafx.collections.WeakSetChangeListener");
+ActionEvent = Java.type("javafx.event.ActionEvent");
+Event = Java.type("javafx.event.Event");
+EventDispatchChain = Java.type("javafx.event.EventDispatchChain");
+EventDispatcher = Java.type("javafx.event.EventDispatcher");
+EventHandler = Java.type("javafx.event.EventHandler");
+EventTarget = Java.type("javafx.event.EventTarget");
+EventType = Java.type("javafx.event.EventType");
+WeakEventHandler = Java.type("javafx.event.WeakEventHandler");
+Builder = Java.type("javafx.util.Builder");
+BuilderFactory = Java.type("javafx.util.BuilderFactory");
+Callback = Java.type("javafx.util.Callback");
+BigDecimalStringConverter = Java.type("javafx.util.converter.BigDecimalStringConverter");
+BigIntegerStringConverter = Java.type("javafx.util.converter.BigIntegerStringConverter");
+BooleanStringConverter = Java.type("javafx.util.converter.BooleanStringConverter");
+ByteStringConverter = Java.type("javafx.util.converter.ByteStringConverter");
+CharacterStringConverter = Java.type("javafx.util.converter.CharacterStringConverter");
+CurrencyStringConverter = Java.type("javafx.util.converter.CurrencyStringConverter");
+DateStringConverter = Java.type("javafx.util.converter.DateStringConverter");
+DateTimeStringConverter = Java.type("javafx.util.converter.DateTimeStringConverter");
+DefaultStringConverter = Java.type("javafx.util.converter.DefaultStringConverter");
+DoubleStringConverter = Java.type("javafx.util.converter.DoubleStringConverter");
+FloatStringConverter = Java.type("javafx.util.converter.FloatStringConverter");
+FormatStringConverter = Java.type("javafx.util.converter.FormatStringConverter");
+IntegerStringConverter = Java.type("javafx.util.converter.IntegerStringConverter");
+LongStringConverter = Java.type("javafx.util.converter.LongStringConverter");
+NumberStringConverter = Java.type("javafx.util.converter.NumberStringConverter");
+PercentageStringConverter = Java.type("javafx.util.converter.PercentageStringConverter");
+ShortStringConverter = Java.type("javafx.util.converter.ShortStringConverter");
+TimeStringConverter = Java.type("javafx.util.converter.TimeStringConverter");
+Duration = Java.type("javafx.util.Duration");
+Pair = Java.type("javafx.util.Pair");
+StringConverter = Java.type("javafx.util.StringConverter");
diff --git a/src/jdk/nashorn/internal/runtime/resources/fx/bootstrap.js b/src/jdk/nashorn/internal/runtime/resources/fx/bootstrap.js
new file mode 100644
index 00000000..97b01a08
--- /dev/null
+++ b/src/jdk/nashorn/internal/runtime/resources/fx/bootstrap.js
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+// Check for fx presence.
+if (typeof javafx.application.Application != "function") {
+ print("JavaFX is not available.");
+ exit(1);
+}
+
+// Extend the javafx.application.Application class overriding init, start and stop.
+com.sun.javafx.application.LauncherImpl.launchApplication((Java.extend(javafx.application.Application, {
+ // Overridden javafx.application.Application.init();
+ init: function() {
+ // Java FX packages and classes must be defined here because
+ // they may not be viable until launch time due to clinit ordering.
+
+ load("fx:base.js");
+ },
+
+ // Overridden javafx.application.Application.start(Stage stage);
+ start: function(stage) {
+ // Set up stage global.
+ $STAGE = stage;
+
+ // Load user FX scripts.
+ for each (var script in $SCRIPTS) {
+ load(script);
+ }
+
+ // Call the global init function if present.
+ if ($GLOBAL.init) {
+ init();
+ }
+
+ // Call the global start function if present. Otherwise show the stage.
+ if ($GLOBAL.start) {
+ start(stage);
+ } else {
+ stage.show();
+ }
+ },
+
+ // Overridden javafx.application.Application.stop();
+ stop: function() {
+ // Call the global stop function if present.
+ if ($GLOBAL.stop) {
+ stop();
+ }
+ }
+
+ // No arguments passed to application (handled thru $ARG.)
+})).class, new (Java.type("java.lang.String[]"))(0));
diff --git a/src/jdk/nashorn/internal/runtime/resources/fx/controls.js b/src/jdk/nashorn/internal/runtime/resources/fx/controls.js
new file mode 100644
index 00000000..f550107d
--- /dev/null
+++ b/src/jdk/nashorn/internal/runtime/resources/fx/controls.js
@@ -0,0 +1,264 @@
+/*
+ * 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.
+ */
+
+AreaChart = Java.type("javafx.scene.chart.AreaChart");
+AreaChartBuilder = Java.type("javafx.scene.chart.AreaChartBuilder");
+Axis = Java.type("javafx.scene.chart.Axis");
+Axis$TickMark = Java.type("javafx.scene.chart.Axis$TickMark");
+AxisBuilder = Java.type("javafx.scene.chart.AxisBuilder");
+BarChart = Java.type("javafx.scene.chart.BarChart");
+BarChartBuilder = Java.type("javafx.scene.chart.BarChartBuilder");
+BubbleChart = Java.type("javafx.scene.chart.BubbleChart");
+BubbleChartBuilder = Java.type("javafx.scene.chart.BubbleChartBuilder");
+CategoryAxis = Java.type("javafx.scene.chart.CategoryAxis");
+CategoryAxisBuilder = Java.type("javafx.scene.chart.CategoryAxisBuilder");
+Chart = Java.type("javafx.scene.chart.Chart");
+ChartBuilder = Java.type("javafx.scene.chart.ChartBuilder");
+LineChart = Java.type("javafx.scene.chart.LineChart");
+LineChartBuilder = Java.type("javafx.scene.chart.LineChartBuilder");
+NumberAxis = Java.type("javafx.scene.chart.NumberAxis");
+NumberAxis$DefaultFormatter = Java.type("javafx.scene.chart.NumberAxis$DefaultFormatter");
+NumberAxisBuilder = Java.type("javafx.scene.chart.NumberAxisBuilder");
+PieChart = Java.type("javafx.scene.chart.PieChart");
+PieChart$Data = Java.type("javafx.scene.chart.PieChart$Data");
+PieChartBuilder = Java.type("javafx.scene.chart.PieChartBuilder");
+ScatterChart = Java.type("javafx.scene.chart.ScatterChart");
+ScatterChartBuilder = Java.type("javafx.scene.chart.ScatterChartBuilder");
+StackedAreaChart = Java.type("javafx.scene.chart.StackedAreaChart");
+StackedAreaChartBuilder = Java.type("javafx.scene.chart.StackedAreaChartBuilder");
+StackedBarChart = Java.type("javafx.scene.chart.StackedBarChart");
+StackedBarChartBuilder = Java.type("javafx.scene.chart.StackedBarChartBuilder");
+ValueAxis = Java.type("javafx.scene.chart.ValueAxis");
+ValueAxisBuilder = Java.type("javafx.scene.chart.ValueAxisBuilder");
+XYChart = Java.type("javafx.scene.chart.XYChart");
+XYChart$Data = Java.type("javafx.scene.chart.XYChart$Data");
+XYChart$Series = Java.type("javafx.scene.chart.XYChart$Series");
+XYChartBuilder = Java.type("javafx.scene.chart.XYChartBuilder");
+Accordion = Java.type("javafx.scene.control.Accordion");
+AccordionBuilder = Java.type("javafx.scene.control.AccordionBuilder");
+Button = Java.type("javafx.scene.control.Button");
+ButtonBase = Java.type("javafx.scene.control.ButtonBase");
+ButtonBaseBuilder = Java.type("javafx.scene.control.ButtonBaseBuilder");
+ButtonBuilder = Java.type("javafx.scene.control.ButtonBuilder");
+Cell = Java.type("javafx.scene.control.Cell");
+CheckBoxListCell = Java.type("javafx.scene.control.cell.CheckBoxListCell");
+CheckBoxListCellBuilder = Java.type("javafx.scene.control.cell.CheckBoxListCellBuilder");
+CheckBoxTableCell = Java.type("javafx.scene.control.cell.CheckBoxTableCell");
+CheckBoxTableCellBuilder = Java.type("javafx.scene.control.cell.CheckBoxTableCellBuilder");
+CheckBoxTreeCell = Java.type("javafx.scene.control.cell.CheckBoxTreeCell");
+CheckBoxTreeCellBuilder = Java.type("javafx.scene.control.cell.CheckBoxTreeCellBuilder");
+CheckBoxTreeTableCell = Java.type("javafx.scene.control.cell.CheckBoxTreeTableCell");
+CheckBoxTreeTableCellBuilder = Java.type("javafx.scene.control.cell.CheckBoxTreeTableCellBuilder");
+ChoiceBoxListCell = Java.type("javafx.scene.control.cell.ChoiceBoxListCell");
+ChoiceBoxListCellBuilder = Java.type("javafx.scene.control.cell.ChoiceBoxListCellBuilder");
+ChoiceBoxTableCell = Java.type("javafx.scene.control.cell.ChoiceBoxTableCell");
+ChoiceBoxTableCellBuilder = Java.type("javafx.scene.control.cell.ChoiceBoxTableCellBuilder");
+ChoiceBoxTreeCell = Java.type("javafx.scene.control.cell.ChoiceBoxTreeCell");
+ChoiceBoxTreeCellBuilder = Java.type("javafx.scene.control.cell.ChoiceBoxTreeCellBuilder");
+ChoiceBoxTreeTableCell = Java.type("javafx.scene.control.cell.ChoiceBoxTreeTableCell");
+ChoiceBoxTreeTableCellBuilder = Java.type("javafx.scene.control.cell.ChoiceBoxTreeTableCellBuilder");
+ComboBoxListCell = Java.type("javafx.scene.control.cell.ComboBoxListCell");
+ComboBoxListCellBuilder = Java.type("javafx.scene.control.cell.ComboBoxListCellBuilder");
+ComboBoxTableCell = Java.type("javafx.scene.control.cell.ComboBoxTableCell");
+ComboBoxTableCellBuilder = Java.type("javafx.scene.control.cell.ComboBoxTableCellBuilder");
+ComboBoxTreeCell = Java.type("javafx.scene.control.cell.ComboBoxTreeCell");
+ComboBoxTreeCellBuilder = Java.type("javafx.scene.control.cell.ComboBoxTreeCellBuilder");
+ComboBoxTreeTableCell = Java.type("javafx.scene.control.cell.ComboBoxTreeTableCell");
+ComboBoxTreeTableCellBuilder = Java.type("javafx.scene.control.cell.ComboBoxTreeTableCellBuilder");
+MapValueFactory = Java.type("javafx.scene.control.cell.MapValueFactory");
+ProgressBarTableCell = Java.type("javafx.scene.control.cell.ProgressBarTableCell");
+ProgressBarTreeTableCell = Java.type("javafx.scene.control.cell.ProgressBarTreeTableCell");
+PropertyValueFactory = Java.type("javafx.scene.control.cell.PropertyValueFactory");
+PropertyValueFactoryBuilder = Java.type("javafx.scene.control.cell.PropertyValueFactoryBuilder");
+TextFieldListCell = Java.type("javafx.scene.control.cell.TextFieldListCell");
+TextFieldListCellBuilder = Java.type("javafx.scene.control.cell.TextFieldListCellBuilder");
+TextFieldTableCell = Java.type("javafx.scene.control.cell.TextFieldTableCell");
+TextFieldTableCellBuilder = Java.type("javafx.scene.control.cell.TextFieldTableCellBuilder");
+TextFieldTreeCell = Java.type("javafx.scene.control.cell.TextFieldTreeCell");
+TextFieldTreeCellBuilder = Java.type("javafx.scene.control.cell.TextFieldTreeCellBuilder");
+TextFieldTreeTableCell = Java.type("javafx.scene.control.cell.TextFieldTreeTableCell");
+TextFieldTreeTableCellBuilder = Java.type("javafx.scene.control.cell.TextFieldTreeTableCellBuilder");
+TreeItemPropertyValueFactory = Java.type("javafx.scene.control.cell.TreeItemPropertyValueFactory");
+TreeItemPropertyValueFactoryBuilder = Java.type("javafx.scene.control.cell.TreeItemPropertyValueFactoryBuilder");
+CellBuilder = Java.type("javafx.scene.control.CellBuilder");
+CheckBox = Java.type("javafx.scene.control.CheckBox");
+CheckBoxBuilder = Java.type("javafx.scene.control.CheckBoxBuilder");
+CheckBoxTreeItem = Java.type("javafx.scene.control.CheckBoxTreeItem");
+CheckBoxTreeItem$TreeModificationEvent = Java.type("javafx.scene.control.CheckBoxTreeItem$TreeModificationEvent");
+CheckBoxTreeItemBuilder = Java.type("javafx.scene.control.CheckBoxTreeItemBuilder");
+CheckMenuItem = Java.type("javafx.scene.control.CheckMenuItem");
+CheckMenuItemBuilder = Java.type("javafx.scene.control.CheckMenuItemBuilder");
+ChoiceBox = Java.type("javafx.scene.control.ChoiceBox");
+ChoiceBoxBuilder = Java.type("javafx.scene.control.ChoiceBoxBuilder");
+ColorPicker = Java.type("javafx.scene.control.ColorPicker");
+ColorPickerBuilder = Java.type("javafx.scene.control.ColorPickerBuilder");
+ComboBox = Java.type("javafx.scene.control.ComboBox");
+ComboBoxBase = Java.type("javafx.scene.control.ComboBoxBase");
+ComboBoxBaseBuilder = Java.type("javafx.scene.control.ComboBoxBaseBuilder");
+ComboBoxBuilder = Java.type("javafx.scene.control.ComboBoxBuilder");
+ContentDisplay = Java.type("javafx.scene.control.ContentDisplay");
+ContextMenu = Java.type("javafx.scene.control.ContextMenu");
+ContextMenuBuilder = Java.type("javafx.scene.control.ContextMenuBuilder");
+Control = Java.type("javafx.scene.control.Control");
+ControlBuilder = Java.type("javafx.scene.control.ControlBuilder");
+CustomMenuItem = Java.type("javafx.scene.control.CustomMenuItem");
+CustomMenuItemBuilder = Java.type("javafx.scene.control.CustomMenuItemBuilder");
+FocusModel = Java.type("javafx.scene.control.FocusModel");
+Hyperlink = Java.type("javafx.scene.control.Hyperlink");
+HyperlinkBuilder = Java.type("javafx.scene.control.HyperlinkBuilder");
+IndexedCell = Java.type("javafx.scene.control.IndexedCell");
+IndexedCellBuilder = Java.type("javafx.scene.control.IndexedCellBuilder");
+IndexRange = Java.type("javafx.scene.control.IndexRange");
+IndexRangeBuilder = Java.type("javafx.scene.control.IndexRangeBuilder");
+Label = Java.type("javafx.scene.control.Label");
+LabelBuilder = Java.type("javafx.scene.control.LabelBuilder");
+Labeled = Java.type("javafx.scene.control.Labeled");
+LabeledBuilder = Java.type("javafx.scene.control.LabeledBuilder");
+ListCell = Java.type("javafx.scene.control.ListCell");
+ListCellBuilder = Java.type("javafx.scene.control.ListCellBuilder");
+ListView = Java.type("javafx.scene.control.ListView");
+ListView$EditEvent = Java.type("javafx.scene.control.ListView$EditEvent");
+ListViewBuilder = Java.type("javafx.scene.control.ListViewBuilder");
+Menu = Java.type("javafx.scene.control.Menu");
+MenuBar = Java.type("javafx.scene.control.MenuBar");
+MenuBarBuilder = Java.type("javafx.scene.control.MenuBarBuilder");
+MenuBuilder = Java.type("javafx.scene.control.MenuBuilder");
+MenuButton = Java.type("javafx.scene.control.MenuButton");
+MenuButtonBuilder = Java.type("javafx.scene.control.MenuButtonBuilder");
+MenuItem = Java.type("javafx.scene.control.MenuItem");
+MenuItemBuilder = Java.type("javafx.scene.control.MenuItemBuilder");
+MultipleSelectionModel = Java.type("javafx.scene.control.MultipleSelectionModel");
+MultipleSelectionModelBuilder = Java.type("javafx.scene.control.MultipleSelectionModelBuilder");
+OverrunStyle = Java.type("javafx.scene.control.OverrunStyle");
+Pagination = Java.type("javafx.scene.control.Pagination");
+PaginationBuilder = Java.type("javafx.scene.control.PaginationBuilder");
+PasswordField = Java.type("javafx.scene.control.PasswordField");
+PasswordFieldBuilder = Java.type("javafx.scene.control.PasswordFieldBuilder");
+PopupControl = Java.type("javafx.scene.control.PopupControl");
+PopupControlBuilder = Java.type("javafx.scene.control.PopupControlBuilder");
+ProgressBar = Java.type("javafx.scene.control.ProgressBar");
+ProgressBarBuilder = Java.type("javafx.scene.control.ProgressBarBuilder");
+ProgressIndicator = Java.type("javafx.scene.control.ProgressIndicator");
+ProgressIndicatorBuilder = Java.type("javafx.scene.control.ProgressIndicatorBuilder");
+RadioButton = Java.type("javafx.scene.control.RadioButton");
+RadioButtonBuilder = Java.type("javafx.scene.control.RadioButtonBuilder");
+RadioMenuItem = Java.type("javafx.scene.control.RadioMenuItem");
+RadioMenuItemBuilder = Java.type("javafx.scene.control.RadioMenuItemBuilder");
+ResizeFeaturesBase = Java.type("javafx.scene.control.ResizeFeaturesBase");
+ResizeFeaturesBaseBuilder = Java.type("javafx.scene.control.ResizeFeaturesBaseBuilder");
+ScrollBar = Java.type("javafx.scene.control.ScrollBar");
+ScrollBarBuilder = Java.type("javafx.scene.control.ScrollBarBuilder");
+ScrollPane = Java.type("javafx.scene.control.ScrollPane");
+ScrollPane$ScrollBarPolicy = Java.type("javafx.scene.control.ScrollPane$ScrollBarPolicy");
+ScrollPaneBuilder = Java.type("javafx.scene.control.ScrollPaneBuilder");
+ScrollToEvent = Java.type("javafx.scene.control.ScrollToEvent");
+SelectionMode = Java.type("javafx.scene.control.SelectionMode");
+SelectionModel = Java.type("javafx.scene.control.SelectionModel");
+Separator = Java.type("javafx.scene.control.Separator");
+SeparatorBuilder = Java.type("javafx.scene.control.SeparatorBuilder");
+SeparatorMenuItem = Java.type("javafx.scene.control.SeparatorMenuItem");
+SeparatorMenuItemBuilder = Java.type("javafx.scene.control.SeparatorMenuItemBuilder");
+SingleSelectionModel = Java.type("javafx.scene.control.SingleSelectionModel");
+Skin = Java.type("javafx.scene.control.Skin");
+SkinBase = Java.type("javafx.scene.control.SkinBase");
+SkinBaseBuilder = Java.type("javafx.scene.control.SkinBaseBuilder");
+Skinnable = Java.type("javafx.scene.control.Skinnable");
+Slider = Java.type("javafx.scene.control.Slider");
+SliderBuilder = Java.type("javafx.scene.control.SliderBuilder");
+SortEvent = Java.type("javafx.scene.control.SortEvent");
+SplitMenuButton = Java.type("javafx.scene.control.SplitMenuButton");
+SplitMenuButtonBuilder = Java.type("javafx.scene.control.SplitMenuButtonBuilder");
+SplitPane = Java.type("javafx.scene.control.SplitPane");
+SplitPane$Divider = Java.type("javafx.scene.control.SplitPane$Divider");
+SplitPaneBuilder = Java.type("javafx.scene.control.SplitPaneBuilder");
+Tab = Java.type("javafx.scene.control.Tab");
+TabBuilder = Java.type("javafx.scene.control.TabBuilder");
+TableCell = Java.type("javafx.scene.control.TableCell");
+TableCellBuilder = Java.type("javafx.scene.control.TableCellBuilder");
+TableColumn = Java.type("javafx.scene.control.TableColumn");
+TableColumn$CellDataFeatures = Java.type("javafx.scene.control.TableColumn$CellDataFeatures");
+TableColumn$CellEditEvent = Java.type("javafx.scene.control.TableColumn$CellEditEvent");
+TableColumn$SortType = Java.type("javafx.scene.control.TableColumn$SortType");
+TableColumnBase = Java.type("javafx.scene.control.TableColumnBase");
+TableColumnBaseBuilder = Java.type("javafx.scene.control.TableColumnBaseBuilder");
+TableColumnBuilder = Java.type("javafx.scene.control.TableColumnBuilder");
+TableFocusModel = Java.type("javafx.scene.control.TableFocusModel");
+TablePosition = Java.type("javafx.scene.control.TablePosition");
+TablePositionBase = Java.type("javafx.scene.control.TablePositionBase");
+TableRow = Java.type("javafx.scene.control.TableRow");
+TableRowBuilder = Java.type("javafx.scene.control.TableRowBuilder");
+TableSelectionModel = Java.type("javafx.scene.control.TableSelectionModel");
+TableSelectionModelBuilder = Java.type("javafx.scene.control.TableSelectionModelBuilder");
+TableView = Java.type("javafx.scene.control.TableView");
+TableView$ResizeFeatures = Java.type("javafx.scene.control.TableView$ResizeFeatures");
+TableView$TableViewFocusModel = Java.type("javafx.scene.control.TableView$TableViewFocusModel");
+TableView$TableViewSelectionModel = Java.type("javafx.scene.control.TableView$TableViewSelectionModel");
+TableViewBuilder = Java.type("javafx.scene.control.TableViewBuilder");
+TabPane = Java.type("javafx.scene.control.TabPane");
+TabPane$TabClosingPolicy = Java.type("javafx.scene.control.TabPane$TabClosingPolicy");
+TabPaneBuilder = Java.type("javafx.scene.control.TabPaneBuilder");
+TextArea = Java.type("javafx.scene.control.TextArea");
+TextAreaBuilder = Java.type("javafx.scene.control.TextAreaBuilder");
+TextField = Java.type("javafx.scene.control.TextField");
+TextFieldBuilder = Java.type("javafx.scene.control.TextFieldBuilder");
+TextInputControl = Java.type("javafx.scene.control.TextInputControl");
+TextInputControl$Content = Java.type("javafx.scene.control.TextInputControl$Content");
+TextInputControlBuilder = Java.type("javafx.scene.control.TextInputControlBuilder");
+TitledPane = Java.type("javafx.scene.control.TitledPane");
+TitledPaneBuilder = Java.type("javafx.scene.control.TitledPaneBuilder");
+Toggle = Java.type("javafx.scene.control.Toggle");
+ToggleButton = Java.type("javafx.scene.control.ToggleButton");
+ToggleButtonBuilder = Java.type("javafx.scene.control.ToggleButtonBuilder");
+ToggleGroup = Java.type("javafx.scene.control.ToggleGroup");
+ToggleGroupBuilder = Java.type("javafx.scene.control.ToggleGroupBuilder");
+ToolBar = Java.type("javafx.scene.control.ToolBar");
+ToolBarBuilder = Java.type("javafx.scene.control.ToolBarBuilder");
+Tooltip = Java.type("javafx.scene.control.Tooltip");
+TooltipBuilder = Java.type("javafx.scene.control.TooltipBuilder");
+TreeCell = Java.type("javafx.scene.control.TreeCell");
+TreeCellBuilder = Java.type("javafx.scene.control.TreeCellBuilder");
+TreeItem = Java.type("javafx.scene.control.TreeItem");
+TreeItem$TreeModificationEvent = Java.type("javafx.scene.control.TreeItem$TreeModificationEvent");
+TreeItemBuilder = Java.type("javafx.scene.control.TreeItemBuilder");
+TreeSortMode = Java.type("javafx.scene.control.TreeSortMode");
+TreeTableCell = Java.type("javafx.scene.control.TreeTableCell");
+TreeTableCellBuilder = Java.type("javafx.scene.control.TreeTableCellBuilder");
+TreeTableColumn = Java.type("javafx.scene.control.TreeTableColumn");
+TreeTableColumn$CellDataFeatures = Java.type("javafx.scene.control.TreeTableColumn$CellDataFeatures");
+TreeTableColumn$CellEditEvent = Java.type("javafx.scene.control.TreeTableColumn$CellEditEvent");
+TreeTableColumn$SortType = Java.type("javafx.scene.control.TreeTableColumn$SortType");
+TreeTableColumnBuilder = Java.type("javafx.scene.control.TreeTableColumnBuilder");
+TreeTablePosition = Java.type("javafx.scene.control.TreeTablePosition");
+TreeTableRow = Java.type("javafx.scene.control.TreeTableRow");
+TreeTableRowBuilder = Java.type("javafx.scene.control.TreeTableRowBuilder");
+TreeTableView = Java.type("javafx.scene.control.TreeTableView");
+TreeTableView$EditEvent = Java.type("javafx.scene.control.TreeTableView$EditEvent");
+TreeTableView$ResizeFeatures = Java.type("javafx.scene.control.TreeTableView$ResizeFeatures");
+TreeTableView$TreeTableViewFocusModel = Java.type("javafx.scene.control.TreeTableView$TreeTableViewFocusModel");
+TreeTableView$TreeTableViewSelectionModel = Java.type("javafx.scene.control.TreeTableView$TreeTableViewSelectionModel");
+TreeTableViewBuilder = Java.type("javafx.scene.control.TreeTableViewBuilder");
+TreeView = Java.type("javafx.scene.control.TreeView");
+TreeView$EditEvent = Java.type("javafx.scene.control.TreeView$EditEvent");
+TreeViewBuilder = Java.type("javafx.scene.control.TreeViewBuilder");
diff --git a/src/jdk/nashorn/internal/runtime/resources/fx/fxml.js b/src/jdk/nashorn/internal/runtime/resources/fx/fxml.js
new file mode 100644
index 00000000..bc018a9e
--- /dev/null
+++ b/src/jdk/nashorn/internal/runtime/resources/fx/fxml.js
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+FXML = Java.type("javafx.fxml.FXML");
+FXMLLoader = Java.type("javafx.fxml.FXMLLoader");
+Initializable = Java.type("javafx.fxml.Initializable");
+JavaFXBuilderFactory = Java.type("javafx.fxml.JavaFXBuilderFactory");
+LoadException = Java.type("javafx.fxml.LoadException");
diff --git a/src/jdk/nashorn/internal/runtime/resources/fx/graphics.js b/src/jdk/nashorn/internal/runtime/resources/fx/graphics.js
new file mode 100644
index 00000000..9da00bd2
--- /dev/null
+++ b/src/jdk/nashorn/internal/runtime/resources/fx/graphics.js
@@ -0,0 +1,431 @@
+/*
+ * 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.
+ */
+
+Animation = Java.type("javafx.animation.Animation");
+Animation$Status = Java.type("javafx.animation.Animation$Status");
+AnimationBuilder = Java.type("javafx.animation.AnimationBuilder");
+AnimationTimer = Java.type("javafx.animation.AnimationTimer");
+FadeTransition = Java.type("javafx.animation.FadeTransition");
+FadeTransitionBuilder = Java.type("javafx.animation.FadeTransitionBuilder");
+FillTransition = Java.type("javafx.animation.FillTransition");
+FillTransitionBuilder = Java.type("javafx.animation.FillTransitionBuilder");
+Interpolatable = Java.type("javafx.animation.Interpolatable");
+Interpolator = Java.type("javafx.animation.Interpolator");
+KeyFrame = Java.type("javafx.animation.KeyFrame");
+KeyValue = Java.type("javafx.animation.KeyValue");
+ParallelTransition = Java.type("javafx.animation.ParallelTransition");
+ParallelTransitionBuilder = Java.type("javafx.animation.ParallelTransitionBuilder");
+PathTransition = Java.type("javafx.animation.PathTransition");
+PathTransition$OrientationType = Java.type("javafx.animation.PathTransition$OrientationType");
+PathTransitionBuilder = Java.type("javafx.animation.PathTransitionBuilder");
+PauseTransition = Java.type("javafx.animation.PauseTransition");
+PauseTransitionBuilder = Java.type("javafx.animation.PauseTransitionBuilder");
+RotateTransition = Java.type("javafx.animation.RotateTransition");
+RotateTransitionBuilder = Java.type("javafx.animation.RotateTransitionBuilder");
+ScaleTransition = Java.type("javafx.animation.ScaleTransition");
+ScaleTransitionBuilder = Java.type("javafx.animation.ScaleTransitionBuilder");
+SequentialTransition = Java.type("javafx.animation.SequentialTransition");
+SequentialTransitionBuilder = Java.type("javafx.animation.SequentialTransitionBuilder");
+StrokeTransition = Java.type("javafx.animation.StrokeTransition");
+StrokeTransitionBuilder = Java.type("javafx.animation.StrokeTransitionBuilder");
+Timeline = Java.type("javafx.animation.Timeline");
+TimelineBuilder = Java.type("javafx.animation.TimelineBuilder");
+Transition = Java.type("javafx.animation.Transition");
+TransitionBuilder = Java.type("javafx.animation.TransitionBuilder");
+TranslateTransition = Java.type("javafx.animation.TranslateTransition");
+TranslateTransitionBuilder = Java.type("javafx.animation.TranslateTransitionBuilder");
+Application = Java.type("javafx.application.Application");
+Application$Parameters = Java.type("javafx.application.Application$Parameters");
+ConditionalFeature = Java.type("javafx.application.ConditionalFeature");
+HostServices = Java.type("javafx.application.HostServices");
+Platform = Java.type("javafx.application.Platform");
+Preloader = Java.type("javafx.application.Preloader");
+Preloader$ErrorNotification = Java.type("javafx.application.Preloader$ErrorNotification");
+Preloader$PreloaderNotification = Java.type("javafx.application.Preloader$PreloaderNotification");
+Preloader$ProgressNotification = Java.type("javafx.application.Preloader$ProgressNotification");
+Preloader$StateChangeNotification = Java.type("javafx.application.Preloader$StateChangeNotification");
+Preloader$StateChangeNotification$Type = Java.type("javafx.application.Preloader$StateChangeNotification$Type");
+ScheduledService = Java.type("javafx.concurrent.ScheduledService");
+Service = Java.type("javafx.concurrent.Service");
+Task = Java.type("javafx.concurrent.Task");
+Worker = Java.type("javafx.concurrent.Worker");
+Worker$State = Java.type("javafx.concurrent.Worker$State");
+WorkerStateEvent = Java.type("javafx.concurrent.WorkerStateEvent");
+CssMetaData = Java.type("javafx.css.CssMetaData");
+FontCssMetaData = Java.type("javafx.css.FontCssMetaData");
+ParsedValue = Java.type("javafx.css.ParsedValue");
+PseudoClass = Java.type("javafx.css.PseudoClass");
+SimpleStyleableBooleanProperty = Java.type("javafx.css.SimpleStyleableBooleanProperty");
+SimpleStyleableDoubleProperty = Java.type("javafx.css.SimpleStyleableDoubleProperty");
+SimpleStyleableFloatProperty = Java.type("javafx.css.SimpleStyleableFloatProperty");
+SimpleStyleableIntegerProperty = Java.type("javafx.css.SimpleStyleableIntegerProperty");
+SimpleStyleableLongProperty = Java.type("javafx.css.SimpleStyleableLongProperty");
+SimpleStyleableObjectProperty = Java.type("javafx.css.SimpleStyleableObjectProperty");
+SimpleStyleableStringProperty = Java.type("javafx.css.SimpleStyleableStringProperty");
+Styleable = Java.type("javafx.css.Styleable");
+StyleableBooleanProperty = Java.type("javafx.css.StyleableBooleanProperty");
+StyleableDoubleProperty = Java.type("javafx.css.StyleableDoubleProperty");
+StyleableFloatProperty = Java.type("javafx.css.StyleableFloatProperty");
+StyleableIntegerProperty = Java.type("javafx.css.StyleableIntegerProperty");
+StyleableLongProperty = Java.type("javafx.css.StyleableLongProperty");
+StyleableObjectProperty = Java.type("javafx.css.StyleableObjectProperty");
+StyleableProperty = Java.type("javafx.css.StyleableProperty");
+StyleableStringProperty = Java.type("javafx.css.StyleableStringProperty");
+StyleConverter = Java.type("javafx.css.StyleConverter");
+StyleOrigin = Java.type("javafx.css.StyleOrigin");
+BoundingBox = Java.type("javafx.geometry.BoundingBox");
+BoundingBoxBuilder = Java.type("javafx.geometry.BoundingBoxBuilder");
+Bounds = Java.type("javafx.geometry.Bounds");
+Dimension2D = Java.type("javafx.geometry.Dimension2D");
+Dimension2DBuilder = Java.type("javafx.geometry.Dimension2DBuilder");
+HorizontalDirection = Java.type("javafx.geometry.HorizontalDirection");
+HPos = Java.type("javafx.geometry.HPos");
+Insets = Java.type("javafx.geometry.Insets");
+InsetsBuilder = Java.type("javafx.geometry.InsetsBuilder");
+NodeOrientation = Java.type("javafx.geometry.NodeOrientation");
+Orientation = Java.type("javafx.geometry.Orientation");
+Point2D = Java.type("javafx.geometry.Point2D");
+Point2DBuilder = Java.type("javafx.geometry.Point2DBuilder");
+Point3D = Java.type("javafx.geometry.Point3D");
+Point3DBuilder = Java.type("javafx.geometry.Point3DBuilder");
+Pos = Java.type("javafx.geometry.Pos");
+Rectangle2D = Java.type("javafx.geometry.Rectangle2D");
+Rectangle2DBuilder = Java.type("javafx.geometry.Rectangle2DBuilder");
+Side = Java.type("javafx.geometry.Side");
+VerticalDirection = Java.type("javafx.geometry.VerticalDirection");
+VPos = Java.type("javafx.geometry.VPos");
+Collation = Java.type("javafx.print.Collation");
+JobSettings = Java.type("javafx.print.JobSettings");
+PageLayout = Java.type("javafx.print.PageLayout");
+PageOrientation = Java.type("javafx.print.PageOrientation");
+PageRange = Java.type("javafx.print.PageRange");
+Paper = Java.type("javafx.print.Paper");
+Paper$Units = Java.type("javafx.print.Paper$Units");
+PaperSource = Java.type("javafx.print.PaperSource");
+PrintColor = Java.type("javafx.print.PrintColor");
+Printer = Java.type("javafx.print.Printer");
+Printer$MarginType = Java.type("javafx.print.Printer$MarginType");
+PrinterAttributes = Java.type("javafx.print.PrinterAttributes");
+PrinterJob = Java.type("javafx.print.PrinterJob");
+PrinterJob$JobStatus = Java.type("javafx.print.PrinterJob$JobStatus");
+PrintQuality = Java.type("javafx.print.PrintQuality");
+PrintResolution = Java.type("javafx.print.PrintResolution");
+PrintSides = Java.type("javafx.print.PrintSides");
+AmbientLight = Java.type("javafx.scene.AmbientLight");
+AmbientLightBuilder = Java.type("javafx.scene.AmbientLightBuilder");
+CacheHint = Java.type("javafx.scene.CacheHint");
+Camera = Java.type("javafx.scene.Camera");
+CameraBuilder = Java.type("javafx.scene.CameraBuilder");
+Canvas = Java.type("javafx.scene.canvas.Canvas");
+CanvasBuilder = Java.type("javafx.scene.canvas.CanvasBuilder");
+GraphicsContext = Java.type("javafx.scene.canvas.GraphicsContext");
+Cursor = Java.type("javafx.scene.Cursor");
+DepthTest = Java.type("javafx.scene.DepthTest");
+Blend = Java.type("javafx.scene.effect.Blend");
+BlendBuilder = Java.type("javafx.scene.effect.BlendBuilder");
+BlendMode = Java.type("javafx.scene.effect.BlendMode");
+Bloom = Java.type("javafx.scene.effect.Bloom");
+BloomBuilder = Java.type("javafx.scene.effect.BloomBuilder");
+BlurType = Java.type("javafx.scene.effect.BlurType");
+BoxBlur = Java.type("javafx.scene.effect.BoxBlur");
+BoxBlurBuilder = Java.type("javafx.scene.effect.BoxBlurBuilder");
+ColorAdjust = Java.type("javafx.scene.effect.ColorAdjust");
+ColorAdjustBuilder = Java.type("javafx.scene.effect.ColorAdjustBuilder");
+ColorInput = Java.type("javafx.scene.effect.ColorInput");
+ColorInputBuilder = Java.type("javafx.scene.effect.ColorInputBuilder");
+DisplacementMap = Java.type("javafx.scene.effect.DisplacementMap");
+DisplacementMapBuilder = Java.type("javafx.scene.effect.DisplacementMapBuilder");
+DropShadow = Java.type("javafx.scene.effect.DropShadow");
+DropShadowBuilder = Java.type("javafx.scene.effect.DropShadowBuilder");
+Effect = Java.type("javafx.scene.effect.Effect");
+FloatMap = Java.type("javafx.scene.effect.FloatMap");
+FloatMapBuilder = Java.type("javafx.scene.effect.FloatMapBuilder");
+GaussianBlur = Java.type("javafx.scene.effect.GaussianBlur");
+GaussianBlurBuilder = Java.type("javafx.scene.effect.GaussianBlurBuilder");
+Glow = Java.type("javafx.scene.effect.Glow");
+GlowBuilder = Java.type("javafx.scene.effect.GlowBuilder");
+ImageInput = Java.type("javafx.scene.effect.ImageInput");
+ImageInputBuilder = Java.type("javafx.scene.effect.ImageInputBuilder");
+InnerShadow = Java.type("javafx.scene.effect.InnerShadow");
+InnerShadowBuilder = Java.type("javafx.scene.effect.InnerShadowBuilder");
+Light = Java.type("javafx.scene.effect.Light");
+Light$Distant = Java.type("javafx.scene.effect.Light$Distant");
+Light$Point = Java.type("javafx.scene.effect.Light$Point");
+Light$Spot = Java.type("javafx.scene.effect.Light$Spot");
+LightBuilder = Java.type("javafx.scene.effect.LightBuilder");
+Lighting = Java.type("javafx.scene.effect.Lighting");
+LightingBuilder = Java.type("javafx.scene.effect.LightingBuilder");
+MotionBlur = Java.type("javafx.scene.effect.MotionBlur");
+MotionBlurBuilder = Java.type("javafx.scene.effect.MotionBlurBuilder");
+PerspectiveTransform = Java.type("javafx.scene.effect.PerspectiveTransform");
+PerspectiveTransformBuilder = Java.type("javafx.scene.effect.PerspectiveTransformBuilder");
+Reflection = Java.type("javafx.scene.effect.Reflection");
+ReflectionBuilder = Java.type("javafx.scene.effect.ReflectionBuilder");
+SepiaTone = Java.type("javafx.scene.effect.SepiaTone");
+SepiaToneBuilder = Java.type("javafx.scene.effect.SepiaToneBuilder");
+Shadow = Java.type("javafx.scene.effect.Shadow");
+ShadowBuilder = Java.type("javafx.scene.effect.ShadowBuilder");
+//Group = Java.type("javafx.scene.Group");
+GroupBuilder = Java.type("javafx.scene.GroupBuilder");
+Image = Java.type("javafx.scene.image.Image");
+ImageView = Java.type("javafx.scene.image.ImageView");
+ImageViewBuilder = Java.type("javafx.scene.image.ImageViewBuilder");
+PixelFormat = Java.type("javafx.scene.image.PixelFormat");
+PixelFormat$Type = Java.type("javafx.scene.image.PixelFormat$Type");
+PixelReader = Java.type("javafx.scene.image.PixelReader");
+PixelWriter = Java.type("javafx.scene.image.PixelWriter");
+WritableImage = Java.type("javafx.scene.image.WritableImage");
+WritablePixelFormat = Java.type("javafx.scene.image.WritablePixelFormat");
+ImageCursor = Java.type("javafx.scene.ImageCursor");
+ImageCursorBuilder = Java.type("javafx.scene.ImageCursorBuilder");
+Clipboard = Java.type("javafx.scene.input.Clipboard");
+ClipboardContent = Java.type("javafx.scene.input.ClipboardContent");
+ClipboardContentBuilder = Java.type("javafx.scene.input.ClipboardContentBuilder");
+ContextMenuEvent = Java.type("javafx.scene.input.ContextMenuEvent");
+DataFormat = Java.type("javafx.scene.input.DataFormat");
+Dragboard = Java.type("javafx.scene.input.Dragboard");
+DragEvent = Java.type("javafx.scene.input.DragEvent");
+GestureEvent = Java.type("javafx.scene.input.GestureEvent");
+InputEvent = Java.type("javafx.scene.input.InputEvent");
+InputEventBuilder = Java.type("javafx.scene.input.InputEventBuilder");
+InputMethodEvent = Java.type("javafx.scene.input.InputMethodEvent");
+InputMethodHighlight = Java.type("javafx.scene.input.InputMethodHighlight");
+InputMethodRequests = Java.type("javafx.scene.input.InputMethodRequests");
+InputMethodTextRun = Java.type("javafx.scene.input.InputMethodTextRun");
+InputMethodTextRunBuilder = Java.type("javafx.scene.input.InputMethodTextRunBuilder");
+KeyCharacterCombination = Java.type("javafx.scene.input.KeyCharacterCombination");
+KeyCharacterCombinationBuilder = Java.type("javafx.scene.input.KeyCharacterCombinationBuilder");
+KeyCode = Java.type("javafx.scene.input.KeyCode");
+KeyCodeCombination = Java.type("javafx.scene.input.KeyCodeCombination");
+KeyCodeCombinationBuilder = Java.type("javafx.scene.input.KeyCodeCombinationBuilder");
+KeyCombination = Java.type("javafx.scene.input.KeyCombination");
+KeyCombination$Modifier = Java.type("javafx.scene.input.KeyCombination$Modifier");
+KeyCombination$ModifierValue = Java.type("javafx.scene.input.KeyCombination$ModifierValue");
+KeyEvent = Java.type("javafx.scene.input.KeyEvent");
+Mnemonic = Java.type("javafx.scene.input.Mnemonic");
+MnemonicBuilder = Java.type("javafx.scene.input.MnemonicBuilder");
+MouseButton = Java.type("javafx.scene.input.MouseButton");
+MouseDragEvent = Java.type("javafx.scene.input.MouseDragEvent");
+MouseEvent = Java.type("javafx.scene.input.MouseEvent");
+PickResult = Java.type("javafx.scene.input.PickResult");
+RotateEvent = Java.type("javafx.scene.input.RotateEvent");
+ScrollEvent = Java.type("javafx.scene.input.ScrollEvent");
+ScrollEvent$HorizontalTextScrollUnits = Java.type("javafx.scene.input.ScrollEvent$HorizontalTextScrollUnits");
+ScrollEvent$VerticalTextScrollUnits = Java.type("javafx.scene.input.ScrollEvent$VerticalTextScrollUnits");
+SwipeEvent = Java.type("javafx.scene.input.SwipeEvent");
+TouchEvent = Java.type("javafx.scene.input.TouchEvent");
+TouchPoint = Java.type("javafx.scene.input.TouchPoint");
+TouchPoint$State = Java.type("javafx.scene.input.TouchPoint$State");
+TouchPointBuilder = Java.type("javafx.scene.input.TouchPointBuilder");
+TransferMode = Java.type("javafx.scene.input.TransferMode");
+ZoomEvent = Java.type("javafx.scene.input.ZoomEvent");
+AnchorPane = Java.type("javafx.scene.layout.AnchorPane");
+AnchorPaneBuilder = Java.type("javafx.scene.layout.AnchorPaneBuilder");
+Background = Java.type("javafx.scene.layout.Background");
+BackgroundBuilder = Java.type("javafx.scene.layout.BackgroundBuilder");
+BackgroundFill = Java.type("javafx.scene.layout.BackgroundFill");
+BackgroundFillBuilder = Java.type("javafx.scene.layout.BackgroundFillBuilder");
+BackgroundImage = Java.type("javafx.scene.layout.BackgroundImage");
+BackgroundImageBuilder = Java.type("javafx.scene.layout.BackgroundImageBuilder");
+BackgroundPosition = Java.type("javafx.scene.layout.BackgroundPosition");
+BackgroundPositionBuilder = Java.type("javafx.scene.layout.BackgroundPositionBuilder");
+BackgroundRepeat = Java.type("javafx.scene.layout.BackgroundRepeat");
+BackgroundSize = Java.type("javafx.scene.layout.BackgroundSize");
+BackgroundSizeBuilder = Java.type("javafx.scene.layout.BackgroundSizeBuilder");
+Border = Java.type("javafx.scene.layout.Border");
+BorderBuilder = Java.type("javafx.scene.layout.BorderBuilder");
+BorderImage = Java.type("javafx.scene.layout.BorderImage");
+BorderImageBuilder = Java.type("javafx.scene.layout.BorderImageBuilder");
+BorderPane = Java.type("javafx.scene.layout.BorderPane");
+BorderPaneBuilder = Java.type("javafx.scene.layout.BorderPaneBuilder");
+BorderRepeat = Java.type("javafx.scene.layout.BorderRepeat");
+BorderStroke = Java.type("javafx.scene.layout.BorderStroke");
+BorderStrokeBuilder = Java.type("javafx.scene.layout.BorderStrokeBuilder");
+BorderStrokeStyle = Java.type("javafx.scene.layout.BorderStrokeStyle");
+BorderStrokeStyleBuilder = Java.type("javafx.scene.layout.BorderStrokeStyleBuilder");
+BorderWidths = Java.type("javafx.scene.layout.BorderWidths");
+BorderWidthsBuilder = Java.type("javafx.scene.layout.BorderWidthsBuilder");
+ColumnConstraints = Java.type("javafx.scene.layout.ColumnConstraints");
+ColumnConstraintsBuilder = Java.type("javafx.scene.layout.ColumnConstraintsBuilder");
+ConstraintsBase = Java.type("javafx.scene.layout.ConstraintsBase");
+CornerRadii = Java.type("javafx.scene.layout.CornerRadii");
+FlowPane = Java.type("javafx.scene.layout.FlowPane");
+FlowPaneBuilder = Java.type("javafx.scene.layout.FlowPaneBuilder");
+GridPane = Java.type("javafx.scene.layout.GridPane");
+GridPaneBuilder = Java.type("javafx.scene.layout.GridPaneBuilder");
+HBox = Java.type("javafx.scene.layout.HBox");
+HBoxBuilder = Java.type("javafx.scene.layout.HBoxBuilder");
+Pane = Java.type("javafx.scene.layout.Pane");
+PaneBuilder = Java.type("javafx.scene.layout.PaneBuilder");
+Priority = Java.type("javafx.scene.layout.Priority");
+Region = Java.type("javafx.scene.layout.Region");
+RegionBuilder = Java.type("javafx.scene.layout.RegionBuilder");
+RowConstraints = Java.type("javafx.scene.layout.RowConstraints");
+RowConstraintsBuilder = Java.type("javafx.scene.layout.RowConstraintsBuilder");
+StackPane = Java.type("javafx.scene.layout.StackPane");
+StackPaneBuilder = Java.type("javafx.scene.layout.StackPaneBuilder");
+TilePane = Java.type("javafx.scene.layout.TilePane");
+TilePaneBuilder = Java.type("javafx.scene.layout.TilePaneBuilder");
+VBox = Java.type("javafx.scene.layout.VBox");
+VBoxBuilder = Java.type("javafx.scene.layout.VBoxBuilder");
+LightBase = Java.type("javafx.scene.LightBase");
+LightBaseBuilder = Java.type("javafx.scene.LightBaseBuilder");
+Node = Java.type("javafx.scene.Node");
+NodeBuilder = Java.type("javafx.scene.NodeBuilder");
+Color = Java.type("javafx.scene.paint.Color");
+ColorBuilder = Java.type("javafx.scene.paint.ColorBuilder");
+CycleMethod = Java.type("javafx.scene.paint.CycleMethod");
+ImagePattern = Java.type("javafx.scene.paint.ImagePattern");
+ImagePatternBuilder = Java.type("javafx.scene.paint.ImagePatternBuilder");
+LinearGradient = Java.type("javafx.scene.paint.LinearGradient");
+LinearGradientBuilder = Java.type("javafx.scene.paint.LinearGradientBuilder");
+Material = Java.type("javafx.scene.paint.Material");
+Paint = Java.type("javafx.scene.paint.Paint");
+PhongMaterial = Java.type("javafx.scene.paint.PhongMaterial");
+PhongMaterialBuilder = Java.type("javafx.scene.paint.PhongMaterialBuilder");
+RadialGradient = Java.type("javafx.scene.paint.RadialGradient");
+RadialGradientBuilder = Java.type("javafx.scene.paint.RadialGradientBuilder");
+Stop = Java.type("javafx.scene.paint.Stop");
+StopBuilder = Java.type("javafx.scene.paint.StopBuilder");
+ParallelCamera = Java.type("javafx.scene.ParallelCamera");
+ParallelCameraBuilder = Java.type("javafx.scene.ParallelCameraBuilder");
+Parent = Java.type("javafx.scene.Parent");
+ParentBuilder = Java.type("javafx.scene.ParentBuilder");
+PerspectiveCamera = Java.type("javafx.scene.PerspectiveCamera");
+PerspectiveCameraBuilder = Java.type("javafx.scene.PerspectiveCameraBuilder");
+PointLight = Java.type("javafx.scene.PointLight");
+PointLightBuilder = Java.type("javafx.scene.PointLightBuilder");
+//Scene = Java.type("javafx.scene.Scene");
+SceneBuilder = Java.type("javafx.scene.SceneBuilder");
+Arc = Java.type("javafx.scene.shape.Arc");
+ArcBuilder = Java.type("javafx.scene.shape.ArcBuilder");
+ArcTo = Java.type("javafx.scene.shape.ArcTo");
+ArcToBuilder = Java.type("javafx.scene.shape.ArcToBuilder");
+ArcType = Java.type("javafx.scene.shape.ArcType");
+Box = Java.type("javafx.scene.shape.Box");
+BoxBuilder = Java.type("javafx.scene.shape.BoxBuilder");
+Circle = Java.type("javafx.scene.shape.Circle");
+CircleBuilder = Java.type("javafx.scene.shape.CircleBuilder");
+ClosePath = Java.type("javafx.scene.shape.ClosePath");
+ClosePathBuilder = Java.type("javafx.scene.shape.ClosePathBuilder");
+CubicCurve = Java.type("javafx.scene.shape.CubicCurve");
+CubicCurveBuilder = Java.type("javafx.scene.shape.CubicCurveBuilder");
+CubicCurveTo = Java.type("javafx.scene.shape.CubicCurveTo");
+CubicCurveToBuilder = Java.type("javafx.scene.shape.CubicCurveToBuilder");
+CullFace = Java.type("javafx.scene.shape.CullFace");
+Cylinder = Java.type("javafx.scene.shape.Cylinder");
+CylinderBuilder = Java.type("javafx.scene.shape.CylinderBuilder");
+DrawMode = Java.type("javafx.scene.shape.DrawMode");
+Ellipse = Java.type("javafx.scene.shape.Ellipse");
+EllipseBuilder = Java.type("javafx.scene.shape.EllipseBuilder");
+FillRule = Java.type("javafx.scene.shape.FillRule");
+HLineTo = Java.type("javafx.scene.shape.HLineTo");
+HLineToBuilder = Java.type("javafx.scene.shape.HLineToBuilder");
+Line = Java.type("javafx.scene.shape.Line");
+LineBuilder = Java.type("javafx.scene.shape.LineBuilder");
+LineTo = Java.type("javafx.scene.shape.LineTo");
+LineToBuilder = Java.type("javafx.scene.shape.LineToBuilder");
+Mesh = Java.type("javafx.scene.shape.Mesh");
+MeshView = Java.type("javafx.scene.shape.MeshView");
+MeshViewBuilder = Java.type("javafx.scene.shape.MeshViewBuilder");
+MoveTo = Java.type("javafx.scene.shape.MoveTo");
+MoveToBuilder = Java.type("javafx.scene.shape.MoveToBuilder");
+Path = Java.type("javafx.scene.shape.Path");
+PathBuilder = Java.type("javafx.scene.shape.PathBuilder");
+PathElement = Java.type("javafx.scene.shape.PathElement");
+PathElementBuilder = Java.type("javafx.scene.shape.PathElementBuilder");
+Polygon = Java.type("javafx.scene.shape.Polygon");
+PolygonBuilder = Java.type("javafx.scene.shape.PolygonBuilder");
+Polyline = Java.type("javafx.scene.shape.Polyline");
+PolylineBuilder = Java.type("javafx.scene.shape.PolylineBuilder");
+QuadCurve = Java.type("javafx.scene.shape.QuadCurve");
+QuadCurveBuilder = Java.type("javafx.scene.shape.QuadCurveBuilder");
+QuadCurveTo = Java.type("javafx.scene.shape.QuadCurveTo");
+QuadCurveToBuilder = Java.type("javafx.scene.shape.QuadCurveToBuilder");
+Rectangle = Java.type("javafx.scene.shape.Rectangle");
+RectangleBuilder = Java.type("javafx.scene.shape.RectangleBuilder");
+Shape = Java.type("javafx.scene.shape.Shape");
+Shape3D = Java.type("javafx.scene.shape.Shape3D");
+Shape3DBuilder = Java.type("javafx.scene.shape.Shape3DBuilder");
+ShapeBuilder = Java.type("javafx.scene.shape.ShapeBuilder");
+Sphere = Java.type("javafx.scene.shape.Sphere");
+SphereBuilder = Java.type("javafx.scene.shape.SphereBuilder");
+StrokeLineCap = Java.type("javafx.scene.shape.StrokeLineCap");
+StrokeLineJoin = Java.type("javafx.scene.shape.StrokeLineJoin");
+StrokeType = Java.type("javafx.scene.shape.StrokeType");
+SVGPath = Java.type("javafx.scene.shape.SVGPath");
+SVGPathBuilder = Java.type("javafx.scene.shape.SVGPathBuilder");
+TriangleMesh = Java.type("javafx.scene.shape.TriangleMesh");
+VLineTo = Java.type("javafx.scene.shape.VLineTo");
+VLineToBuilder = Java.type("javafx.scene.shape.VLineToBuilder");
+SnapshotParameters = Java.type("javafx.scene.SnapshotParameters");
+SnapshotParametersBuilder = Java.type("javafx.scene.SnapshotParametersBuilder");
+SnapshotResult = Java.type("javafx.scene.SnapshotResult");
+SubScene = Java.type("javafx.scene.SubScene");
+SubSceneBuilder = Java.type("javafx.scene.SubSceneBuilder");
+Font = Java.type("javafx.scene.text.Font");
+FontBuilder = Java.type("javafx.scene.text.FontBuilder");
+FontPosture = Java.type("javafx.scene.text.FontPosture");
+FontSmoothingType = Java.type("javafx.scene.text.FontSmoothingType");
+FontWeight = Java.type("javafx.scene.text.FontWeight");
+Text = Java.type("javafx.scene.text.Text");
+TextAlignment = Java.type("javafx.scene.text.TextAlignment");
+TextBoundsType = Java.type("javafx.scene.text.TextBoundsType");
+TextBuilder = Java.type("javafx.scene.text.TextBuilder");
+TextFlow = Java.type("javafx.scene.text.TextFlow");
+TextFlowBuilder = Java.type("javafx.scene.text.TextFlowBuilder");
+Affine = Java.type("javafx.scene.transform.Affine");
+AffineBuilder = Java.type("javafx.scene.transform.AffineBuilder");
+MatrixType = Java.type("javafx.scene.transform.MatrixType");
+NonInvertibleTransformException = Java.type("javafx.scene.transform.NonInvertibleTransformException");
+Rotate = Java.type("javafx.scene.transform.Rotate");
+RotateBuilder = Java.type("javafx.scene.transform.RotateBuilder");
+Scale = Java.type("javafx.scene.transform.Scale");
+ScaleBuilder = Java.type("javafx.scene.transform.ScaleBuilder");
+Shear = Java.type("javafx.scene.transform.Shear");
+ShearBuilder = Java.type("javafx.scene.transform.ShearBuilder");
+Transform = Java.type("javafx.scene.transform.Transform");
+TransformBuilder = Java.type("javafx.scene.transform.TransformBuilder");
+TransformChangedEvent = Java.type("javafx.scene.transform.TransformChangedEvent");
+Translate = Java.type("javafx.scene.transform.Translate");
+TranslateBuilder = Java.type("javafx.scene.transform.TranslateBuilder");
+DirectoryChooser = Java.type("javafx.stage.DirectoryChooser");
+DirectoryChooserBuilder = Java.type("javafx.stage.DirectoryChooserBuilder");
+FileChooser = Java.type("javafx.stage.FileChooser");
+FileChooser$ExtensionFilter = Java.type("javafx.stage.FileChooser$ExtensionFilter");
+FileChooserBuilder = Java.type("javafx.stage.FileChooserBuilder");
+Modality = Java.type("javafx.stage.Modality");
+Popup = Java.type("javafx.stage.Popup");
+PopupBuilder = Java.type("javafx.stage.PopupBuilder");
+PopupWindow = Java.type("javafx.stage.PopupWindow");
+PopupWindowBuilder = Java.type("javafx.stage.PopupWindowBuilder");
+Screen = Java.type("javafx.stage.Screen");
+//Stage = Java.type("javafx.stage.Stage");
+StageBuilder = Java.type("javafx.stage.StageBuilder");
+StageStyle = Java.type("javafx.stage.StageStyle");
+Window = Java.type("javafx.stage.Window");
+WindowBuilder = Java.type("javafx.stage.WindowBuilder");
+WindowEvent = Java.type("javafx.stage.WindowEvent");
+
diff --git a/src/jdk/nashorn/internal/runtime/resources/fx/media.js b/src/jdk/nashorn/internal/runtime/resources/fx/media.js
new file mode 100644
index 00000000..7cc7887b
--- /dev/null
+++ b/src/jdk/nashorn/internal/runtime/resources/fx/media.js
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+AudioClip = Java.type("javafx.scene.media.AudioClip");
+AudioClipBuilder = Java.type("javafx.scene.media.AudioClipBuilder");
+AudioEqualizer = Java.type("javafx.scene.media.AudioEqualizer");
+AudioSpectrumListener = Java.type("javafx.scene.media.AudioSpectrumListener");
+AudioTrack = Java.type("javafx.scene.media.AudioTrack");
+EqualizerBand = Java.type("javafx.scene.media.EqualizerBand");
+Media = Java.type("javafx.scene.media.Media");
+MediaBuilder = Java.type("javafx.scene.media.MediaBuilder");
+MediaErrorEvent = Java.type("javafx.scene.media.MediaErrorEvent");
+MediaException = Java.type("javafx.scene.media.MediaException");
+MediaException$Type = Java.type("javafx.scene.media.MediaException$Type");
+MediaMarkerEvent = Java.type("javafx.scene.media.MediaMarkerEvent");
+MediaPlayer = Java.type("javafx.scene.media.MediaPlayer");
+MediaPlayer$Status = Java.type("javafx.scene.media.MediaPlayer$Status");
+MediaPlayerBuilder = Java.type("javafx.scene.media.MediaPlayerBuilder");
+MediaView = Java.type("javafx.scene.media.MediaView");
+MediaViewBuilder = Java.type("javafx.scene.media.MediaViewBuilder");
+SubtitleTrack = Java.type("javafx.scene.media.SubtitleTrack");
+Track = Java.type("javafx.scene.media.Track");
+VideoTrack = Java.type("javafx.scene.media.VideoTrack");
diff --git a/src/jdk/nashorn/internal/runtime/resources/fx/swing.js b/src/jdk/nashorn/internal/runtime/resources/fx/swing.js
new file mode 100644
index 00000000..241205d1
--- /dev/null
+++ b/src/jdk/nashorn/internal/runtime/resources/fx/swing.js
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+JFXPanel = Java.type("javafx.embed.swing.JFXPanel");
+JFXPanelBuilder = Java.type("javafx.embed.swing.JFXPanelBuilder");
+SwingFXUtils = Java.type("javafx.embed.swing.SwingFXUtils");
+SwingNode = Java.type("javafx.embed.swing.SwingNode");
diff --git a/src/jdk/nashorn/internal/runtime/resources/fx/swt.js b/src/jdk/nashorn/internal/runtime/resources/fx/swt.js
new file mode 100644
index 00000000..526be750
--- /dev/null
+++ b/src/jdk/nashorn/internal/runtime/resources/fx/swt.js
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+CustomTransfer = Java.type("javafx.embed.swt.CustomTransfer");
+CustomTransferBuilder = Java.type("javafx.embed.swt.CustomTransferBuilder");
+FXCanvas = Java.type("javafx.embed.swt.FXCanvas");
+SWTFXUtils = Java.type("javafx.embed.swt.SWTFXUtils");
diff --git a/src/jdk/nashorn/internal/runtime/resources/fx/web.js b/src/jdk/nashorn/internal/runtime/resources/fx/web.js
new file mode 100644
index 00000000..d9099345
--- /dev/null
+++ b/src/jdk/nashorn/internal/runtime/resources/fx/web.js
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+HTMLEditor = Java.type("javafx.scene.web.HTMLEditor");
+HTMLEditorBuilder = Java.type("javafx.scene.web.HTMLEditorBuilder");
+PopupFeatures = Java.type("javafx.scene.web.PopupFeatures");
+PromptData = Java.type("javafx.scene.web.PromptData");
+PromptDataBuilder = Java.type("javafx.scene.web.PromptDataBuilder");
+WebEngine = Java.type("javafx.scene.web.WebEngine");
+WebEngineBuilder = Java.type("javafx.scene.web.WebEngineBuilder");
+WebEvent = Java.type("javafx.scene.web.WebEvent");
+WebHistory = Java.type("javafx.scene.web.WebHistory");
+WebView = Java.type("javafx.scene.web.WebView");
+WebViewBuilder = Java.type("javafx.scene.web.WebViewBuilder");
diff --git a/src/jdk/nashorn/internal/runtime/resources/mozilla_compat.js b/src/jdk/nashorn/internal/runtime/resources/mozilla_compat.js
index 0b967d69..206a193f 100644
--- a/src/jdk/nashorn/internal/runtime/resources/mozilla_compat.js
+++ b/src/jdk/nashorn/internal/runtime/resources/mozilla_compat.js
@@ -49,6 +49,7 @@ Object.defineProperty(this, "importPackage", {
var global = this;
var oldNoSuchProperty = global.__noSuchProperty__;
global.__noSuchProperty__ = function(name) {
+ 'use strict';
for (var i in _packages) {
try {
var type = Java.type(_packages[i] + "." + name);
@@ -57,7 +58,15 @@ Object.defineProperty(this, "importPackage", {
} catch (e) {}
}
- return oldNoSuchProperty? oldNoSuchProperty(name) : undefined;
+ if (oldNoSuchProperty) {
+ return oldNoSuchProperty.call(this, name);
+ } else {
+ if (this === undefined) {
+ throw new ReferenceError(name + " is not defined");
+ } else {
+ return undefined;
+ }
+ }
}
var prefix = "[JavaPackage ";
diff --git a/src/jdk/nashorn/tools/Shell.java b/src/jdk/nashorn/tools/Shell.java
index 0c040a2c..22d879f5 100644
--- a/src/jdk/nashorn/tools/Shell.java
+++ b/src/jdk/nashorn/tools/Shell.java
@@ -42,9 +42,12 @@ import java.util.ResourceBundle;
import jdk.nashorn.api.scripting.NashornException;
import jdk.nashorn.internal.codegen.Compiler;
import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.ir.debug.ASTWriter;
+import jdk.nashorn.internal.ir.debug.PrintVisitor;
import jdk.nashorn.internal.parser.Parser;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.ErrorManager;
+import jdk.nashorn.internal.runtime.Property;
import jdk.nashorn.internal.runtime.ScriptEnvironment;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
@@ -168,7 +171,11 @@ public class Shell {
return compileScripts(context, global, files);
}
- return runScripts(context, global, files);
+ if (env._fx) {
+ return runFXScripts(context, global, files);
+ } else {
+ return runScripts(context, global, files);
+ }
}
/**
@@ -254,6 +261,14 @@ public class Shell {
return COMPILATION_ERROR;
}
+ if (env._print_ast) {
+ context.getErr().println(new ASTWriter(functionNode));
+ }
+
+ if (env._print_parse) {
+ context.getErr().println(new PrintVisitor(functionNode));
+ }
+
//null - pass no code installer - this is compile only
new Compiler(env, functionNode).compile();
}
@@ -318,6 +333,46 @@ public class Shell {
}
/**
+ * Runs launches "fx:bootstrap.js" with the given JavaScript files provided
+ * as arguments.
+ *
+ * @param context the nashorn context
+ * @param global the global scope
+ * @param files the list of script files to provide
+ *
+ * @return error code
+ * @throws IOException when any script file read results in I/O error
+ */
+ private int runFXScripts(final Context context, final ScriptObject global, final List<String> files) throws IOException {
+ final ScriptObject oldGlobal = Context.getGlobal();
+ final boolean globalChanged = (oldGlobal != global);
+ try {
+ if (globalChanged) {
+ Context.setGlobal(global);
+ }
+
+ global.addOwnProperty("$GLOBAL", Property.NOT_ENUMERABLE, global);
+ global.addOwnProperty("$SCRIPTS", Property.NOT_ENUMERABLE, files);
+ context.load(global, "fx:bootstrap.js");
+ } catch (final NashornException e) {
+ context.getErrorManager().error(e.toString());
+ if (context.getEnv()._dump_on_error) {
+ e.printStackTrace(context.getErr());
+ }
+
+ return RUNTIME_ERROR;
+ } finally {
+ context.getOut().flush();
+ context.getErr().flush();
+ if (globalChanged) {
+ Context.setGlobal(oldGlobal);
+ }
+ }
+
+ return SUCCESS;
+ }
+
+ /**
* Hook to ScriptFunction "apply". A performance metering shell may
* introduce enter/exit timing here.
*
diff --git a/test/examples/int-micro.js b/test/examples/int-micro.js
new file mode 100644
index 00000000..82f4b099
--- /dev/null
+++ b/test/examples/int-micro.js
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of Oracle nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+function bench(name, func) {
+ var start = Date.now();
+ for (var iter = 0; iter < 5000000; iter++) {
+ func();
+ }
+ print(name + "\t" + (Date.now() - start));
+}
+
+function uint32(value) {
+ return function() {
+ value >>> 0;
+ value >>> 0;
+ value >>> 0;
+ value >>> 0;
+ value >>> 0;
+ value >>> 0;
+ value >>> 0;
+ value >>> 0;
+ value >>> 0;
+ value >>> 0;
+ value >>> 0;
+ value >>> 0;
+ value >>> 0;
+ value >>> 0;
+ value >>> 0;
+ };
+}
+
+function int32(value) {
+ return function() {
+ value >> 0;
+ value >> 0;
+ value >> 0;
+ value >> 0;
+ value >> 0;
+ value >> 0;
+ value >> 0;
+ value >> 0;
+ value >> 0;
+ value >> 0;
+ value >> 0;
+ value >> 0;
+ value >> 0;
+ value >> 0;
+ value >> 0;
+ };
+}
+
+print("\nToUint32");
+for (var i = 1; i < 3; i++) {
+ bench("infinity ", uint32(Infinity));
+ bench("infinity neg ", uint32(-Infinity));
+ bench("nan ", uint32(NaN));
+ bench("small ", uint32(1));
+ bench("small neg ", uint32(-1));
+ bench("small frac ", uint32(1.5));
+ bench("small neg frac", uint32(-1.5));
+ bench("large ", uint32(9223372036854775807));
+ bench("large neg ", uint32(-9223372036854775808));
+}
+
+print("\nToInt32");
+for (var i = 1; i < 3; i++) {
+ bench("infinity ", int32(Infinity));
+ bench("infinity neg ", int32(-Infinity));
+ bench("nan ", int32(NaN));
+ bench("small ", int32(1));
+ bench("small neg ", int32(-1));
+ bench("small frac ", int32(1.5));
+ bench("small neg frac", int32(-1.5));
+ bench("large ", int32(9223372036854775807));
+ bench("large neg ", int32(-9223372036854775808));
+}
+
+
diff --git a/test/script/basic/JDK-8008238.js b/test/script/basic/JDK-8008238.js
new file mode 100644
index 00000000..f58ec20a
--- /dev/null
+++ b/test/script/basic/JDK-8008238.js
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+/**
+ * JDK-8008238: Labeled break in finally causes stack overflow in Node copy
+ *
+ * @test
+ * @run
+ */
+
+a: try {
+} finally {
+ break a;
+}
diff --git a/test/script/basic/JDK-8008814-3.js b/test/script/basic/JDK-8008814-3.js
new file mode 100644
index 00000000..87a9ebf4
--- /dev/null
+++ b/test/script/basic/JDK-8008814-3.js
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+/**
+ * NASHORN-8008814: it's not a compile time error to have a nested strict function declaration when the outer one is not strict
+ *
+ * @test
+ * @run
+ */
+
+function f() {
+ if(true) {
+ function g() {
+ "use strict";
+ print("g invoked!")
+ }
+ }
+ g()
+}
+f()
diff --git a/test/script/basic/JDK-8008814-3.js.EXPECTED b/test/script/basic/JDK-8008814-3.js.EXPECTED
new file mode 100644
index 00000000..dab29578
--- /dev/null
+++ b/test/script/basic/JDK-8008814-3.js.EXPECTED
@@ -0,0 +1 @@
+g invoked!
diff --git a/test/script/basic/JDK-8008814-4.js b/test/script/basic/JDK-8008814-4.js
new file mode 100644
index 00000000..baca0221
--- /dev/null
+++ b/test/script/basic/JDK-8008814-4.js
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+/**
+ * NASHORN-8008814: it's not a compile time error to have a nested function declaration when warnings are reported
+ *
+ * @option --function-statement-warning
+ * @test
+ * @run/ignore-std-error
+ */
+
+function f() {
+ if(true) {
+ function g() {
+ print("g invoked!")
+ }
+ }
+ g()
+}
+f()
diff --git a/test/script/basic/JDK-8008814-4.js.EXPECTED b/test/script/basic/JDK-8008814-4.js.EXPECTED
new file mode 100644
index 00000000..dab29578
--- /dev/null
+++ b/test/script/basic/JDK-8008814-4.js.EXPECTED
@@ -0,0 +1 @@
+g invoked!
diff --git a/test/script/basic/JDK-8009230.js b/test/script/basic/JDK-8009230.js
new file mode 100644
index 00000000..f161158a
--- /dev/null
+++ b/test/script/basic/JDK-8009230.js
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ */
+
+/**
+ * JDK-8009230: Nashorn rejects extended RegExp syntax accepted by all major JS engines
+ *
+ * @test
+ * @run
+ */
+
+
+// Invalid ControlEscape/IdentityEscape character treated as literal.
+print(/\z/.exec("z")); // Invalid escape, same as /z/
+// Incomplete/Invalid ControlEscape treated as "\\c"
+print(/\c/.exec("\\c")); // same as /\\c/
+print(/\c2/.exec("\\c2")); // same as /\\c2/
+print(/\C/.exec("C")); // same as /C/
+print(/\C2/.exec("C2")); // same as /C2/
+// Incomplete HexEscapeSequence escape treated as "x".
+print(/\x/.exec("x")); // incomplete x-escape
+print(/\x1/.exec("x1")); // incomplete x-escape
+print(/\x1z/.exec("x1z")); // incomplete x-escape
+// Incomplete UnicodeEscapeSequence escape treated as "u".
+print(/\u/.exec("u")); // incomplete u-escape
+print(/\uz/.exec("uz")); // incomplete u-escape
+print(/\u1/.exec("u1")); // incomplete u-escape
+print(/\u1z/.exec("u1z")); // incomplete u-escape
+print(/\u12/.exec("u12")); // incomplete u-escape
+print(/\u12z/.exec("u12z")); // incomplete u-escape
+print(/\u123/.exec("u123")); // incomplete u-escape
+print(/\u123z/.exec("u123z")); // incomplete u-escape
+// Bad quantifier range:
+print(/x{z/.exec("x{z")); // same as /x\{z/
+print(/x{1z/.exec("x{1z")); // same as /x\{1z/
+print(/x{1,z/.exec("x{1,z")); // same as /x\{1,z/
+print(/x{1,2z/.exec("x{1,2z")); // same as /x\{1,2z/
+print(/x{10000,20000z/.exec("x{10000,20000z")); // same as /x\{10000,20000z/
+// Notice: It needs arbitrary lookahead to determine the invalidity,
+// except Mozilla that limits the numbers.
+
+// Zero-initialized Octal escapes.
+/\012/; // same as /\x0a/
+
+// Nonexisting back-references smaller than 8 treated as octal escapes:
+print(/\5/.exec("\u0005")); // same as /\x05/
+print(/\7/.exec("\u0007")); // same as /\x07/
+print(/\8/.exec("\u0008")); // does not match
+
+// Invalid PatternCharacter accepted unescaped
+print(/]/.exec("]"));
+print(/{/.exec("{"));
+print(/}/.exec("}"));
+
+// Bad escapes also inside CharacterClass.
+print(/[\z]/.exec("z"));
+print(/[\c]/.exec("c"));
+print(/[\c2]/.exec("c"));
+print(/[\x]/.exec("x"));
+print(/[\x1]/.exec("x1"));
+print(/[\x1z]/.exec("x1z"));
+print(/[\u]/.exec("u"));
+print(/[\uz]/.exec("u"));
+print(/[\u1]/.exec("u"));
+print(/[\u1z]/.exec("u"));
+print(/[\u12]/.exec("u"));
+print(/[\u12z]/.exec("u"));
+print(/[\u123]/.exec("u"));
+print(/[\u123z]/.exec("u"));
+print(/[\012]/.exec("0"));
+print(/[\5]/.exec("5"));
+// And in addition:
+print(/[\B]/.exec("B"));
+print(/()()[\2]/.exec("")); // Valid backreference should be invalid.
diff --git a/test/script/basic/JDK-8009230.js.EXPECTED b/test/script/basic/JDK-8009230.js.EXPECTED
new file mode 100644
index 00000000..63f6e615
--- /dev/null
+++ b/test/script/basic/JDK-8009230.js.EXPECTED
@@ -0,0 +1,45 @@
+z
+\c
+\c2
+C
+C2
+x
+x1
+x1z
+u
+uz
+u1
+u1z
+u12
+u12z
+u123
+u123z
+x{z
+x{1z
+x{1,z
+x{1,2z
+x{10000,20000z
+
+
+null
+]
+{
+}
+z
+c
+null
+x
+x
+x
+u
+u
+u
+u
+u
+u
+u
+u
+null
+null
+B
+null
diff --git a/test/script/basic/JDK-8017010.js b/test/script/basic/JDK-8010710.js
index aa6e61a9..aa6e61a9 100644
--- a/test/script/basic/JDK-8017010.js
+++ b/test/script/basic/JDK-8010710.js
diff --git a/test/script/basic/JDK-8017010.js.EXPECTED b/test/script/basic/JDK-8010710.js.EXPECTED
index 296c81e5..296c81e5 100644
--- a/test/script/basic/JDK-8017010.js.EXPECTED
+++ b/test/script/basic/JDK-8010710.js.EXPECTED
diff --git a/test/script/basic/JDK-8010924.js b/test/script/basic/JDK-8010924.js
new file mode 100644
index 00000000..aaa7d129
--- /dev/null
+++ b/test/script/basic/JDK-8010924.js
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+/**
+ * JDK-8010924: Dealing with undefined property gets you a fatal stack
+ *
+ * @test
+ * @run
+ * @option -scripting
+ */
+
+load("nashorn:mozilla_compat.js");
+
+if (this.non_existent_foo !== undefined) {
+ fail("this.non_existent_foo is defined!");
+}
+
+try {
+ non_existent_foo;
+ fail("should have thrown ReferenceError");
+} catch (e) {
+ if (! (e instanceof ReferenceError)) {
+ fail("ReferenceError expected, got " + e);
+ }
+}
+
+// try the same via script engine
+
+var ScriptEngineManager = Java.type("javax.script.ScriptEngineManager");
+var engine = new ScriptEngineManager().getEngineByName("nashorn");
+
+engine.eval("load('nashorn:mozilla_compat.js')");
+
+if (! engine.eval("this.non_existent_foo === undefined")) {
+ fail("this.non_existent_foo is not undefined");
+}
+
+engine.eval(<<EOF
+ try {
+ non_existent_foo;
+ throw new Error("should have thrown ReferenceError");
+ } catch (e) {
+ if (! (e instanceof ReferenceError)) {
+ throw new Error("ReferenceError expected, got " + e);
+ }
+ }
+EOF);
diff --git a/test/script/basic/JDK-8011209.js b/test/script/basic/JDK-8011209.js
new file mode 100644
index 00000000..9098dfaa
--- /dev/null
+++ b/test/script/basic/JDK-8011209.js
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+/**
+ * JDK-8011209: Object.getOwnPropertyDescriptor(function(){"use strict"},"caller").get.length is not 0
+ *
+ * @test
+ * @run
+ */
+
+var callerPropDesc = Object.getOwnPropertyDescriptor(function(){"use strict"},"caller");
+
+var getterLen = callerPropDesc.get.length;
+if (getterLen != 0) {
+ fail("caller's get.length != 0");
+}
+
+var setterLen = callerPropDesc.set.length;
+if (setterLen != 0) {
+ fail("caller's set.length != 1");
+}
+
+var argumentsPropDesc = Object.getOwnPropertyDescriptor(function(){"use strict"},"arguments");
+
+getterLen = argumentsPropDesc.get.length;
+if (getterLen != 0) {
+ fail("arguments's get.length != 0");
+}
+
+setterLen = argumentsPropDesc.set.length;
+if (setterLen != 0) {
+ fail("arguments's set.length != 1");
+}
+
+var strictArgs = (function() { 'use strict'; return arguments; })();
+callerPropDesc = Object.getOwnPropertyDescriptor(strictArgs,"caller");
+getterLen = callerPropDesc.get.length;
+if (getterLen != 0) {
+ fail("argument.caller's get.length != 0");
+}
+
+setterLen = callerPropDesc.set.length;
+if (setterLen != 0) {
+ fail("argument.caller's set.length != 1");
+}
+
+calleePropDesc = Object.getOwnPropertyDescriptor(strictArgs,"callee");
+getterLen = calleePropDesc.get.length;
+if (getterLen != 0) {
+ fail("argument.callee's get.length != 0");
+}
+
+setterLen = calleePropDesc.set.length;
+if (setterLen != 0) {
+ fail("argument.callee's set.length != 1");
+}
diff --git a/test/script/basic/JDK-8011237.js b/test/script/basic/JDK-8011237.js
new file mode 100644
index 00000000..e9e521a4
--- /dev/null
+++ b/test/script/basic/JDK-8011237.js
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+/**
+ * JDK-8011237: Object.isExtensible(Object.getOwnPropertyDescriptor(function(){"use strict"},"caller").get) should be false
+ *
+ * @test
+ * @run
+ */
+
+// ECMA Section 13.2.3 The [[ThrowTypeError]] Function Object
+// 11. Set the [[Extensible]] internal property of F to false
+
+var strictFunc = (function() { 'use strict' });
+var strictFuncCallerDesc = Object.getOwnPropertyDescriptor(strictFunc, "caller")
+var isExtensible = Object.isExtensible(strictFuncCallerDesc.get);
+if (isExtensible) {
+ fail("strict function caller's getter is extensible!");
+}
diff --git a/test/script/basic/JDK-8011274.js b/test/script/basic/JDK-8011274.js
new file mode 100644
index 00000000..b483c3e0
--- /dev/null
+++ b/test/script/basic/JDK-8011274.js
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+/**
+ * JDK-8011324: Object.getOwnPropertyDescriptor(function(){"use strict"},"caller").get.hasOwnProperty("prototype") should be false
+ *
+ * @test
+ * @run
+ */
+
+var strictFunc = (function() { 'use strict' });
+var desc = Object.getOwnPropertyDescriptor(strictFunc, "caller");
+if (desc.get.hasOwnProperty("prototype")) {
+ fail("strict function's caller getter has 'prototype' property");
+}
+
+// try few built-ins
+if (parseInt.hasOwnProperty("prototype")) {
+ fail("parseInt.hasOwnProperty('prototype') is true");
+}
+
+if (parseFloat.hasOwnProperty("prototype")) {
+ fail("parseFloat.hasOwnProperty('prototype') is true");
+}
+
+if (isFinite.hasOwnProperty("prototype")) {
+ fail("isFinite.hasOwnProperty('prototype') is true");
+}
diff --git a/test/script/basic/JDK-8011357.js b/test/script/basic/JDK-8011357.js
new file mode 100644
index 00000000..40efec5d
--- /dev/null
+++ b/test/script/basic/JDK-8011357.js
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+/**
+ * JDK-8011357: Array.prototype.slice and Array.prototype.splice should not call user defined valueOf of start, end arguments more than once
+ *
+ * @test
+ * @run
+ */
+
+var startValueOf = 0;
+var endValueOf = 0;
+
+[].slice(
+ {
+ valueOf: function() {
+ startValueOf++;
+ }
+ },
+ {
+ valueOf: function() {
+ endValueOf++;
+ }
+ }
+);
+
+if (startValueOf !== 1) {
+ fail("Array.prototype.slice should call valueOf on start arg once");
+}
+
+if (endValueOf !== 1) {
+ fail("Array.prototype.slice should call valueOf on end arg once");
+}
+
+startValueOf = 0;
+
+[].splice(
+ {
+ valueOf: function() {
+ startValueOf++;
+ }
+ }
+);
+
+if (startValueOf !== 1) {
+ fail("Array.prototype.splice should call valueOf on start arg once");
+}
+
diff --git a/test/script/basic/JDK-8011362.js b/test/script/basic/JDK-8011362.js
new file mode 100644
index 00000000..bda4851a
--- /dev/null
+++ b/test/script/basic/JDK-8011362.js
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+/**
+ * JDK-8011362: Overloaded method resolution foiled by nulls
+ *
+ * @test
+ * @run
+ */
+
+var subject = new (Java.type("jdk.nashorn.test.models.Jdk8011362TestSubject"))
+
+print(subject.overloaded("", null))
+print(subject.overloaded(0, null))
diff --git a/test/script/basic/JDK-8011362.js.EXPECTED b/test/script/basic/JDK-8011362.js.EXPECTED
new file mode 100644
index 00000000..e0eb4dc9
--- /dev/null
+++ b/test/script/basic/JDK-8011362.js.EXPECTED
@@ -0,0 +1,2 @@
+overloaded(String, String)
+overloaded(Double, Double)
diff --git a/test/script/basic/JDK-8011365.js b/test/script/basic/JDK-8011365.js
new file mode 100644
index 00000000..30802576
--- /dev/null
+++ b/test/script/basic/JDK-8011365.js
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+/**
+ * JDK-8011365: Array.prototype.join and Array.prototype.toString do not throw TypeError on null, undefined
+ *
+ * @test
+ * @run
+ */
+
+try {
+ Array.prototype.join.call(null, { toString:function() { throw 2 } });
+ fail("should have thrown TypeError");
+} catch (e) {
+ if (! (e instanceof TypeError)) {
+ fail("TypeError expected, got " + e);
+ }
+}
+
+// check all Array.prototype functions to be sure
+var names = Object.getOwnPropertyNames(Array.prototype);
+
+for (var n in names) {
+ var funcName = names[n];
+ // ignore constructor
+ if (funcName == "constructor") {
+ continue;
+ }
+
+ var prop = Array.prototype[funcName];
+ if (prop instanceof Function) {
+ // try 'null' this
+ try {
+ prop.call(null);
+ fail(funcName + " does not throw TypeError on 'null' this");
+ } catch (e) {
+ if (! (e instanceof TypeError)) {
+ fail("TypeError expected from " + funcName + ", got " + e);
+ }
+ }
+
+ // try 'undefined' this
+ try {
+ prop.call(undefined);
+ fail(funcName + " does not throw TypeError on 'undefined' this");
+ } catch (e) {
+ if (! (e instanceof TypeError)) {
+ fail("TypeError expected from " + funcName + ", got " + e);
+ }
+ }
+ }
+}
diff --git a/test/script/basic/JDK-8011382.js b/test/script/basic/JDK-8011382.js
new file mode 100644
index 00000000..13b3a771
--- /dev/null
+++ b/test/script/basic/JDK-8011382.js
@@ -0,0 +1,115 @@
+/*
+ * 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.
+ */
+
+/**
+ * JDK-8011382: Data prototype methods and constructor do not call user defined toISOString, valueOf methods per spec.
+ *
+ * @test
+ * @run
+ */
+
+var yearValueOf = 0;
+var monthValueOf = 0;
+var dayValueOf = 0;
+
+var d = new Date(
+ {
+ valueOf: function() { yearValueOf++; return NaN; }
+ },
+ {
+ valueOf: function() { monthValueOf++; return NaN; }
+ },
+ {
+ valueOf: function() { dayValueOf++; return NaN; }
+ }
+);
+
+if (yearValueOf !== 1) {
+ fail("Date constructor does not call valueOf on year argument once");
+}
+
+if (monthValueOf !== 1) {
+ fail("Date constructor does not call valueOf on month argument once");
+}
+
+if (dayValueOf !== 1) {
+ fail("Date constructor does not call valueOf on day argument once");
+}
+
+yearValueOf = 0;
+monthValueOf = 0;
+dayValueOf = 0;
+
+d = new Date();
+
+d.setFullYear(
+ {
+ valueOf: function() { yearValueOf++; return NaN; }
+ },
+ {
+ valueOf: function() { monthValueOf++; return NaN; }
+ },
+ {
+ valueOf: function() { dayValueOf++; return NaN; }
+ }
+);
+
+if (yearValueOf !== 1) {
+ fail("Date setFullYear does not call valueOf on year argument once");
+}
+
+if (monthValueOf !== 1) {
+ fail("Date setFullYear does not call valueOf on month argument once");
+}
+
+if (dayValueOf !== 1) {
+ fail("Date setFullYear does not call valueOf on day argument once");
+}
+
+// check toJSON calls toISOString override
+var toISOStringCalled = 0;
+d = new Date();
+d.toISOString = function() {
+ toISOStringCalled++;
+};
+
+d.toJSON();
+if (toISOStringCalled !== 1) {
+ fail("toISOString was not called by Date.prototype.toJSON once");
+}
+
+toISOStringCalled = 0;
+
+// toJSON is generic - try for non-Date object
+Date.prototype.toJSON.call({
+ toISOString: function() {
+ toISOStringCalled++;
+ },
+ valueOf: function() {
+ return 12;
+ }
+});
+
+if (toISOStringCalled !== 1) {
+ fail("toISOString was not called by Date.prototype.toJSON once");
+}
diff --git a/test/script/basic/JDK-8011394.js b/test/script/basic/JDK-8011394.js
new file mode 100644
index 00000000..070f5d3c
--- /dev/null
+++ b/test/script/basic/JDK-8011394.js
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+/**
+ * JDK-8011394: RegExp.prototype.test() does not call valueOf on lastIndex property as per the spec.
+ *
+ * @test
+ * @run
+ */
+
+var re = new RegExp();
+var lastIndexValueOfCalled = false;
+
+re.lastIndex = {
+ valueOf: function() {
+ lastIndexValueOfCalled = true;
+ return 0;
+ }
+};
+
+re.test("");
+
+if (! lastIndexValueOfCalled) {
+ fail("RegExp.prototype.test() does not call 'valueOf' on 'lastIndex'");
+}
diff --git a/test/script/basic/JDK-8011421.js b/test/script/basic/JDK-8011421.js
new file mode 100644
index 00000000..c1acef4b
--- /dev/null
+++ b/test/script/basic/JDK-8011421.js
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+/**
+ * JDK-8011421: When using Object.defineProperty on arrays, PropertyDescriptor's property accessors are invoked multiple times
+ *
+ * @test
+ * @run
+ */
+
+var configurableGetterCalled = 0;
+
+// create a property descriptor object with "configurable"
+// property with a user defined getter
+var propDesc = Object.defineProperty({},
+ "configurable",
+ {
+ get: function() {
+ configurableGetterCalled++;
+ return false
+ }
+ }
+);
+
+// make array length non-configurable
+Object.defineProperty([], "length", propDesc);
+
+// above should have called "configurable" getter only once
+if (configurableGetterCalled !== 1) {
+ fail("defineProperty on array should call propDesc getters only once");
+}
diff --git a/test/script/basic/JDK-8011543.js b/test/script/basic/JDK-8011543.js
new file mode 100644
index 00000000..49baef71
--- /dev/null
+++ b/test/script/basic/JDK-8011543.js
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+/**
+ * JDK-8011543: "".split(undefined,{valueOf:function(){throw 2}}) does not throw exception
+ *
+ * @test
+ * @run
+ */
+
+try {
+ "".split(undefined,{
+ valueOf: function() {
+ throw 42;
+ }
+ });
+ fail("should have thrown 42");
+} catch (e) {
+ if (e != 42) {
+ fail("expected 42 to be thrown");
+ }
+}
diff --git a/test/script/basic/JDK-8011552.js b/test/script/basic/JDK-8011552.js
new file mode 100644
index 00000000..f0735a00
--- /dev/null
+++ b/test/script/basic/JDK-8011552.js
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+/**
+ * JDK-8011552: Arrays with missing elements are not properly sorted
+ *
+ * @test
+ * @run
+ */
+
+if ([,void 0].sort().hasOwnProperty("1")) {
+ fail("missing element found in sorted array");
+}
+
+if ([1,,2,,-1].sort().toString() != "-1,1,2,,") {
+ faiil("missing elements are not at the end of sorted array");
+}
diff --git a/test/script/basic/JDK-8011555.js b/test/script/basic/JDK-8011555.js
new file mode 100644
index 00000000..c65ad612
--- /dev/null
+++ b/test/script/basic/JDK-8011555.js
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+/**
+ * JDK-8011555: Invalid class name in with block with JavaImporter causes MH type mismatch
+ *
+ * @test
+ * @run
+ */
+
+with(new JavaImporter()) {
+ try {
+ new X()
+ print("Expected to fail!")
+ } catch(e) {
+ // We expect to get a TypeError for trying to use __noSuchMethod__ as
+ // a constructor. Before we fixed this bug, we were getting a runtime
+ // exception with MH type mismatch on a MH.foldArguments within the
+ // WithObject code instead.
+ print(e)
+ }
+}
diff --git a/test/script/basic/JDK-8011555.js.EXPECTED b/test/script/basic/JDK-8011555.js.EXPECTED
new file mode 100644
index 00000000..c296217a
--- /dev/null
+++ b/test/script/basic/JDK-8011555.js.EXPECTED
@@ -0,0 +1 @@
+TypeError: function __noSuchMethod__() { [native code] } is not a constructor function
diff --git a/test/script/basic/JDK-8011578.js b/test/script/basic/JDK-8011578.js
new file mode 100644
index 00000000..9a2f7438
--- /dev/null
+++ b/test/script/basic/JDK-8011578.js
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+/**
+ * JDK-8011578 : -Dnashorn.unstable.relink.threshold=1 causes tests to fail.
+ *
+ * @test
+ * @option -Dnashorn.unstable.relink.threshold=1
+ * @run
+ */
+
+load(__DIR__ + "NASHORN-296.js");
+load(__DIR__ + "NASHORN-691.js");
+load(__DIR__ + "calllink.js");
+load(__DIR__ + "nosuchproperty.js");
+
+__noSuchProperty__ = function(x) {
+ print(x);
+ return x;
+}
+
+print(this["find"]);
diff --git a/test/script/basic/JDK-8011578.js.EXPECTED b/test/script/basic/JDK-8011578.js.EXPECTED
new file mode 100644
index 00000000..aa7184a7
--- /dev/null
+++ b/test/script/basic/JDK-8011578.js.EXPECTED
@@ -0,0 +1,22 @@
+o.foo = 33
+o.foo = 44
+o.foo = 3
+o.foo = hello
+obj1.func called
+obj2.func called
+no such method: func
+obj4's prototype func called
+MyConstructor.prototype.func
+MyConstructor.prototype.func
+obj1.func called
+obj2.func called
+new obj3.func called
+new obj4.func called
+all new MyConstructor.prototype.func
+all new MyConstructor.prototype.func
+obj.__noSuchProperty__ for foo
+new obj.__noSuchProperty__ for foo
+proto.__noSuchProperty__ for foo
+new proto.__noSuchProperty__ for foo
+find
+find
diff --git a/test/script/basic/JDK-8011714.js b/test/script/basic/JDK-8011714.js
new file mode 100644
index 00000000..dac00743
--- /dev/null
+++ b/test/script/basic/JDK-8011714.js
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+/**
+ * JDK-8011714: Regexp decimal escape handling still not correct
+ *
+ * @test
+ * @run
+ */
+
+// \0 should be interpreted as <NUL> character here
+print(/\08/.test("\x008"));
+print(/[\08]/.test("8"));
+print(/[\08]/.test("\x00"));
+
+// Can't be converted to octal thus encoded as literal char sequence
+print(/\8/.exec("\\8"));
+print(/[\8]/.exec("\\"));
+print(/[\8]/.exec("8"));
+
+// 0471 is too high for an octal escape so it is \047 outside a character class
+// and \\471 inside a character class
+print(/\471/.exec("\x271"));
+print(/[\471]/.exec("1"));
+print(/[\471]/.exec("\x27"));
+
+// 0366 is a valid octal escape (246)
+print(/\366/.test("\xf6"));
+print(/[\366]/.test("\xf6"));
+print(/[\366]/.test("\xf6"));
+
+// more tests for conversion of invalid backreferences to octal escapes or literals
+print(/(a)(b)(c)(d)\4/.exec("abcdd"));
+print(/(a)(b)(c)(d)\4x/.exec("abcddx"));
+print(/(a)(b)(c)(d)\47/.exec("abcdd7"));
+print(/(a)(b)(c)(d)\47/.exec("abcd\x27"));
+print(/(a)(b)(c)(d)\47xyz/.exec("abcd\x27xyz"));
+print(/(a)(b)(c)(d)[\47]/.exec("abcd\x27"));
+print(/(a)(b)(c)(d)[\47]xyz/.exec("abcd\x27xyz"));
+print(/(a)(b)(c)(d)\48/.exec("abcd\x048"));
+print(/(a)(b)(c)(d)\48xyz/.exec("abcd\x048xyz"));
+print(/(a)(b)(c)(d)[\48]/.exec("abcd\x04"));
+print(/(a)(b)(c)(d)[\48]xyz/.exec("abcd\x04xyz"));
+print(/(a)(b)(c)(d)\84/.exec("abcd84"));
+print(/(a)(b)(c)(d)\84xyz/.exec("abcd84xyz"));
+print(/(a)(b)(c)(d)[\84]/.exec("abcd8"));
+print(/(a)(b)(c)(d)[\84]xyz/.exec("abcd8xyz"));
+
diff --git a/test/script/basic/JDK-8011714.js.EXPECTED b/test/script/basic/JDK-8011714.js.EXPECTED
new file mode 100644
index 00000000..c5a8b345
--- /dev/null
+++ b/test/script/basic/JDK-8011714.js.EXPECTED
@@ -0,0 +1,27 @@
+true
+true
+true
+8
+null
+8
+'1
+1
+'
+true
+true
+true
+abcdd,a,b,c,d
+abcddx,a,b,c,d
+null
+abcd',a,b,c,d
+abcd'xyz,a,b,c,d
+abcd',a,b,c,d
+abcd'xyz,a,b,c,d
+abcd8,a,b,c,d
+abcd8xyz,a,b,c,d
+abcd,a,b,c,d
+abcdxyz,a,b,c,d
+abcd84,a,b,c,d
+abcd84xyz,a,b,c,d
+abcd8,a,b,c,d
+abcd8xyz,a,b,c,d
diff --git a/test/script/basic/JDK-8011749.js b/test/script/basic/JDK-8011749.js
new file mode 100644
index 00000000..41d9ac64
--- /dev/null
+++ b/test/script/basic/JDK-8011749.js
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+/**
+ * JDK-8011749: Bugs with empty character class handling
+ *
+ * @test
+ * @run
+ */
+
+// empty class in alternative
+print(/[]|[^]/.exec("a"));
+print(/[]|[]/.test("a"));
+print(/[]|[]|[a]/.exec("a"));
+
+// empty class in negative lookahead
+print(/(?![])/.test(""));
+print(/(?![])./.exec("a"));
diff --git a/test/script/basic/JDK-8011749.js.EXPECTED b/test/script/basic/JDK-8011749.js.EXPECTED
new file mode 100644
index 00000000..82d8c103
--- /dev/null
+++ b/test/script/basic/JDK-8011749.js.EXPECTED
@@ -0,0 +1,5 @@
+a
+false
+a
+true
+a
diff --git a/test/script/basic/JDK-8011756.js b/test/script/basic/JDK-8011756.js
new file mode 100644
index 00000000..c0e8d834
--- /dev/null
+++ b/test/script/basic/JDK-8011756.js
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+/**
+ * JDK-8011756: Wrong characters supported in RegExp \c escape
+ *
+ * @test
+ * @run
+ */
+
+
+// Invalid control letters should be escaped:
+print(/\cı/.test("\x09"));
+print(/\cı/.test("\\cı"));
+
+print(/\cſ/.test("\x13"));
+print(/\cſ/.test("\\cſ"));
+
+print(/[\cſ]/.test("\x13"));
+print(/[\cſ]/.test("\\"));
+print(/[\cſ]/.test("c"));
+print(/[\cſ]/.test("ſ"));
+
+print(/[\c#]/.test("\\"));
+print(/[\c#]/.test("c"));
+print(/[\c#]/.test("#"));
+
+// The characters that are supported by other engines are '0'-'9', '_':
+print(/[\c0]/.test("\x10"));
+print(/[\c1]/.test("\x11"));
+print(/[\c2]/.test("\x12"));
+print(/[\c3]/.test("\x13"));
+print(/[\c4]/.test("\x14"));
+print(/[\c5]/.test("\x15"));
+print(/[\c6]/.test("\x16"));
+print(/[\c7]/.test("\x17"));
+print(/[\c8]/.test("\x18"));
+print(/[\c9]/.test("\x19"));
+print(/[\c_]/.test("\x1F"));
diff --git a/test/script/basic/JDK-8011756.js.EXPECTED b/test/script/basic/JDK-8011756.js.EXPECTED
new file mode 100644
index 00000000..63722a57
--- /dev/null
+++ b/test/script/basic/JDK-8011756.js.EXPECTED
@@ -0,0 +1,22 @@
+false
+true
+false
+true
+false
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
diff --git a/test/script/basic/JDK-8011960.js b/test/script/basic/JDK-8011960.js
new file mode 100644
index 00000000..f691d2c8
--- /dev/null
+++ b/test/script/basic/JDK-8011960.js
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+/**
+ * JDK-8011960: [2,1].sort(null) should throw TypeError
+ *
+ * @test
+ * @run
+ */
+
+function check(func) {
+ try {
+ [2,1].sort(func);
+ fail("should have thrown TypeError for :" + func);
+ } catch (e) {
+ if (! (e instanceof TypeError)) {
+ fail("TypeError not thrown for sort comparefn: " + func);
+ }
+ }
+}
+
+// should not result in TypeError for undefined
+[1, 2].sort(undefined);
+
+// TypeError for null
+check(null);
+
+// should result in TypeError other non-callable params
+check(32);
+check("foo");
+check(false);
+check({});
+check([]);
diff --git a/test/script/basic/JDK-8011974.js b/test/script/basic/JDK-8011974.js
new file mode 100644
index 00000000..f3f9b4ae
--- /dev/null
+++ b/test/script/basic/JDK-8011974.js
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+/**
+ * JDK-8011974: Comparator function returning negative and positive Infinity does not work as expected with Array.prototype.sort
+ *
+ * @test
+ * @run
+ */
+
+function compare(x, y) {
+ return x < y? -Infinity : (x > y? Infinity: 0)
+}
+
+var sorted = [5, 4, 3, 2, 1].sort(compare);
+
+if (sorted + '' != "1,2,3,4,5") {
+ fail("Array.prototype.sort does not work when compare returns +/-Infinity");
+}
diff --git a/test/script/basic/JDK-8011980.js b/test/script/basic/JDK-8011980.js
new file mode 100644
index 00000000..a83f2864
--- /dev/null
+++ b/test/script/basic/JDK-8011980.js
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+/**
+ * JDK-8011980: Allow NUL character in character class
+ *
+ * @test
+ * @run
+ */
+
+print(RegExp("\0").test("\0"));
+print(RegExp("[\0]").test("\0"));
+print(RegExp("[\x00]").test("\0"));
+print(RegExp("[\u0000]").test("\0"));
diff --git a/test/script/basic/JDK-8011980.js.EXPECTED b/test/script/basic/JDK-8011980.js.EXPECTED
new file mode 100644
index 00000000..1140ff52
--- /dev/null
+++ b/test/script/basic/JDK-8011980.js.EXPECTED
@@ -0,0 +1,4 @@
+true
+true
+true
+true
diff --git a/test/script/basic/JDK-8012240.js b/test/script/basic/JDK-8012240.js
new file mode 100644
index 00000000..2ac6eaf7
--- /dev/null
+++ b/test/script/basic/JDK-8012240.js
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+/**
+ * JDK-8012240: Array.prototype.map.call({length: -1, get 0(){throw 0}}, function(){}).length does not throw error
+ *
+ * @test
+ * @run
+ */
+
+var in_getter_for_0 = false;
+
+try {
+ Array.prototype.map.call(
+ {
+ length: -1,
+ get 0() {
+ in_getter_for_0 = true;
+ throw 0;
+ }
+ },
+ function(){}).length;
+} catch (e) {
+ if (e !== 0 || !in_getter_for_0) {
+ fail("should have thrown error from getter for '0'th element");
+ }
+}
diff --git a/test/script/basic/JDK-8012334.js b/test/script/basic/JDK-8012334.js
new file mode 100644
index 00000000..4a1c7a3c
--- /dev/null
+++ b/test/script/basic/JDK-8012334.js
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+
+/**
+ * JDK-8012334: ToUint32, ToInt32, and ToUint16 don't conform to spec
+ *
+ * @test
+ * @run
+ */
+
+
+function test(val) {
+ print(val | 0);
+ print(val >> 0);
+ print(val >>> 0);
+ print(1 >>> val);
+ print(parseInt("10", val));
+}
+
+test(0);
+test(-0);
+test('Infinity');
+test('+Infinity');
+test('-Infinity');
+test(Number.POSITIVE_INFINITY);
+test(Number.NEGATIVE_INFINITY);
+test(Number.NaN);
+test(Number.MIN_VALUE);
+test(-Number.MIN_VALUE);
+test(1);
+test(-1);
+test(0.1);
+test(-0.1);
+test(1.1);
+test(-1.1);
+test(9223372036854775807);
+test(-9223372036854775808);
+test('9223372036854775807');
+test('-9223372036854775808');
+test(2147483647);
+test(2147483648);
+test(2147483649);
+test(-2147483647);
+test(-2147483648);
+test(-2147483649);
+test(4294967295);
+test(4294967296);
+test(4294967297);
+test(-4294967295);
+test(-4294967296);
+test(-4294967297);
+test(1e23);
+test(-1e23);
+test(1e24);
+test(-1e24);
+test(1e25);
+test(-1e25);
+test(1e26);
+test(-1e26);
+
diff --git a/test/script/basic/JDK-8012334.js.EXPECTED b/test/script/basic/JDK-8012334.js.EXPECTED
new file mode 100644
index 00000000..d9b16f41
--- /dev/null
+++ b/test/script/basic/JDK-8012334.js.EXPECTED
@@ -0,0 +1,200 @@
+0
+0
+0
+1
+10
+0
+0
+0
+1
+10
+0
+0
+0
+1
+10
+0
+0
+0
+1
+10
+0
+0
+0
+1
+10
+0
+0
+0
+1
+10
+0
+0
+0
+1
+10
+0
+0
+0
+1
+10
+0
+0
+0
+1
+10
+0
+0
+0
+1
+10
+1
+1
+1
+0
+NaN
+-1
+-1
+4294967295
+0
+NaN
+0
+0
+0
+1
+10
+0
+0
+0
+1
+10
+1
+1
+1
+0
+NaN
+-1
+-1
+4294967295
+0
+NaN
+0
+0
+0
+1
+10
+0
+0
+0
+1
+10
+0
+0
+0
+1
+10
+0
+0
+0
+1
+10
+2147483647
+2147483647
+2147483647
+0
+NaN
+-2147483648
+-2147483648
+2147483648
+1
+NaN
+-2147483647
+-2147483647
+2147483649
+0
+NaN
+-2147483647
+-2147483647
+2147483649
+0
+NaN
+-2147483648
+-2147483648
+2147483648
+1
+NaN
+2147483647
+2147483647
+2147483647
+0
+NaN
+-1
+-1
+4294967295
+0
+NaN
+0
+0
+0
+1
+10
+1
+1
+1
+0
+NaN
+1
+1
+1
+0
+NaN
+0
+0
+0
+1
+10
+-1
+-1
+4294967295
+0
+NaN
+-167772160
+-167772160
+4127195136
+1
+NaN
+167772160
+167772160
+167772160
+1
+NaN
+-1610612736
+-1610612736
+2684354560
+1
+NaN
+1610612736
+1610612736
+1610612736
+1
+NaN
+-2147483648
+-2147483648
+2147483648
+1
+NaN
+-2147483648
+-2147483648
+2147483648
+1
+NaN
+0
+0
+0
+1
+10
+0
+0
+0
+1
+10
diff --git a/test/script/basic/JDK-8012457.js b/test/script/basic/JDK-8012457.js
new file mode 100644
index 00000000..2f71a9a9
--- /dev/null
+++ b/test/script/basic/JDK-8012457.js
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+/**
+ * JDK-8012457: Function.prototype.apply should accept any array-like argument for function arguments
+ *
+ * @test
+ * @run
+ */
+
+// no exception for these
+Function().apply(null, {length: null})
+Function().apply(null, {length: 0.1})
+
+// getter should be called
+var getter_0_called = false;
+
+Function().apply(null,
+ Object.defineProperty([],"0",
+ { get: function(){ getter_0_called = true; return 0 }
+ })
+);
+
+if (! getter_0_called) {
+ fail("getter for '0' of arguments array not called");
+}
diff --git a/test/script/basic/JDK-8012460.js b/test/script/basic/JDK-8012460.js
new file mode 100644
index 00000000..0b41298d
--- /dev/null
+++ b/test/script/basic/JDK-8012460.js
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+/**
+ * JDK-8012460: RegExp regression
+ *
+ * @test
+ * @run
+ */
+
+
+var semver = "\\s*[v=]*\\s*([0-9]+)" // major
+ + "\\.([0-9]+)" // minor
+ + "\\.([0-9]+)" // patch
+ + "(-[0-9]+-?)?" // build
+ + "([a-zA-Z-+][a-zA-Z0-9-\.:]*)?" // tag
+ , exprComparator = "^((<|>)?=?)\s*("+semver+")$|^$";
+var validComparator = new RegExp("^"+exprComparator+"$");
+
+
+print(exprComparator);
+print(">=0.6.0-".match(validComparator));
+print("=0.6.0-".match(validComparator));
+print("0.6.0-".match(validComparator));
+print("<=0.6.0-".match(validComparator));
+print(">=0.6.0-a:b-c.d".match(validComparator));
+print("=0.6.0-a:b-c.d".match(validComparator));
+print("0.6.0+a:b-c.d".match(validComparator));
+print("<=0.6.0+a:b-c.d".match(validComparator));
+
+print(/[a-zA-Z-+]/.exec("a"));
+print(/[a-zA-Z-+]/.exec("b"));
+print(/[a-zA-Z-+]/.exec("y"));
+print(/[a-zA-Z-+]/.exec("z"));
+print(/[a-zA-Z-+]/.exec("B"));
+print(/[a-zA-Z-+]/.exec("Y"));
+print(/[a-zA-Z-+]/.exec("Z"));
+print(/[a-zA-Z-+]/.exec("-"));
+print(/[a-zA-Z-+]/.exec("+"));
diff --git a/test/script/basic/JDK-8012460.js.EXPECTED b/test/script/basic/JDK-8012460.js.EXPECTED
new file mode 100644
index 00000000..917cac54
--- /dev/null
+++ b/test/script/basic/JDK-8012460.js.EXPECTED
@@ -0,0 +1,18 @@
+^((<|>)?=?)s*(\s*[v=]*\s*([0-9]+)\.([0-9]+)\.([0-9]+)(-[0-9]+-?)?([a-zA-Z-+][a-zA-Z0-9-.:]*)?)$|^$
+>=0.6.0-,>=,>,0.6.0-,0,6,0,,-
+=0.6.0-,=,,0.6.0-,0,6,0,,-
+0.6.0-,,,0.6.0-,0,6,0,,-
+<=0.6.0-,<=,<,0.6.0-,0,6,0,,-
+>=0.6.0-a:b-c.d,>=,>,0.6.0-a:b-c.d,0,6,0,,-a:b-c.d
+=0.6.0-a:b-c.d,=,,0.6.0-a:b-c.d,0,6,0,,-a:b-c.d
+0.6.0+a:b-c.d,,,0.6.0+a:b-c.d,0,6,0,,+a:b-c.d
+<=0.6.0+a:b-c.d,<=,<,0.6.0+a:b-c.d,0,6,0,,+a:b-c.d
+a
+b
+y
+z
+B
+Y
+Z
+-
++
diff --git a/test/script/basic/JDK-8012462.js b/test/script/basic/JDK-8012462.js
new file mode 100644
index 00000000..4e015a99
--- /dev/null
+++ b/test/script/basic/JDK-8012462.js
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+/**
+ * JDK-8012462: Date.prototype.toJSON does not handle non-Date 'this' as per the spec.
+ *
+ * @test
+ * @run
+ */
+
+var toJSON = Date.prototype.toJSON;
+
+function checkJSON(value, expected) {
+ var res = toJSON.call({
+ valueOf: function() { return value; },
+ toISOString: function() { return value; }
+ });
+
+ if (res !== expected) {
+ fail("Date.prototype.toJSON does not work for non-Date 'this'");
+ }
+}
+
+checkJSON(NaN, null);
+checkJSON(-Infinity, null);
+checkJSON(Infinity, null);
+checkJSON("foo", "foo");
diff --git a/test/script/basic/JDK-8012931.js b/test/script/basic/JDK-8012931.js
new file mode 100644
index 00000000..c3119b93
--- /dev/null
+++ b/test/script/basic/JDK-8012931.js
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+/**
+ * JDK-8012931: NativeDate.safeToString() throws RangeError for invalid date
+ *
+ * @test
+ * @run
+ */
+
+var d = new Date(NaN);
+d.toString = Number.prototype.toString;
+
+try {
+ d.toString();
+} catch(e) {
+ print(e);
+}
diff --git a/test/script/basic/JDK-8012931.js.EXPECTED b/test/script/basic/JDK-8012931.js.EXPECTED
new file mode 100644
index 00000000..0e84e7da
--- /dev/null
+++ b/test/script/basic/JDK-8012931.js.EXPECTED
@@ -0,0 +1 @@
+TypeError: [Date Invalid Date] is not a Number
diff --git a/test/script/basic/JDK-8013131.js b/test/script/basic/JDK-8013131.js
new file mode 100644
index 00000000..dfff8ac6
--- /dev/null
+++ b/test/script/basic/JDK-8013131.js
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+/**
+ * JDK-8013131: Various compatibility issues in String.prototype.split()
+ *
+ * @test
+ * @run
+ */
+
+
+// Make sure limit is honored with undefined/empty separator
+print(JSON.stringify("aa".split(undefined, 0)));
+print(JSON.stringify("abc".split("", 1)));
+
+// Make sure limit is honored with capture groups
+print(JSON.stringify("aa".split(/(a)/, 1)));
+print(JSON.stringify("aa".split(/(a)/, 2)));
+print(JSON.stringify("aa".split(/((a))/, 1)));
+print(JSON.stringify("aa".split(/((a))/, 2)));
+
+// Empty capture group at end of string should be ignored
+print(JSON.stringify("aaa".split(/((?:))/)));
+
+// Tests below are to make sure that split does not read or write lastIndex property
+var r = /a/;
+r.lastIndex = {
+ valueOf: function(){throw 2}
+};
+print(JSON.stringify("aa".split(r)));
+
+r = /a/g;
+r.lastIndex = 100;
+print(JSON.stringify("aa".split(r)));
+print(r.lastIndex);
+
+r = /((?:))/g;
+r.lastIndex = 100;
+print(JSON.stringify("aaa".split(r)));
+print(r.lastIndex);
+
+// Make sure lastIndex is not updated on non-global regexp
+r = /a/;
+r.lastIndex = 100;
+print(JSON.stringify(r.exec("aaa")));
+print(r.lastIndex);
diff --git a/test/script/basic/JDK-8013131.js.EXPECTED b/test/script/basic/JDK-8013131.js.EXPECTED
new file mode 100644
index 00000000..9e0fa963
--- /dev/null
+++ b/test/script/basic/JDK-8013131.js.EXPECTED
@@ -0,0 +1,14 @@
+[]
+["a"]
+[""]
+["","a"]
+[""]
+["","a"]
+["a","","a","","a"]
+["","",""]
+["","",""]
+100
+["a","","a","","a"]
+100
+["a"]
+100
diff --git a/test/script/basic/JDK-8013167.js b/test/script/basic/JDK-8013167.js
new file mode 100644
index 00000000..ec3c71a1
--- /dev/null
+++ b/test/script/basic/JDK-8013167.js
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+/**
+ * JDK-8013167: Vararg constructor was not found
+ *
+ * @test
+ * @run
+ */
+
+var x = new Packages.jdk.nashorn.test.models.VarArgConstructor(1, false, "a", "b", "c")
+print(x.indicator) \ No newline at end of file
diff --git a/test/script/basic/JDK-8013167.js.EXPECTED b/test/script/basic/JDK-8013167.js.EXPECTED
new file mode 100644
index 00000000..e632d3be
--- /dev/null
+++ b/test/script/basic/JDK-8013167.js.EXPECTED
@@ -0,0 +1 @@
+vararg
diff --git a/test/script/basic/JDK-8013325.js b/test/script/basic/JDK-8013325.js
new file mode 100644
index 00000000..e1faa7b7
--- /dev/null
+++ b/test/script/basic/JDK-8013325.js
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+/**
+ * JDK-8013325: function named 'arguments' should still access arguments object within itself.
+ * Its parent should however see the function and not an arguments object.
+ *
+ * @test
+ * @run
+ */
+
+function x() {
+ // x doesn't see an arguments object as it has a nested function with that name
+ // so it'll invoke the function.
+ arguments("a", "b", "c");
+
+ function arguments(x, y, z) {
+ // The function 'arguments' OTOH can't see itself; if it uses the
+ // identifier 'arguments', it'll see its own arguments object.
+ print(arguments)
+ print(x + " " + y + " " + z)
+ }
+}
+x()
diff --git a/test/script/basic/JDK-8013325.js.EXPECTED b/test/script/basic/JDK-8013325.js.EXPECTED
new file mode 100644
index 00000000..e2dc7e54
--- /dev/null
+++ b/test/script/basic/JDK-8013325.js.EXPECTED
@@ -0,0 +1,2 @@
+[object Arguments]
+a b c
diff --git a/test/script/basic/JDK-8013337.js b/test/script/basic/JDK-8013337.js
new file mode 100644
index 00000000..b95da8bd
--- /dev/null
+++ b/test/script/basic/JDK-8013337.js
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+/**
+ * JDK-8013337: Issues with Date.prototype's get, set functions
+ *
+ * @test
+ * @option -timezone=Asia/Calcutta
+ * @run
+ */
+
+function check(str) {
+ print(str + " = " + eval(str));
+}
+
+check('new Date(NaN).setFullYear(NaN)');
+check('new Date(0).setYear(70)');
+check('new Date(0).setYear(NaN)');
+check('new Date(NaN).setYear(70)');
+check('new Date(NaN).getTimezoneOffset()');
+
+function checkGetterCalled(func) {
+ var getterCalled = false;
+
+ new Date(NaN)[func]( { valueOf: function() { getterCalled = true } } );
+
+ if (getterCalled) {
+ print("Date.prototype." + func + " calls valueOf on arg");
+ }
+}
+
+checkGetterCalled("setMilliseconds");
+checkGetterCalled("setUTCMilliseconds");
+checkGetterCalled("setSeconds");
+checkGetterCalled("setUTCSeconds");
+checkGetterCalled("setMinutes");
+checkGetterCalled("setUTCMinutes");
+checkGetterCalled("setHours");
+checkGetterCalled("setUTCHours");
+checkGetterCalled("setDate");
+checkGetterCalled("setUTCDate");
+checkGetterCalled("setMonth");
+checkGetterCalled("setUTCMonth");
+
+try {
+ Date.prototype.setTime.call({}, { valueOf: function() { throw "err" } })
+} catch (e) {
+ if (! (e instanceof TypeError)) {
+ fail("TypeError expected, got " + e);
+ }
+}
diff --git a/test/script/basic/JDK-8013337.js.EXPECTED b/test/script/basic/JDK-8013337.js.EXPECTED
new file mode 100644
index 00000000..fbd2d655
--- /dev/null
+++ b/test/script/basic/JDK-8013337.js.EXPECTED
@@ -0,0 +1,17 @@
+new Date(NaN).setFullYear(NaN) = NaN
+new Date(0).setYear(70) = 0
+new Date(0).setYear(NaN) = NaN
+new Date(NaN).setYear(70) = -19800000
+new Date(NaN).getTimezoneOffset() = NaN
+Date.prototype.setMilliseconds calls valueOf on arg
+Date.prototype.setUTCMilliseconds calls valueOf on arg
+Date.prototype.setSeconds calls valueOf on arg
+Date.prototype.setUTCSeconds calls valueOf on arg
+Date.prototype.setMinutes calls valueOf on arg
+Date.prototype.setUTCMinutes calls valueOf on arg
+Date.prototype.setHours calls valueOf on arg
+Date.prototype.setUTCHours calls valueOf on arg
+Date.prototype.setDate calls valueOf on arg
+Date.prototype.setUTCDate calls valueOf on arg
+Date.prototype.setMonth calls valueOf on arg
+Date.prototype.setUTCMonth calls valueOf on arg
diff --git a/test/script/basic/JDK-8013444.js b/test/script/basic/JDK-8013444.js
new file mode 100644
index 00000000..166575cd
--- /dev/null
+++ b/test/script/basic/JDK-8013444.js
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+/**
+ * JDK-8013444: JSON.parse does not invoke "reviver" callback as per spec.
+ *
+ * @test
+ * @run
+ */
+
+
+var type = typeof JSON.parse('{}',function(){})
+print("type is " + type);
+
+var obj = JSON.parse('{"name": "nashorn"}',
+ function(k, v) {
+ if (k === "") return v;
+ return v.toUpperCase();
+ });
+print(JSON.stringify(obj))
+
+var array =
+ JSON.parse("[1, 3, 5, 7, 9, 11]",
+ function(k, v) {
+ if (k === "") return v;
+ return v*2;
+ }
+ );
+print(array)
diff --git a/test/script/basic/JDK-8013444.js.EXPECTED b/test/script/basic/JDK-8013444.js.EXPECTED
new file mode 100644
index 00000000..2214e49b
--- /dev/null
+++ b/test/script/basic/JDK-8013444.js.EXPECTED
@@ -0,0 +1,3 @@
+type is undefined
+{"name":"NASHORN"}
+2,6,10,14,18,22
diff --git a/test/script/basic/javaclassoverrides.js b/test/script/basic/javaclassoverrides.js
new file mode 100644
index 00000000..e7ad61d8
--- /dev/null
+++ b/test/script/basic/javaclassoverrides.js
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+
+/**
+ * Check behavior of class-level overrides.
+ *
+ * @test
+ * @run
+ */
+
+
+// Make two classes with class overrides
+
+var R1 = Java.extend(java.lang.Runnable, {
+ run: function() {
+ print("R1.run() invoked")
+ }
+})
+
+var R2 = Java.extend(java.lang.Runnable, {
+ run: function() {
+ print("R2.run() invoked")
+ }
+})
+
+var r1 = new R1
+var r2 = new R2
+// Create one with an instance-override too
+var r3 = new R2(function() { print("r3.run() invoked") })
+
+// Run 'em - we're passing them through a Thread to make sure they indeed
+// are full-blown Runnables
+function runInThread(r) {
+ var t = new java.lang.Thread(r)
+ t.start()
+ t.join()
+}
+runInThread(r1)
+runInThread(r2)
+runInThread(r3)
+
+// Two class-override classes differ
+print("r1.class != r2.class: " + (r1.class != r2.class))
+// However, adding instance-overrides doesn't change the class
+print("r2.class == r3.class: " + (r2.class == r3.class))
+
+function checkAbstract(r) {
+ try {
+ r.run()
+ print("Expected to fail!")
+ } catch(e) {
+ print("Got exception: " + e)
+ }
+}
+
+// Check we're hitting UnsupportedOperationException if neither class
+// overrides nor instance overrides are present
+var RAbstract = Java.extend(java.lang.Runnable, {})
+checkAbstract(new RAbstract()) // class override (empty)
+checkAbstract(new RAbstract() {}) // class+instance override (empty)
+
+// Check we delegate to superclass if neither class
+// overrides nor instance overrides are present
+var ExtendsList = Java.extend(java.util.ArrayList, {})
+print("(new ExtendsList).size() = " + (new ExtendsList).size())
+print("(new ExtendsList(){}).size() = " + (new ExtendsList(){}).size()) \ No newline at end of file
diff --git a/test/script/basic/javaclassoverrides.js.EXPECTED b/test/script/basic/javaclassoverrides.js.EXPECTED
new file mode 100644
index 00000000..6c534302
--- /dev/null
+++ b/test/script/basic/javaclassoverrides.js.EXPECTED
@@ -0,0 +1,9 @@
+R1.run() invoked
+R2.run() invoked
+r3.run() invoked
+r1.class != r2.class: true
+r2.class == r3.class: true
+Got exception: java.lang.UnsupportedOperationException
+Got exception: java.lang.UnsupportedOperationException
+(new ExtendsList).size() = 0
+(new ExtendsList(){}).size() = 0
diff --git a/test/script/basic/try2.js b/test/script/basic/try2.js
new file mode 100644
index 00000000..8a59c89b
--- /dev/null
+++ b/test/script/basic/try2.js
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+/**
+ * Try throw test - nest finally
+ *
+ * @test
+ * @run
+ */
+
+function f() {
+ print("a");
+ try {
+ print("b");
+ } finally {
+ print("c");
+ try {
+ print("d");
+ } finally {
+ print("e");
+ }
+ print("f");
+ }
+ print("g");
+}
+
+f();
+
+print("done");
diff --git a/test/script/basic/try2.js.EXPECTED b/test/script/basic/try2.js.EXPECTED
new file mode 100644
index 00000000..603b8cd1
--- /dev/null
+++ b/test/script/basic/try2.js.EXPECTED
@@ -0,0 +1,8 @@
+a
+b
+c
+d
+e
+f
+g
+done
diff --git a/test/script/error/JDK-8008814-1.js b/test/script/error/JDK-8008814-1.js
new file mode 100644
index 00000000..324d090c
--- /dev/null
+++ b/test/script/error/JDK-8008814-1.js
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+/**
+ * NASHORN-8008814: it's a compile time error to have a nested function declaration when there's an option to treat it as an error
+ *
+ * @option --function-statement-error
+ * @test/compile-error
+ */
+
+if(true) {
+ function g() {
+ }
+}
diff --git a/test/script/error/JDK-8008814-1.js.EXPECTED b/test/script/error/JDK-8008814-1.js.EXPECTED
new file mode 100644
index 00000000..91437255
--- /dev/null
+++ b/test/script/error/JDK-8008814-1.js.EXPECTED
@@ -0,0 +1,3 @@
+test/script/error/JDK-8008814-1.js:32:2 Function declarations can only occur at program or function body level. You should use a function expression here instead.
+ function g() {
+ ^
diff --git a/test/script/error/JDK-8008814-2.js b/test/script/error/JDK-8008814-2.js
new file mode 100644
index 00000000..079b253f
--- /dev/null
+++ b/test/script/error/JDK-8008814-2.js
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+/**
+ * NASHORN-8008814: it's a compile time error to have a nested function declaration in strict mode
+ *
+ * @test/compile-error
+ */
+
+"use strict";
+if(true) {
+ function g() {
+ }
+}
diff --git a/test/script/error/JDK-8008814-2.js.EXPECTED b/test/script/error/JDK-8008814-2.js.EXPECTED
new file mode 100644
index 00000000..f79c679c
--- /dev/null
+++ b/test/script/error/JDK-8008814-2.js.EXPECTED
@@ -0,0 +1,3 @@
+test/script/error/JDK-8008814-2.js:32:2 In strict mode, function declarations can only occur at program or function body level. You should use a function expression here instead.
+ function g() {
+ ^
diff --git a/test/script/trusted/logcoverage.js b/test/script/trusted/logcoverage.js
new file mode 100644
index 00000000..a2cd7a0f
--- /dev/null
+++ b/test/script/trusted/logcoverage.js
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ */
+
+/**
+ * mh_coverage.js
+ * Screen scrape various logs to ensure that we cover enough functionality,
+ * e.g. method handle instrumentation
+ *
+ * @test
+ * @run
+ */
+
+/*
+ * creates new script engine initialized with given options and
+ * runs given code on it. Returns standard output captured.
+ */
+
+function runScriptEngine(opts, name) {
+ var imports = new JavaImporter(
+ Packages.jdk.nashorn.api.scripting,
+ java.io, java.lang, java.util);
+
+ with(imports) {
+ var fac = new NashornScriptEngineFactory();
+ // get current System.err
+ var oldErr = System.err;
+ var oldOut = System.out;
+ var baosErr = new ByteArrayOutputStream();
+ var newErr = new PrintStream(baosErr);
+ var baosOut = new ByteArrayOutputStream();
+ var newOut = new PrintStream(baosOut);
+ try {
+ // set new standard err
+ System.setErr(newErr);
+ System.setOut(newOut);
+ var strType = Java.type("java.lang.String");
+ var engine = fac.getScriptEngine(Java.toJavaArray(opts, strType));
+ var reader = new java.io.FileReader(name);
+ engine.eval(reader);
+ newErr.flush();
+ newOut.flush();
+ return new java.lang.String(baosErr.toByteArray());
+ } finally {
+ // restore System.err to old value
+ System.setErr(oldErr);
+ System.setOut(oldOut);
+ }
+ }
+}
+
+var str;
+
+var methodsCalled = [
+ 'asCollector',
+ 'asType',
+ 'bindTo',
+ 'dropArguments',
+ 'explicitCastArguments',
+ 'filterArguments',
+ 'filterReturnValue',
+ 'findStatic',
+ 'findVirtual',
+ 'foldArguments',
+ 'getter',
+ 'guardWithTest',
+ 'insertArguments',
+ 'methodType',
+ 'setter'
+];
+
+function check(str, strs) {
+ for each (s in strs) {
+ if (s.indexOf(str) !== -1) {
+ continue;
+ }
+ print(method + "not found");
+ return;
+ }
+ print("check ok!");
+}
+
+str = runScriptEngine([ "--log=codegen,compiler=finest,methodhandles=finest,fields=finest" ], __DIR__ + "../basic/NASHORN-19.js");
+
+check(str, methodsCalled);
+check(str, ['return', 'get', 'set', '[fields]']);
+check(str, ['codegen']);
+
+print("hello, world!");
diff --git a/test/script/trusted/logcoverage.js.EXPECTED b/test/script/trusted/logcoverage.js.EXPECTED
new file mode 100644
index 00000000..c3e64ce5
--- /dev/null
+++ b/test/script/trusted/logcoverage.js.EXPECTED
@@ -0,0 +1,4 @@
+check ok!
+check ok!
+check ok!
+hello, world!
diff --git a/test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java b/test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java
index 48277aa8..d12c9057 100644
--- a/test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java
+++ b/test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java
@@ -55,7 +55,7 @@ import org.testng.annotations.Test;
* Tests for JSR-223 script engine for Nashorn.
*
* @test
- * @build jdk.nashorn.api.scripting.Window jdk.nashorn.api.scripting.WindowEventHandler jdk.nashorn.api.scripting.ScriptEngineTest
+ * @build jdk.nashorn.api.scripting.Window jdk.nashorn.api.scripting.WindowEventHandler jdk.nashorn.api.scripting.VariableArityTestInterface jdk.nashorn.api.scripting.ScriptEngineTest
* @run testng jdk.nashorn.api.scripting.ScriptEngineTest
*/
public class ScriptEngineTest {
@@ -906,4 +906,26 @@ public class ScriptEngineTest {
fail(se.getMessage());
}
}
+
+ @Test
+ /**
+ * Tests whether invocation of a JavaScript method through a variable arity Java method will pass the vararg array.
+ * Both non-vararg and vararg JavaScript methods are tested.
+ * @throws ScriptException
+ */
+ public void variableArityInterfaceTest() throws ScriptException {
+ final ScriptEngineManager m = new ScriptEngineManager();
+ final ScriptEngine e = m.getEngineByName("nashorn");
+ e.eval(
+ "function test1(i, strings) {" +
+ " return 'i == ' + i + ', strings instanceof java.lang.String[] == ' + (strings instanceof Java.type('java.lang.String[]')) + ', strings == ' + java.util.Arrays.toString(strings)" +
+ "}" +
+ "function test2() {" +
+ " return 'arguments[0] == ' + arguments[0] + ', arguments[1] instanceof java.lang.String[] == ' + (arguments[1] instanceof Java.type('java.lang.String[]')) + ', arguments[1] == ' + java.util.Arrays.toString(arguments[1])" +
+ "}"
+ );
+ final VariableArityTestInterface itf = ((Invocable)e).getInterface(VariableArityTestInterface.class);
+ Assert.assertEquals(itf.test1(42, "a", "b"), "i == 42, strings instanceof java.lang.String[] == true, strings == [a, b]");
+ Assert.assertEquals(itf.test2(44, "c", "d", "e"), "arguments[0] == 44, arguments[1] instanceof java.lang.String[] == true, arguments[1] == [c, d, e]");
+ }
}
diff --git a/test/src/jdk/nashorn/api/scripting/VariableArityTestInterface.java b/test/src/jdk/nashorn/api/scripting/VariableArityTestInterface.java
new file mode 100644
index 00000000..d3904522
--- /dev/null
+++ b/test/src/jdk/nashorn/api/scripting/VariableArityTestInterface.java
@@ -0,0 +1,31 @@
+/*
+ * 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.api.scripting;
+
+public interface VariableArityTestInterface {
+ public String test1(int i, String... strings);
+ public String test2(int i, String... strings);
+}
diff --git a/test/src/jdk/nashorn/internal/runtime/JSTypeTest.java b/test/src/jdk/nashorn/internal/runtime/JSTypeTest.java
index 6cf18d02..406ce1c3 100644
--- a/test/src/jdk/nashorn/internal/runtime/JSTypeTest.java
+++ b/test/src/jdk/nashorn/internal/runtime/JSTypeTest.java
@@ -105,4 +105,89 @@ public class JSTypeTest {
// FIXME: add more number-to-string test cases
// FIXME: add case for Object type (JSObject with getDefaultValue)
}
+
+ /**
+ * Test of JSType.toUint32(double)
+ */
+ @Test
+ public void testToUint32() {
+ assertEquals(JSType.toUint32(+0.0), 0);
+ assertEquals(JSType.toUint32(-0.0), 0);
+ assertEquals(JSType.toUint32(Double.NaN), 0);
+ assertEquals(JSType.toUint32(Double.POSITIVE_INFINITY), 0);
+ assertEquals(JSType.toUint32(Double.NEGATIVE_INFINITY), 0);
+ assertEquals(JSType.toUint32(9223372036854775807.0d), 0);
+ assertEquals(JSType.toUint32(-9223372036854775807.0d), 0);
+ assertEquals(JSType.toUint32(1099511627776.0d), 0);
+ assertEquals(JSType.toUint32(-1099511627776.0d), 0);
+ assertEquals(JSType.toUint32(4294967295.0d), 4294967295l);
+ assertEquals(JSType.toUint32(4294967296.0d), 0);
+ assertEquals(JSType.toUint32(4294967297.0d), 1);
+ assertEquals(JSType.toUint32(-4294967295.0d), 1);
+ assertEquals(JSType.toUint32(-4294967296.0d), 0);
+ assertEquals(JSType.toUint32(-4294967297.0d), 4294967295l);
+ assertEquals(JSType.toUint32(4294967295.6d), 4294967295l);
+ assertEquals(JSType.toUint32(4294967296.6d), 0);
+ assertEquals(JSType.toUint32(4294967297.6d), 1);
+ assertEquals(JSType.toUint32(-4294967295.6d), 1);
+ assertEquals(JSType.toUint32(-4294967296.6d), 0);
+ assertEquals(JSType.toUint32(-4294967297.6d), 4294967295l);
+ }
+
+ /**
+ * Test of JSType.toInt32(double)
+ */
+ @Test
+ public void testToInt32() {
+ assertEquals(JSType.toInt32(+0.0), 0);
+ assertEquals(JSType.toInt32(-0.0), 0);
+ assertEquals(JSType.toInt32(Double.NaN), 0);
+ assertEquals(JSType.toInt32(Double.POSITIVE_INFINITY), 0);
+ assertEquals(JSType.toInt32(Double.NEGATIVE_INFINITY), 0);
+ assertEquals(JSType.toInt32(9223372036854775807.0d), 0);
+ assertEquals(JSType.toInt32(-9223372036854775807.0d), 0);
+ assertEquals(JSType.toInt32(1099511627776.0d), 0);
+ assertEquals(JSType.toInt32(-1099511627776.0d), 0);
+ assertEquals(JSType.toInt32(4294967295.0d), -1);
+ assertEquals(JSType.toInt32(4294967296.0d), 0);
+ assertEquals(JSType.toInt32(4294967297.0d), 1);
+ assertEquals(JSType.toInt32(-4294967295.0d), 1);
+ assertEquals(JSType.toInt32(-4294967296.0d), 0);
+ assertEquals(JSType.toInt32(-4294967297.d), -1);
+ assertEquals(JSType.toInt32(4294967295.6d), -1);
+ assertEquals(JSType.toInt32(4294967296.6d), 0);
+ assertEquals(JSType.toInt32(4294967297.6d), 1);
+ assertEquals(JSType.toInt32(-4294967295.6d), 1);
+ assertEquals(JSType.toInt32(-4294967296.6d), 0);
+ assertEquals(JSType.toInt32(-4294967297.6d), -1);
+ }
+
+ /**
+ * Test of JSType.toUint16(double)
+ */
+ @Test
+ public void testToUint16() {
+ assertEquals(JSType.toUint16(+0.0), 0);
+ assertEquals(JSType.toUint16(-0.0), 0);
+ assertEquals(JSType.toUint16(Double.NaN), 0);
+ assertEquals(JSType.toUint16(Double.POSITIVE_INFINITY), 0);
+ assertEquals(JSType.toUint16(Double.NEGATIVE_INFINITY), 0);
+ assertEquals(JSType.toUint16(9223372036854775807.0d), 0);
+ assertEquals(JSType.toUint16(-9223372036854775807.0d), 0);
+ assertEquals(JSType.toUint16(1099511627776.0d), 0);
+ assertEquals(JSType.toUint16(-1099511627776.0d), 0);
+ assertEquals(JSType.toUint16(4294967295.0d), 65535);
+ assertEquals(JSType.toUint16(4294967296.0d), 0);
+ assertEquals(JSType.toUint16(4294967297.0d), 1);
+ assertEquals(JSType.toUint16(-4294967295.0d), 1);
+ assertEquals(JSType.toUint16(-4294967296.0d), 0);
+ assertEquals(JSType.toUint16(-4294967297.0d), 65535);
+ assertEquals(JSType.toUint16(4294967295.6d), 65535);
+ assertEquals(JSType.toUint16(4294967296.6d), 0);
+ assertEquals(JSType.toUint16(4294967297.6d), 1);
+ assertEquals(JSType.toUint16(-4294967295.6d), 1);
+ assertEquals(JSType.toUint16(-4294967296.6d), 0);
+ assertEquals(JSType.toUint16(-4294967297.6d), 65535);
+ }
+
}
diff --git a/test/src/jdk/nashorn/test/models/Jdk8011362TestSubject.java b/test/src/jdk/nashorn/test/models/Jdk8011362TestSubject.java
new file mode 100644
index 00000000..1b179758
--- /dev/null
+++ b/test/src/jdk/nashorn/test/models/Jdk8011362TestSubject.java
@@ -0,0 +1,47 @@
+/*
+ * 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.test.models;
+
+/**
+ * Test class used by JDK-8011362.js.
+ */
+public class Jdk8011362TestSubject {
+ // This is selected for overloaded("", null)
+ public String overloaded(String a, String b) {
+ return "overloaded(String, String)";
+ }
+
+ // This is selected for overloaded(0, null)
+ public String overloaded(Double a, Double b) {
+ return "overloaded(Double, Double)";
+ }
+
+ // This method is added to test that null will not match a primitive type, that is overloaded(0, null) will always
+ // select the (Double, Double) over (Double, double).
+ public String overloaded(Double a, double b) {
+ return "overloaded(Double, double)";
+ }
+}
diff --git a/test/src/jdk/nashorn/test/models/VarArgConstructor.java b/test/src/jdk/nashorn/test/models/VarArgConstructor.java
new file mode 100644
index 00000000..5f5da30e
--- /dev/null
+++ b/test/src/jdk/nashorn/test/models/VarArgConstructor.java
@@ -0,0 +1,44 @@
+/*
+ * 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.test.models;
+
+import java.util.List;
+
+public class VarArgConstructor {
+ private final String indicator;
+
+ public VarArgConstructor(int x, boolean y, List<String> z) {
+ indicator = "non-vararg";
+ }
+
+ public VarArgConstructor(int x, boolean y, String... z) {
+ indicator = "vararg";
+ }
+
+ public String getIndicator() {
+ return indicator;
+ }
+}
diff --git a/tools/fxshell/jdk/nashorn/tools/FXShell.java b/tools/fxshell/jdk/nashorn/tools/FXShell.java
new file mode 100644
index 00000000..df473189
--- /dev/null
+++ b/tools/fxshell/jdk/nashorn/tools/FXShell.java
@@ -0,0 +1,194 @@
+/*
+ * 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.tools;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javafx.application.Application;
+import javafx.stage.Stage;
+import javax.script.Invocable;
+import javax.script.ScriptContext;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineFactory;
+import javax.script.ScriptEngineManager;
+import javax.script.ScriptException;
+import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
+
+/**
+ * This shell is designed to launch a JavaFX application written in Nashorn JavaScript.
+ */
+public class FXShell extends Application {
+ /**
+ * Script engine manager to search.
+ */
+ private ScriptEngineManager manager;
+ /**
+ * Nashorn script engine factory.
+ */
+ private NashornScriptEngineFactory factory;
+ /**
+ * Main instance of Nashorn script engine.
+ */
+ private ScriptEngine engine;
+
+ /**
+ * Needed so that the FX launcher can create an instance of this class.
+ */
+ public FXShell() {
+ }
+
+ /**
+ * Main entry point. Never actually used.
+ * @param args Command lien arguments.
+ */
+ public static void main(String[] args) {
+ launch(args);
+ }
+
+ /*
+ * Application overrides.
+ */
+
+ @Override
+ public void init() throws Exception {
+ // Script engine manager to search.
+ this.manager = new ScriptEngineManager();
+
+ // Locate the Nashorn script engine factory. Needed for passing arguments.
+ for (ScriptEngineFactory engineFactory : this.manager.getEngineFactories()) {
+ if (engineFactory.getEngineName().equals("Oracle Nashorn") && engineFactory instanceof NashornScriptEngineFactory) {
+ this.factory = (NashornScriptEngineFactory)engineFactory;
+ }
+ }
+
+ // If none located.
+ if (this.factory == null) {
+ System.err.println("Nashorn script engine not available");
+ System.exit(1);
+ }
+
+ // Get the command line and JNLP parameters.
+ final Parameters parameters = getParameters();
+
+ // To collect the script paths and command line arguments.
+ final List<String> paths = new ArrayList<>();
+ final List<String> args = new ArrayList<>();
+
+ // Pull out relevant JNLP named parameters.
+ final Map<String, String> named = parameters.getNamed();
+ for (Map.Entry<String, String> entry : named.entrySet()) {
+ final String key = entry.getKey();
+ final String value = entry.getValue();
+
+ if ((key.equals("cp") || key.equals("classpath")) && value != null) {
+ args.add("-classpath");
+ args.add(value);
+ } else if (key.equals("source") && value != null && value.toLowerCase().endsWith(".js")) {
+ paths.add(value);
+ }
+ }
+
+ // Pull out relevant command line arguments.
+ boolean addNextArg = false;
+ boolean addAllArgs = false;
+ for (String parameter : parameters.getUnnamed()) {
+ if (addAllArgs || addNextArg) {
+ args.add(parameter);
+ addNextArg = false;
+ } else if (parameter.equals("--")) {
+ args.add(parameter);
+ addAllArgs = true;
+ } else if (parameter.startsWith("-")) {
+ args.add(parameter);
+ addNextArg = parameter.equals("-cp") || parameter.equals("-classpath");
+ } else if (parameter.toLowerCase().endsWith(".js")) {
+ paths.add(parameter);
+ }
+ }
+
+ // Create a Nashorn script engine with specified arguments.
+ engine = factory.getScriptEngine(args.toArray(new String[args.size()]));
+
+ // Load initial scripts.
+ for (String path : paths) {
+ load(path);
+ }
+
+ // Invoke users JavaScript init function if present.
+ try {
+ ((Invocable) engine).invokeFunction("init");
+ } catch (NoSuchMethodException ex) {
+ // Presence of init is optional.
+ }
+ }
+
+ @Override
+ public void start(Stage stage) throws Exception {
+ // Invoke users JavaScript start function if present.
+ try {
+ ((Invocable) engine).invokeFunction("start", stage);
+ } catch (NoSuchMethodException ex) {
+ // Presence of start is optional.
+ }
+ }
+
+ @Override
+ public void stop() throws Exception {
+ // Invoke users JavaScript stop function if present.
+ try {
+ ((Invocable) engine).invokeFunction("stop");
+ } catch (NoSuchMethodException ex) {
+ // Presence of stop is optional.
+ }
+ }
+
+ /**
+ * Load and evaluate the specified JavaScript file.
+ *
+ * @param path Path to UTF-8 encoded JavaScript file.
+ *
+ * @return Last evaluation result (discarded.)
+ */
+ private Object load(String path) {
+ try {
+ FileInputStream file = new FileInputStream(path);
+ InputStreamReader input = new InputStreamReader(file, "UTF-8");
+ return engine.eval(input);
+ } catch (FileNotFoundException | UnsupportedEncodingException | ScriptException ex) {
+ ex.printStackTrace();
+ }
+
+ return null;
+ }
+}