aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorenevill <none@none>2015-02-03 16:49:06 +0000
committerenevill <none@none>2015-02-03 16:49:06 +0000
commit55f5842a16a711703dc9a9b4f67a40028de6c1c7 (patch)
tree2aa50158f84aeaaac3a8211f3e40d48241b1f926
parent7dd91a9850d9bf8fb74518a84ae420d430eebdcf (diff)
parent31b66ce436148cbcbf1f4dfd37f89c9da5fc1ccf (diff)
downloadnashorn-55f5842a16a711703dc9a9b4f67a40028de6c1c7.tar.gz
Merge up to jdk8u40-b23
--HG-- rename : src/jdk/nashorn/internal/runtime/linker/BoundDynamicMethodLinker.java => src/jdk/nashorn/internal/runtime/linker/BoundCallableLinker.java
-rw-r--r--.hgtags25
-rw-r--r--THIRD_PARTY_README2
-rw-r--r--bin/runopt.sh117
-rw-r--r--docs/source/EvalFile.java10
-rw-r--r--docs/source/EvalScript.java10
-rw-r--r--docs/source/InvokeScriptFunction.java15
-rw-r--r--docs/source/InvokeScriptMethod.java17
-rw-r--r--docs/source/MultiScopes.java17
-rw-r--r--docs/source/RunnableImpl.java19
-rw-r--r--docs/source/RunnableImplObject.java21
-rw-r--r--docs/source/ScriptVars.java14
-rw-r--r--make/build.xml1
-rw-r--r--samples/browser_dom.js91
-rw-r--r--src/jdk/internal/dynalink/DynamicLinkerFactory.java32
-rw-r--r--src/jdk/internal/dynalink/beans/AbstractJavaLinker.java20
-rw-r--r--src/jdk/internal/dynalink/beans/OverloadedMethod.java1
-rw-r--r--src/jdk/internal/dynalink/linker/MethodTypeConversionStrategy.java100
-rw-r--r--src/jdk/internal/dynalink/support/TypeConverterFactory.java44
-rw-r--r--src/jdk/internal/dynalink/support/TypeUtilities.java31
-rw-r--r--src/jdk/nashorn/internal/AssertsEnabled.java2
-rw-r--r--src/jdk/nashorn/internal/codegen/ApplySpecialization.java215
-rw-r--r--src/jdk/nashorn/internal/codegen/AssignSymbols.java38
-rw-r--r--src/jdk/nashorn/internal/codegen/AstSerializer.java6
-rw-r--r--src/jdk/nashorn/internal/codegen/CodeGenerator.java113
-rw-r--r--src/jdk/nashorn/internal/codegen/CodeGeneratorLexicalContext.java3
-rw-r--r--src/jdk/nashorn/internal/codegen/Compiler.java1
-rw-r--r--src/jdk/nashorn/internal/codegen/FoldConstants.java35
-rw-r--r--src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java174
-rw-r--r--src/jdk/nashorn/internal/codegen/Lower.java35
-rw-r--r--src/jdk/nashorn/internal/codegen/MapCreator.java4
-rw-r--r--src/jdk/nashorn/internal/codegen/MethodEmitter.java28
-rw-r--r--src/jdk/nashorn/internal/codegen/OptimisticTypesPersistence.java49
-rw-r--r--src/jdk/nashorn/internal/codegen/SharedScopeCall.java2
-rw-r--r--src/jdk/nashorn/internal/codegen/SpillObjectCreator.java2
-rw-r--r--src/jdk/nashorn/internal/codegen/TypeEvaluator.java2
-rw-r--r--src/jdk/nashorn/internal/codegen/anchor.properties27
-rw-r--r--src/jdk/nashorn/internal/codegen/types/IntType.java19
-rw-r--r--src/jdk/nashorn/internal/codegen/types/LongType.java18
-rw-r--r--src/jdk/nashorn/internal/codegen/types/Type.java25
-rw-r--r--src/jdk/nashorn/internal/ir/AccessNode.java10
-rw-r--r--src/jdk/nashorn/internal/ir/BinaryNode.java9
-rw-r--r--src/jdk/nashorn/internal/ir/ForNode.java24
-rw-r--r--src/jdk/nashorn/internal/ir/LexicalContext.java14
-rw-r--r--src/jdk/nashorn/internal/ir/LoopNode.java6
-rw-r--r--src/jdk/nashorn/internal/ir/RuntimeNode.java7
-rw-r--r--src/jdk/nashorn/internal/ir/SwitchNode.java47
-rw-r--r--src/jdk/nashorn/internal/ir/VarNode.java19
-rw-r--r--src/jdk/nashorn/internal/ir/WhileNode.java5
-rw-r--r--src/jdk/nashorn/internal/objects/ArrayBufferView.java116
-rw-r--r--src/jdk/nashorn/internal/objects/Global.java206
-rw-r--r--src/jdk/nashorn/internal/objects/NativeArray.java373
-rw-r--r--src/jdk/nashorn/internal/objects/NativeArrayBuffer.java15
-rw-r--r--src/jdk/nashorn/internal/objects/NativeDataView.java25
-rw-r--r--src/jdk/nashorn/internal/objects/NativeDate.java3
-rw-r--r--src/jdk/nashorn/internal/objects/NativeDebug.java31
-rw-r--r--src/jdk/nashorn/internal/objects/NativeFloat32Array.java5
-rw-r--r--src/jdk/nashorn/internal/objects/NativeFloat64Array.java5
-rw-r--r--src/jdk/nashorn/internal/objects/NativeFunction.java9
-rw-r--r--src/jdk/nashorn/internal/objects/NativeInt16Array.java5
-rw-r--r--src/jdk/nashorn/internal/objects/NativeInt32Array.java5
-rw-r--r--src/jdk/nashorn/internal/objects/NativeInt8Array.java5
-rw-r--r--src/jdk/nashorn/internal/objects/NativeObject.java4
-rw-r--r--src/jdk/nashorn/internal/objects/NativeRegExpExecResult.java2
-rw-r--r--src/jdk/nashorn/internal/objects/NativeString.java6
-rw-r--r--src/jdk/nashorn/internal/objects/NativeUint16Array.java5
-rw-r--r--src/jdk/nashorn/internal/objects/NativeUint32Array.java7
-rw-r--r--src/jdk/nashorn/internal/objects/NativeUint8Array.java5
-rw-r--r--src/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java5
-rw-r--r--src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java5
-rw-r--r--src/jdk/nashorn/internal/objects/annotations/SpecializedFunction.java129
-rw-r--r--src/jdk/nashorn/internal/parser/JSONParser.java1
-rw-r--r--src/jdk/nashorn/internal/parser/Parser.java86
-rw-r--r--src/jdk/nashorn/internal/runtime/AccessorProperty.java56
-rw-r--r--src/jdk/nashorn/internal/runtime/CodeStore.java40
-rw-r--r--src/jdk/nashorn/internal/runtime/CompiledFunction.java80
-rw-r--r--src/jdk/nashorn/internal/runtime/Context.java67
-rw-r--r--src/jdk/nashorn/internal/runtime/ECMAException.java22
-rw-r--r--src/jdk/nashorn/internal/runtime/FindProperty.java10
-rw-r--r--src/jdk/nashorn/internal/runtime/GlobalConstants.java189
-rw-r--r--src/jdk/nashorn/internal/runtime/JSType.java90
-rw-r--r--src/jdk/nashorn/internal/runtime/Property.java64
-rw-r--r--src/jdk/nashorn/internal/runtime/PropertyMap.java21
-rw-r--r--src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java37
-rw-r--r--src/jdk/nashorn/internal/runtime/ScriptEnvironment.java1
-rw-r--r--src/jdk/nashorn/internal/runtime/ScriptFunction.java10
-rw-r--r--src/jdk/nashorn/internal/runtime/ScriptObject.java291
-rw-r--r--src/jdk/nashorn/internal/runtime/SpillProperty.java4
-rw-r--r--src/jdk/nashorn/internal/runtime/UserAccessorProperty.java198
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/AnyElements.java38
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/ArrayData.java332
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/ArrayFilter.java34
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/ArrayIndex.java8
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/ContinuousArrayData.java69
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/DeletedArrayFilter.java27
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/DeletedRangeArrayFilter.java10
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/FrozenArrayFilter.java13
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/IntArrayData.java120
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/IteratorAction.java16
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/LengthNotWritableFilter.java198
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/LongArrayData.java111
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/NoTypeArrayData.java186
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/NonExtensibleArrayFilter.java68
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/NumberArrayData.java95
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/NumericElements.java2
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/ObjectArrayData.java84
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java80
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/TypedArrayData.java8
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/UndefinedArrayFilter.java19
-rw-r--r--src/jdk/nashorn/internal/runtime/events/RecompilationEvent.java3
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/Bootstrap.java112
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/BoundCallable.java96
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/BoundCallableLinker.java132
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/BoundDynamicMethodLinker.java91
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/BrowserJSObjectLinker.java26
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java11
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/JavaSuperAdapterLinker.java2
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java103
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java60
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/Analyser.java259
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/ApplyCaseFold.java6
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/ApplyCaseFoldArg.java1
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/ArrayCompiler.java80
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/BitSet.java48
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/BitStatus.java12
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/ByteCodeMachine.java217
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/ByteCodePrinter.java18
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/CodeRangeBuffer.java114
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/Compiler.java12
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/Config.java1
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/EncodingHelper.java18
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/Lexer.java235
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/Matcher.java241
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/MatcherFactory.java1
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/MinMaxLen.java54
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/NodeOptInfo.java9
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/OptAnchorInfo.java44
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/OptExactInfo.java55
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/OptMapInfo.java32
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/Option.java51
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/Parser.java83
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/Regex.java36
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/Region.java5
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/ScanEnvironment.java7
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/ScannerSupport.java26
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/SearchAlgorithm.java83
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/StackMachine.java126
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/Syntax.java4
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/WarnCallback.java1
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/Warnings.java1
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/ast/AnchorNode.java73
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/ast/AnyCharNode.java1
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/ast/BackRefNode.java1
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/ast/CClassNode.java112
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/ast/ConsAltNode.java12
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/ast/EncloseNode.java1
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/ast/Node.java37
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/ast/QuantifierNode.java39
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/ast/StateNode.java1
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/ast/StringNode.java1
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/constants/AnchorType.java1
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/constants/Arguments.java1
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/constants/AsmConstants.java1
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/constants/CCSTATE.java1
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/constants/CCVALTYPE.java1
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/constants/EncloseType.java1
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/constants/MetaChar.java1
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/constants/NodeStatus.java1
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/constants/NodeType.java1
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/constants/OPCode.java1
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/constants/OPSize.java1
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/constants/RegexState.java1
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/constants/StackPopLevel.java1
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/constants/StackType.java1
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/constants/StringType.java1
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/constants/SyntaxProperties.java1
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/constants/TargetInfo.java1
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/constants/TokenType.java1
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/constants/Traverse.java1
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/encoding/CharacterType.java1
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/encoding/IntHolder.java1
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/encoding/ObjPtr.java1
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/exception/ErrorMessages.java1
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/exception/InternalException.java1
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/exception/JOniException.java1
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/exception/SyntaxException.java1
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/joni/exception/ValueException.java3
-rw-r--r--src/jdk/nashorn/internal/runtime/resources/Messages.properties4
-rw-r--r--test/examples/getter-setter-micro.js81
-rw-r--r--test/script/basic/JDK-8020324.js.EXPECTED4
-rw-r--r--test/script/basic/JDK-8035312.js225
-rw-r--r--test/script/basic/JDK-8035312.js.EXPECTED186
-rw-r--r--test/script/basic/JDK-8035312_2.js65
-rw-r--r--test/script/basic/JDK-8035312_2.js.EXPECTED47
-rw-r--r--test/script/basic/JDK-8035312_3.js43
-rw-r--r--test/script/basic/JDK-8035312_3.js.EXPECTED3
-rw-r--r--test/script/basic/JDK-8035312_4.js59
-rw-r--r--test/script/basic/JDK-8035312_4.js.EXPECTED23
-rw-r--r--test/script/basic/JDK-8035312_5.js60
-rw-r--r--test/script/basic/JDK-8035312_5.js.EXPECTED6
-rw-r--r--test/script/basic/JDK-8049407-big-endian.js33
-rw-r--r--test/script/basic/JDK-8049407-big-endian.js.EXPECTED1
-rw-r--r--test/script/basic/JDK-8049407-payload.js37
-rw-r--r--test/script/basic/JDK-8049407.js33
-rw-r--r--test/script/basic/JDK-8049407.js.EXPECTED1
-rw-r--r--test/script/basic/JDK-8051778.js83
-rw-r--r--test/script/basic/JDK-8051778.js.EXPECTED10
-rw-r--r--test/script/basic/JDK-8057825.js45
-rw-r--r--test/script/basic/JDK-8057825.js.EXPECTED2
-rw-r--r--test/script/basic/JDK-8059443.js39
-rw-r--r--test/script/basic/JDK-8059443.js.EXPECTED4
-rw-r--r--test/script/basic/JDK-8061391.js151
-rw-r--r--test/script/basic/JDK-8061391.js.EXPECTED138
-rw-r--r--test/script/basic/JDK-8061391_2.js52
-rw-r--r--test/script/basic/JDK-8061391_3.js44
-rw-r--r--test/script/basic/JDK-8061391_3.js.EXPECTED1
-rw-r--r--test/script/basic/JDK-8061959.js35
-rw-r--r--test/script/basic/JDK-8061959.js.EXPECTED3
-rw-r--r--test/script/basic/JDK-8062024.js40
-rw-r--r--test/script/basic/JDK-8062024.js.EXPECTED4
-rw-r--r--test/script/basic/JDK-8062132.js80
-rw-r--r--test/script/basic/JDK-8062132.js.EXPECTED6
-rw-r--r--test/script/basic/JDK-8062381.js53
-rw-r--r--test/script/basic/JDK-8062381.js.EXPECTED32
-rw-r--r--test/script/basic/JDK-8062583.js51
-rw-r--r--test/script/basic/JDK-8062583.js.EXPECTED8
-rw-r--r--test/script/basic/JDK-8062624.js45
-rw-r--r--test/script/basic/JDK-8062624.js.EXPECTED16
-rw-r--r--test/script/basic/JDK-8062799.js103
-rw-r--r--test/script/basic/JDK-8062799.js.EXPECTED50
-rw-r--r--test/script/basic/JDK-8062937.js46
-rw-r--r--test/script/basic/JDK-8062937.js.EXPECTED9
-rw-r--r--test/script/basic/JDK-8066221.js31
-rw-r--r--test/script/basic/JDK-8066224.js38
-rw-r--r--test/script/basic/JDK-8066224.js.EXPECTED1
-rw-r--r--test/script/basic/JDK-8066225.js36
-rw-r--r--test/script/basic/JDK-8066225.js.EXPECTED2
-rw-r--r--test/script/basic/JDK-8066227.js40
-rw-r--r--test/script/basic/JDK-8066227.js.EXPECTED10
-rw-r--r--test/script/basic/JDK-8066230.js34
-rw-r--r--test/script/basic/JDK-8066230.js.EXPECTED1
-rw-r--r--test/script/basic/JDK-8066236.js46
-rw-r--r--test/script/basic/JDK-8066236.js.EXPECTED3
-rw-r--r--test/script/basic/JDK-8066669.js58
-rw-r--r--test/script/basic/JDK-8066669.js.EXPECTED13
-rw-r--r--test/script/basic/JDK-8066932.js48
-rw-r--r--test/script/basic/JDK-8067136.js69
-rw-r--r--test/script/basic/JDK-8067136.js.EXPECTED1
-rw-r--r--test/script/basic/JDK-8068573.js57
-rw-r--r--test/script/basic/JDK-8069002.js35
-rw-r--r--test/script/basic/NASHORN-377-big-endian.js33
-rw-r--r--test/script/basic/NASHORN-377-big-endian.js.EXPECTED34
-rw-r--r--test/script/basic/NASHORN-377-payload.js226
-rw-r--r--test/script/basic/NASHORN-377.js199
-rw-r--r--test/script/basic/compile-octane-normal.js4
-rw-r--r--test/script/basic/compile-octane-splitter.js4
-rw-r--r--test/script/basic/compile-octane.js2
-rw-r--r--test/script/basic/es6/const-empty.js2
-rw-r--r--test/script/basic/es6/const-redeclare-extra.js2
-rw-r--r--test/script/basic/es6/const-redeclare-extra.js.EXPECTED12
-rw-r--r--test/script/basic/es6/const-redeclare.js2
-rw-r--r--test/script/basic/es6/for-let-object-fields.js81
-rw-r--r--test/script/basic/es6/for-let-object-fields.js.EXPECTED33
-rw-r--r--test/script/basic/es6/for-let.js40
-rw-r--r--test/script/basic/es6/for-let.js.EXPECTED22
-rw-r--r--test/script/basic/es6/let-const-statement-context.js49
-rw-r--r--test/script/basic/es6/let-const-statement-context.js.EXPECTED30
-rw-r--r--test/script/basic/es6/let-const-switch.js45
-rw-r--r--test/script/basic/es6/let-const-switch.js.EXPECTED12
-rw-r--r--test/script/basic/es6/let-load.js18
-rw-r--r--test/script/basic/es6/let-load.js.EXPECTED5
-rw-r--r--test/script/basic/es6/let-redeclare-extra.js2
-rw-r--r--test/script/basic/es6/let-redeclare-extra.js.EXPECTED10
-rw-r--r--test/script/basic/es6/let-redeclare.js2
-rw-r--r--test/script/basic/es6/let_const_closure.js.EXPECTED6
-rw-r--r--test/script/basic/es6/let_const_reuse.js2
-rw-r--r--test/script/basic/es6/lexical-toplevel-def.js34
-rw-r--r--test/script/basic/es6/lexical-toplevel-print.js51
-rw-r--r--test/script/basic/es6/lexical-toplevel-redeclare-func-on-let.js31
-rw-r--r--test/script/basic/es6/lexical-toplevel-redeclare-let-on-builtin.js30
-rw-r--r--test/script/basic/es6/lexical-toplevel-redeclare-let-on-func.js31
-rw-r--r--test/script/basic/es6/lexical-toplevel-redeclare-let-on-global.js30
-rw-r--r--test/script/basic/es6/lexical-toplevel-redeclare-let-on-var.js31
-rw-r--r--test/script/basic/es6/lexical-toplevel-redeclare-var-on-let.js31
-rw-r--r--test/script/basic/es6/lexical-toplevel-redeclare.js78
-rw-r--r--test/script/basic/es6/lexical-toplevel-redeclare.js.EXPECTED100
-rw-r--r--test/script/basic/es6/lexical-toplevel.js35
-rw-r--r--test/script/basic/es6/lexical-toplevel.js.EXPECTED32
-rw-r--r--test/script/basic/fastpushpop.js.EXPECTED4
-rw-r--r--test/script/basic/list.js.EXPECTED2
-rw-r--r--test/script/nosecurity/JDK-8050964.js1
-rw-r--r--test/script/nosecurity/JDK-8055034.js2
-rw-r--r--test/src/jdk/internal/dynalink/beans/CallerSensitiveTest.java37
-rw-r--r--test/src/jdk/nashorn/api/javaaccess/ArrayConversionTest.java6
-rw-r--r--test/src/jdk/nashorn/api/javaaccess/BooleanAccessTest.java2
-rw-r--r--test/src/jdk/nashorn/api/javaaccess/ConsStringTest.java6
-rw-r--r--test/src/jdk/nashorn/api/javaaccess/MethodAccessTest.java2
-rw-r--r--test/src/jdk/nashorn/api/javaaccess/NumberAccessTest.java2
-rw-r--r--test/src/jdk/nashorn/api/javaaccess/NumberBoxingTest.java2
-rw-r--r--test/src/jdk/nashorn/api/javaaccess/ObjectAccessTest.java2
-rw-r--r--test/src/jdk/nashorn/api/javaaccess/Person.java1
-rw-r--r--test/src/jdk/nashorn/api/javaaccess/SharedObject.java1
-rw-r--r--test/src/jdk/nashorn/api/javaaccess/StringAccessTest.java2
-rw-r--r--test/src/jdk/nashorn/api/scripting/InvocableTest.java16
-rw-r--r--test/src/jdk/nashorn/api/scripting/MultipleEngineTest.java2
-rw-r--r--test/src/jdk/nashorn/api/scripting/PluggableJSObjectTest.java2
-rw-r--r--test/src/jdk/nashorn/api/scripting/ScopeTest.java4
-rw-r--r--test/src/jdk/nashorn/api/scripting/ScriptEngineSecurityTest.java27
-rw-r--r--test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java49
-rw-r--r--test/src/jdk/nashorn/api/scripting/ScriptObjectMirrorTest.java7
-rw-r--r--test/src/jdk/nashorn/api/scripting/VariableArityTestInterface.java1
-rw-r--r--test/src/jdk/nashorn/api/scripting/Window.java1
-rw-r--r--test/src/jdk/nashorn/api/scripting/WindowEventHandler.java3
-rw-r--r--test/src/jdk/nashorn/internal/codegen/CompilerTest.java4
-rw-r--r--test/src/jdk/nashorn/internal/parser/ParserTest.java2
-rw-r--r--test/src/jdk/nashorn/internal/performance/AuroraWrapper.java1
-rw-r--r--test/src/jdk/nashorn/internal/performance/OctaneTest.java15
-rw-r--r--test/src/jdk/nashorn/internal/performance/PerformanceWrapper.java5
-rw-r--r--test/src/jdk/nashorn/internal/performance/SplayTest.java5
-rw-r--r--test/src/jdk/nashorn/internal/runtime/ClassFilterTest.java62
-rw-r--r--test/src/jdk/nashorn/internal/runtime/CodeStoreAndPathTest.java66
-rw-r--r--test/src/jdk/nashorn/internal/runtime/ContextTest.java6
-rw-r--r--test/src/jdk/nashorn/internal/runtime/ExceptionsNotSerializable.java4
-rw-r--r--test/src/jdk/nashorn/internal/runtime/LexicalBindingTest.java212
-rw-r--r--test/src/jdk/nashorn/internal/runtime/NoPersistenceCachingTest.java4
-rw-r--r--test/src/jdk/nashorn/internal/runtime/SourceTest.java6
-rw-r--r--test/src/jdk/nashorn/internal/runtime/TrustedScriptEngineTest.java23
-rw-r--r--test/src/jdk/nashorn/internal/runtime/regexp/joni/JoniTest.java1
-rw-r--r--test/src/jdk/nashorn/internal/test/framework/AbstractScriptRunnable.java8
-rw-r--r--test/src/jdk/nashorn/internal/test/framework/OrphanTestFinder.java1
-rw-r--r--test/src/jdk/nashorn/internal/test/framework/ParallelTestRunner.java10
-rw-r--r--test/src/jdk/nashorn/internal/test/framework/ScriptRunnable.java1
-rw-r--r--test/src/jdk/nashorn/internal/test/framework/ScriptTest.java3
-rw-r--r--test/src/jdk/nashorn/internal/test/framework/TestConfig.java1
-rw-r--r--test/src/jdk/nashorn/internal/test/framework/TestFinder.java15
-rw-r--r--test/src/jdk/nashorn/internal/test/framework/TestHelper.java1
-rw-r--r--test/src/jdk/nashorn/internal/test/framework/TestReorderInterceptor.java5
-rw-r--r--test/src/jdk/nashorn/internal/test/models/InternalRunnable.java1
-rw-r--r--test/src/jdk/nashorn/internal/test/models/RestrictedRunnable.java2
-rw-r--r--test/src/jdk/nashorn/test/models/ClassLoaderAware.java32
-rw-r--r--test/src/jdk/nashorn/test/models/ClassWithFinalFinalizer.java3
-rw-r--r--test/src/jdk/nashorn/test/models/ClassWithInheritedFinalFinalizer.java2
-rw-r--r--test/src/jdk/nashorn/test/models/ConstructorWithArgument.java1
-rw-r--r--test/src/jdk/nashorn/test/models/DessertTopping.java1
-rw-r--r--test/src/jdk/nashorn/test/models/DessertToppingFloorWaxDriver.java1
-rw-r--r--test/src/jdk/nashorn/test/models/FinalClass.java1
-rw-r--r--test/src/jdk/nashorn/test/models/FloorWax.java1
-rw-r--r--test/src/jdk/nashorn/test/models/IntFloatOverloadSelection.java1
-rw-r--r--test/src/jdk/nashorn/test/models/InternalRunnableSuperclass.java3
-rw-r--r--test/src/jdk/nashorn/test/models/Jdk8011362TestSubject.java3
-rw-r--r--test/src/jdk/nashorn/test/models/Nashorn401TestSubject.java1
-rw-r--r--test/src/jdk/nashorn/test/models/NoAccessibleConstructorClass.java1
-rw-r--r--test/src/jdk/nashorn/test/models/NullProvider.java34
-rw-r--r--test/src/jdk/nashorn/test/models/OuterClass.java8
-rw-r--r--test/src/jdk/nashorn/test/models/OverloadedSam.java1
-rw-r--r--test/src/jdk/nashorn/test/models/OverloadedSetter.java (renamed from src/jdk/nashorn/internal/runtime/linker/BoundDynamicMethod.java)37
-rw-r--r--test/src/jdk/nashorn/test/models/OverrideObject.java1
-rw-r--r--test/src/jdk/nashorn/test/models/PropertyBind.java1
-rw-r--r--test/src/jdk/nashorn/test/models/SourceHelper.java1
-rw-r--r--test/src/jdk/nashorn/test/models/StringArgs.java1
-rw-r--r--test/src/jdk/nashorn/test/models/Toothpaste.java1
-rw-r--r--test/src/jdk/nashorn/test/models/VarArgConstructor.java1
-rw-r--r--test/src/jdk/nashorn/test/tools/StaticTypeInspector.java1
362 files changed, 9904 insertions, 2962 deletions
diff --git a/.hgtags b/.hgtags
index db0b2204..4500897f 100644
--- a/.hgtags
+++ b/.hgtags
@@ -308,6 +308,8 @@ ad36f9454ce38d78be39fc819902e1223765ee5e jdk8u20-b23
d3da140e179343011017669a6dbfcc52b0e56f52 jdk8u20-b24
d3da140e179343011017669a6dbfcc52b0e56f52 jdk8u20-b25
a23ac9db4227d78b3389e01fa94a8cb695a8fb0a jdk8u20-b26
+aa30541c5f0db0d03ae6625268642ac71f59c4e6 jdk8u20-b31
+bc4b5edeb8268a75718e65a84864f09c95b3032c jdk8u20-b32
7001e9f95b443a75e432205a29974c05b88e0fdc jdk8u25-b00
a9f77bd14874d5f8fdf935704dd54a0451f2bc69 jdk8u25-b01
895e47783e2ee6823496a5ae84039a4f50311c7d jdk8u25-b02
@@ -327,6 +329,21 @@ f76715cd4e902602bdbb4ba9a3774c10afeee012 jdk8u25-b12
1500138ce513600457be6bfa10979ecce6515aa6 jdk8u25-b16
4b9cc65dd24d398c4f921c0beccfb8caeaaaf584 jdk8u25-b17
cdbf34dbef404b47805c8c85b11c65c2afaa6674 jdk8u25-b18
+4f9e65387c21831d0ea5726641a302c2ce73a4cc jdk8u25-b31
+9b692a6e5f22228f822973d35610d37cb9dd9693 jdk8u31-b00
+6bf53bb6c969678488b1c073d56dd55df1a0ea17 jdk8u31-b01
+809bf97d7e70dcb3873fcbc10f12f62580b1c11d jdk8u31-b02
+3505d266634ded89bf9617ff6b385ab8a52f78cf jdk8u31-b03
+96acff2ad9e19aa80c4f7ed60d87a422bca1ea91 jdk8u31-b04
+5fc3f210872d365c57ed4e8dba3926d9ed5c7e45 jdk8u31-b05
+99a3333f7f8489bb3c80f0c0643ae19e549a0941 jdk8u31-b06
+5ed4fa732b26b6d8e37dfc5bbd00047c5352719b jdk8u31-b07
+b17ecf341ee544cc5507b9b586c14a13c3adc058 jdk8u31-b08
+762eaacc45cec3f7d593bedd08fb8de478d4415b jdk8u31-b09
+c68ba913a0eeea6eb94d9568e9985505ec3408a3 jdk8u31-b10
+599bd596fa549d882aa8fc5104c322a75a3af728 jdk8u31-b11
+f36c71a03e4ed467f630cc46d076a5bb4c58b6d5 jdk8u31-b12
+ec36fa3b35eb00f053d624ae837579c6b8e446ac jdk8u31-b13
f2925491b61b22ac42f8c30ee9c6723ffa401a4c jdk8u40-b00
62468d841b842769d875bd97d10370585c296eb7 jdk8u40-b01
bdd9c38d1e61edbf770b8733b70a37d0cf0e7055 jdk8u40-b02
@@ -342,3 +359,11 @@ a2e0a985764b5afd5f316429bfab4f44bf150f7f jdk8u40-b03
4f97f0da29eb78c8234ada7b7cb874a494ee653e jdk8u40-b12-aarch64
af0397959d778072cca638774c9b53ed3787fcfb jdk8u40-b12-aarch64-1262
e67a19f29c7095700105ca2b09b8bb21ad02e078 jdk8u40-b12-aarch64-1263
+fc37699ddc0ed41d4ab5da821211a6d2648c8883 jdk8u40-b15
+e079f3f6d536510b1ab3589b1038d893d78302ac jdk8u40-b16
+88e22262fdb26e3154a1034c2413415e97b9a86a jdk8u40-b17
+653739706172ae94e999731a3a9f10f8ce11ffca jdk8u40-b18
+6ec61d2494283fbaca6df227f1a5b45487dc1ca7 jdk8u40-b19
+4d240320929f7b2247eeb97e43efe2370b70582e jdk8u40-b20
+dbb663a9d9aa2807ef501c7d20f29415816a1973 jdk8u40-b21
+f9f70a0f60f48fbb95275b6c1110cedf740c6177 jdk8u40-b22
diff --git a/THIRD_PARTY_README b/THIRD_PARTY_README
index 6d1c60f2..c34ce6b3 100644
--- a/THIRD_PARTY_README
+++ b/THIRD_PARTY_README
@@ -3385,7 +3385,7 @@ with JRE 8, JDK 8, and OpenJDK 8.
included with JRE 8, JDK 8, and OpenJDK 8.
Apache Commons Math 3.2
- Apache Derby 10.10.1.3
+ Apache Derby 10.11.1.2
Apache Jakarta BCEL 5.1
Apache Jakarta Regexp 1.4
Apache Santuario XML Security for Java 1.5.4
diff --git a/bin/runopt.sh b/bin/runopt.sh
index 6b26e287..5ec9046b 100644
--- a/bin/runopt.sh
+++ b/bin/runopt.sh
@@ -28,14 +28,35 @@
# known flags for performance for the current configration
###########################################################################################
+# Flags to enable assertions, we need the system assertions too, since
+# this script runs Nashorn in the BCP to override any nashorn.jar that might
+# reside in your $JAVA_HOME/jre/lib/ext/nashorn.jar
+#
+ENABLE_ASSERTIONS_FLAGS="-ea -esa"
+
# Flags to instrument lambdaform computation, caching, interpretation and compilation
# Default compile threshold for lambdaforms is 30
-#FLAGS="-Djava.lang.invoke.MethodHandle.COMPILE_THRESHOLD=3 -Djava.lang.invoke.MethodHandle.DUMP_CLASS_FILES=true -Djava.lang.invoke.MethodHandle.TRACE_METHOD_LINKAGE=true -Djava.lang.invoke.MethodHandle.TRACE_INTERPRETER=true"
-
+#
+#LAMBDAFORM_FLAGS="\
+# -Djava.lang.invoke.MethodHandle.COMPILE_THRESHOLD=3 \
+# -Djava.lang.invoke.MethodHandle.DUMP_CLASS_FILES=true \
+# -Djava.lang.invoke.MethodHandle.TRACE_METHOD_LINKAGE=true \
+# -Djava.lang.invoke.MethodHandle.TRACE_INTERPRETER=true"
# Flags to run trusted tests from the Nashorn test suite
-#FLAGS="-Djava.security.manager -Djava.security.policy=../build/nashorn.policy -Dnashorn.debug"
+#
+#TRUSTED_TEST_FLAGS="\
+#-Djava.security.manager \
+#-Djava.security.policy=../build/nashorn.policy -Dnashorn.debug"
+# Testing out new code optimizations using the generic hotspot "new code" parameter
+#
+#USE_NEW_CODE_FLAGS=-XX:+UnlockDiagnosticVMOptions -XX:+UseNewCode
+
+#
+#-Dnashorn.typeInfo.disabled=false \
+# and for Nashorn options:
+# --class-cache-size=0 --persistent-code-cache=false
# Unique timestamped file name for JFR recordings. For JFR, we also have to
# crank up the stack cutoff depth to 1024, because of ridiculously long lambda form
@@ -46,8 +67,42 @@
# can go (10 ms on most platforms). The default is normally higher. The increased
# sampling overhead is usually negligible for Nashorn runs, but the data is better
-JFR_FILENAME="./nashorn_$(date|sed "s/ /_/g"|sed "s/:/_/g").jfr"
+if [ -z $JFR_FILENAME ]; then
+ JFR_FILENAME="./nashorn_$(date|sed "s/ /_/g"|sed "s/:/_/g").jfr"
+fi
+
+# Flight recorder
+#
+# see above - already in place, copy the flags down here to disable
+ENABLE_FLIGHT_RECORDER_FLAGS="\
+ -XX:+UnlockCommercialFeatures \
+ -XX:+FlightRecorder \
+ -XX:FlightRecorderOptions=defaultrecording=true,disk=true,dumponexit=true,dumponexitpath=$JFR_FILENAME,stackdepth=1024"
+
+# Type specialization and math intrinsic replacement should be enabled by default in 8u20 and nine,
+# keeping this flag around for experimental reasons. Replace + with - to switch it off
+#
+#ENABLE_TYPE_SPECIALIZATION_FLAGS=-XX:+UseTypeSpeculation
+
+# Same with math intrinsics. They should be enabled by default in 8u20 and 9, so
+# this disables them if needed
+#
+#DISABLE_MATH_INTRINSICS_FLAGS=-XX:-UseMathExactIntrinsics
+
+# Add timing to time the compilation phases.
+#ENABLE_TIME_FLAGS=--log=time
+
+# Add ShowHiddenFrames to get lambda form internals on the stack traces
+#ENABLE_SHOW_HIDDEN_FRAMES_FLAGS=-XX:+ShowHiddenFrames
+
+# Add print optoassembly to get an asm dump. This requires 1) a debug build, not product,
+# That tired compilation is switched off, for C2 only output and that the number of
+# compiler threads is set to 1 for determinsm.
+#
+#PRINT_ASM_FLAGS=-XX:+PrintOptoAssembly -XX:-TieredCompilation -XX:CICompilerCount=1 \
+# Tier compile threasholds. Default value is 10. (1-100 is useful for experiments)
+#TIER_COMPILATION_THRESHOLD_FLAGS=-XX:IncreaseFirstTierCompileThresholdAt=10
# Directory where to look for nashorn.jar in a dist folder. The default is "..", assuming
# that we run the script from the make dir
@@ -59,49 +114,23 @@ NASHORN_JAR=$DIR/dist/nashorn.jar
# nashorn.jar in $JAVA_HOME/jre/lib/ext. Thus, we also need -esa, as assertions in
# nashorn count as system assertions in this configuration
+# Type profiling default level is 111, 222 adds some compile time, but is faster
+
$JAVA_HOME/bin/java \
-$FLAGS \
--ea \
--esa \
+$ENABLE_ASSERTIONS_FLAGS \
+$LAMBDAFORM_FLAGS \
+$TRUSTED_FLAGS \
+$USE_NEW_CODE_FLAGS \
+$ENABLE_SHOW_HIDDEN_FRAMES_FLAGS \
+$ENABLE_FLIGHT_RECORDER_FLAGS \
+$ENABLE_TYPE_SPECIALIZATION_FLAGS \
+$TIERED_COMPILATION_THRESOLD_FLAGS \
+$DISABLE_MATH_INTRINSICS_FLAGS \
+$PRINT_ASM_FLAGS \
-Xbootclasspath/p:$NASHORN_JAR \
-Xms2G -Xmx2G \
+-XX:TypeProfileLevel=222 \
-cp $CLASSPATH:../build/test/classes/ \
-jdk.nashorn.tools.Shell ${@}
-
-# Below are flags that may come in handy, but aren't used for default runs
-
-
-# Type profiling default level is 111, 222 adds some compile time, but produces better code.
-# -XX:TypeProfileLevel=222 \
-
-
-# Testing out new code optimizations using the generic hotspot "new code" parameter
-#-XX:+UnlockDiagnosticVMOptions \
-#-XX:+UseNewCode \
-
-
-# Type specialization and math intrinsic replacement should be enabled by default in 8u20 and nine,
-# keeping this flag around for experimental reasons. Replace + with - to switch it off
-#-XX:+UseTypeSpeculation \
-
+jdk.nashorn.tools.Shell $ENABLE_TIME_FLAGS ${@}
-# Same with math intrinsics. They should be enabled by default in 8u20 and 9
-#-XX:+UseMathExactIntrinsics \
-
-
-# Add -Dnashorn.time to time the compilation phases.
-#-Dnashorn.time \
-
-
-# Add ShowHiddenFrames to get lambda form internals on the stack traces
-#-XX:+ShowHiddenFrames \
-
-
-# Add print optoassembly to get an asm dump. This requires 1) a debug build, not product,
-# That tired compilation is switched off, for C2 only output and that the number of
-# compiler threads is set to 1 for determinsm.
-#-XX:+PrintOptoAssembly -XX:-TieredCompilation -XX:CICompilerCount=1 \
-
-# Tier compile threasholds. Default value is 10. (1-100 is useful for experiments)
-# -XX:IncreaseFirstTierCompileThresholdAt=XX
diff --git a/docs/source/EvalFile.java b/docs/source/EvalFile.java
index 6bb4e381..b12e91bb 100644
--- a/docs/source/EvalFile.java
+++ b/docs/source/EvalFile.java
@@ -29,14 +29,16 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-import javax.script.*;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+@SuppressWarnings("javadoc")
public class EvalFile {
- public static void main(String[] args) throws Exception {
+ public static void main(final String[] args) throws Exception {
// create a script engine manager
- ScriptEngineManager factory = new ScriptEngineManager();
+ final ScriptEngineManager factory = new ScriptEngineManager();
// create JavaScript engine
- ScriptEngine engine = factory.getEngineByName("nashorn");
+ final ScriptEngine engine = factory.getEngineByName("nashorn");
// evaluate JavaScript code from given file - specified by first argument
engine.eval(new java.io.FileReader(args[0]));
}
diff --git a/docs/source/EvalScript.java b/docs/source/EvalScript.java
index 7fcbe1cd..49f581de 100644
--- a/docs/source/EvalScript.java
+++ b/docs/source/EvalScript.java
@@ -29,14 +29,16 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-import javax.script.*;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+@SuppressWarnings("javadoc")
public class EvalScript {
- public static void main(String[] args) throws Exception {
+ public static void main(final String[] args) throws Exception {
// create a script engine manager
- ScriptEngineManager factory = new ScriptEngineManager();
+ final ScriptEngineManager factory = new ScriptEngineManager();
// create a JavaScript engine
- ScriptEngine engine = factory.getEngineByName("nashorn");
+ final ScriptEngine engine = factory.getEngineByName("nashorn");
// evaluate JavaScript code from String
engine.eval("print('Hello, World')");
}
diff --git a/docs/source/InvokeScriptFunction.java b/docs/source/InvokeScriptFunction.java
index 26de36c4..5ce1cae0 100644
--- a/docs/source/InvokeScriptFunction.java
+++ b/docs/source/InvokeScriptFunction.java
@@ -29,22 +29,25 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-import javax.script.*;
+import javax.script.Invocable;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+@SuppressWarnings("javadoc")
public class InvokeScriptFunction {
- public static void main(String[] args) throws Exception {
- ScriptEngineManager manager = new ScriptEngineManager();
- ScriptEngine engine = manager.getEngineByName("nashorn");
+ public static void main(final String[] args) throws Exception {
+ final ScriptEngineManager manager = new ScriptEngineManager();
+ final ScriptEngine engine = manager.getEngineByName("nashorn");
// JavaScript code in a String
- String script = "function hello(name) { print('Hello, ' + name); }";
+ final String script = "function hello(name) { print('Hello, ' + name); }";
// evaluate script
engine.eval(script);
// javax.script.Invocable is an optional interface.
// Check whether your script engine implements or not!
// Note that the JavaScript engine implements Invocable interface.
- Invocable inv = (Invocable) engine;
+ final Invocable inv = (Invocable) engine;
// invoke the global function named "hello"
inv.invokeFunction("hello", "Scripting!!" );
diff --git a/docs/source/InvokeScriptMethod.java b/docs/source/InvokeScriptMethod.java
index a3f5ece7..d3cc788d 100644
--- a/docs/source/InvokeScriptMethod.java
+++ b/docs/source/InvokeScriptMethod.java
@@ -29,26 +29,29 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-import javax.script.*;
+import javax.script.Invocable;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+@SuppressWarnings("javadoc")
public class InvokeScriptMethod {
- public static void main(String[] args) throws Exception {
- ScriptEngineManager manager = new ScriptEngineManager();
- ScriptEngine engine = manager.getEngineByName("nashorn");
+ public static void main(final String[] args) throws Exception {
+ final ScriptEngineManager manager = new ScriptEngineManager();
+ final ScriptEngine engine = manager.getEngineByName("nashorn");
// JavaScript code in a String. This code defines a script object 'obj'
// with one method called 'hello'.
- String script = "var obj = new Object(); obj.hello = function(name) { print('Hello, ' + name); }";
+ final String script = "var obj = new Object(); obj.hello = function(name) { print('Hello, ' + name); }";
// evaluate script
engine.eval(script);
// javax.script.Invocable is an optional interface.
// Check whether your script engine implements or not!
// Note that the JavaScript engine implements Invocable interface.
- Invocable inv = (Invocable) engine;
+ final Invocable inv = (Invocable) engine;
// get script object on which we want to call the method
- Object obj = engine.get("obj");
+ final Object obj = engine.get("obj");
// invoke the method named "hello" on the script object "obj"
inv.invokeMethod(obj, "hello", "Script Method !!" );
diff --git a/docs/source/MultiScopes.java b/docs/source/MultiScopes.java
index 6c4fa2a2..ff4f65b4 100644
--- a/docs/source/MultiScopes.java
+++ b/docs/source/MultiScopes.java
@@ -29,12 +29,17 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-import javax.script.*;
+import javax.script.Bindings;
+import javax.script.ScriptContext;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+import javax.script.SimpleScriptContext;
+@SuppressWarnings("javadoc")
public class MultiScopes {
- public static void main(String[] args) throws Exception {
- ScriptEngineManager manager = new ScriptEngineManager();
- ScriptEngine engine = manager.getEngineByName("nashorn");
+ public static void main(final String[] args) throws Exception {
+ final ScriptEngineManager manager = new ScriptEngineManager();
+ final ScriptEngine engine = manager.getEngineByName("nashorn");
engine.put("x", "hello");
// print global variable "x"
@@ -42,9 +47,9 @@ public class MultiScopes {
// the above line prints "hello"
// Now, pass a different script context
- ScriptContext newContext = new SimpleScriptContext();
+ final ScriptContext newContext = new SimpleScriptContext();
newContext.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE);
- Bindings engineScope = newContext.getBindings(ScriptContext.ENGINE_SCOPE);
+ final Bindings engineScope = newContext.getBindings(ScriptContext.ENGINE_SCOPE);
// add new variable "x" to the new engineScope
engineScope.put("x", "world");
diff --git a/docs/source/RunnableImpl.java b/docs/source/RunnableImpl.java
index 1d858d40..2b5113b1 100644
--- a/docs/source/RunnableImpl.java
+++ b/docs/source/RunnableImpl.java
@@ -29,28 +29,31 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-import javax.script.*;
+import javax.script.Invocable;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+@SuppressWarnings("javadoc")
public class RunnableImpl {
- public static void main(String[] args) throws Exception {
- ScriptEngineManager manager = new ScriptEngineManager();
- ScriptEngine engine = manager.getEngineByName("nashorn");
+ public static void main(final String[] args) throws Exception {
+ final ScriptEngineManager manager = new ScriptEngineManager();
+ final ScriptEngine engine = manager.getEngineByName("nashorn");
// JavaScript code in a String
- String script = "function run() { print('run called'); }";
+ final String script = "function run() { print('run called'); }";
// evaluate script
engine.eval(script);
- Invocable inv = (Invocable) engine;
+ final Invocable inv = (Invocable) engine;
// get Runnable interface object from engine. This interface methods
// are implemented by script functions with the matching name.
- Runnable r = inv.getInterface(Runnable.class);
+ final Runnable r = inv.getInterface(Runnable.class);
// start a new thread that runs the script implemented
// runnable interface
- Thread th = new Thread(r);
+ final Thread th = new Thread(r);
th.start();
th.join();
}
diff --git a/docs/source/RunnableImplObject.java b/docs/source/RunnableImplObject.java
index 877f8c15..0d5f5032 100644
--- a/docs/source/RunnableImplObject.java
+++ b/docs/source/RunnableImplObject.java
@@ -29,31 +29,34 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-import javax.script.*;
+import javax.script.Invocable;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+@SuppressWarnings("javadoc")
public class RunnableImplObject {
- public static void main(String[] args) throws Exception {
- ScriptEngineManager manager = new ScriptEngineManager();
- ScriptEngine engine = manager.getEngineByName("nashorn");
+ public static void main(final String[] args) throws Exception {
+ final ScriptEngineManager manager = new ScriptEngineManager();
+ final ScriptEngine engine = manager.getEngineByName("nashorn");
// JavaScript code in a String
- String script = "var obj = new Object(); obj.run = function() { print('run method called'); }";
+ final String script = "var obj = new Object(); obj.run = function() { print('run method called'); }";
// evaluate script
engine.eval(script);
// get script object on which we want to implement the interface with
- Object obj = engine.get("obj");
+ final Object obj = engine.get("obj");
- Invocable inv = (Invocable) engine;
+ final Invocable inv = (Invocable) engine;
// get Runnable interface object from engine. This interface methods
// are implemented by script methods of object 'obj'
- Runnable r = inv.getInterface(obj, Runnable.class);
+ final Runnable r = inv.getInterface(obj, Runnable.class);
// start a new thread that runs the script implemented
// runnable interface
- Thread th = new Thread(r);
+ final Thread th = new Thread(r);
th.start();
th.join();
}
diff --git a/docs/source/ScriptVars.java b/docs/source/ScriptVars.java
index 7e16cfca..c697431d 100644
--- a/docs/source/ScriptVars.java
+++ b/docs/source/ScriptVars.java
@@ -29,15 +29,17 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-import javax.script.*;
-import java.io.*;
+import java.io.File;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+@SuppressWarnings("javadoc")
public class ScriptVars {
- public static void main(String[] args) throws Exception {
- ScriptEngineManager manager = new ScriptEngineManager();
- ScriptEngine engine = manager.getEngineByName("nashorn");
+ public static void main(final String[] args) throws Exception {
+ final ScriptEngineManager manager = new ScriptEngineManager();
+ final ScriptEngine engine = manager.getEngineByName("nashorn");
- File f = new File("test.txt");
+ final File f = new File("test.txt");
// expose File object as variable to script
engine.put("file", f);
diff --git a/make/build.xml b/make/build.xml
index f6de5589..a7539f48 100644
--- a/make/build.xml
+++ b/make/build.xml
@@ -155,6 +155,7 @@
<fileset dir="${src.dir}/jdk/nashorn/tools/resources/"/>
</copy>
<copy file="${src.dir}/jdk/internal/dynalink/support/messages.properties" todir="${build.classes.dir}/jdk/internal/dynalink/support"/>
+ <copy file="${src.dir}/jdk/nashorn/internal/codegen/anchor.properties" todir="${build.classes.dir}/jdk/nashorn/internal/codegen"/>
<echo message="full=${nashorn.fullversion}" file="${build.classes.dir}/jdk/nashorn/internal/runtime/resources/version.properties"/>
<echo file="${build.classes.dir}/jdk/nashorn/internal/runtime/resources/version.properties" append="true">${line.separator}</echo>
diff --git a/samples/browser_dom.js b/samples/browser_dom.js
new file mode 100644
index 00000000..94324c7a
--- /dev/null
+++ b/samples/browser_dom.js
@@ -0,0 +1,91 @@
+#// Usage: jjs -fx browser.js
+
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+if (!$OPTIONS._fx) {
+ print("Usage: jjs -fx browser.js");
+ exit(1);
+}
+
+// JavaFX classes used
+var ChangeListener = Java.type("javafx.beans.value.ChangeListener");
+var Scene = Java.type("javafx.scene.Scene");
+var WebView = Java.type("javafx.scene.web.WebView");
+var EventListener = Java.type("org.w3c.dom.events.EventListener");
+
+// JavaFX start method
+function start(stage) {
+ start.title = "Web View";
+ var wv = new WebView();
+ wv.engine.loadContent(<<EOF
+<html>
+<head>
+<title>
+This is the title
+</title>
+<script>
+// click count for OK button
+var okCount = 0;
+</script>
+</head>
+<body>
+Button from the input html<br>
+<button type="button" onclick="okCount++">OK</button><br>
+</body>
+</html>
+EOF, "text/html");
+
+ // attach onload handler
+ wv.engine.loadWorker.stateProperty().addListener(
+ new ChangeListener() {
+ changed: function() {
+ // DOM document element
+ var document = wv.engine.document;
+ // DOM manipulation
+ var btn = document.createElement("button");
+ var n = 0;
+ // attach a button handler - nashorn function!
+ btn.onclick = new EventListener(function() {
+ n++; print("You clicked " + n + " time(s)");
+ print("you clicked OK " + wv.engine.executeScript("okCount"));
+ });
+ // attach text to button
+ var t = document.createTextNode("Click Me!");
+ btn.appendChild(t);
+ // attach button to the document
+ document.body.appendChild(btn);
+ }
+ }
+ );
+ stage.scene = new Scene(wv, 750, 500);
+ stage.show();
+}
diff --git a/src/jdk/internal/dynalink/DynamicLinkerFactory.java b/src/jdk/internal/dynalink/DynamicLinkerFactory.java
index af5eb119..2f4e5f33 100644
--- a/src/jdk/internal/dynalink/DynamicLinkerFactory.java
+++ b/src/jdk/internal/dynalink/DynamicLinkerFactory.java
@@ -97,6 +97,7 @@ import jdk.internal.dynalink.beans.BeansLinker;
import jdk.internal.dynalink.linker.GuardingDynamicLinker;
import jdk.internal.dynalink.linker.GuardingTypeConverterFactory;
import jdk.internal.dynalink.linker.LinkRequest;
+import jdk.internal.dynalink.linker.MethodTypeConversionStrategy;
import jdk.internal.dynalink.support.AutoDiscovery;
import jdk.internal.dynalink.support.BottomGuardingDynamicLinker;
import jdk.internal.dynalink.support.ClassLoaderGetterContextProvider;
@@ -105,6 +106,7 @@ import jdk.internal.dynalink.support.CompositeTypeBasedGuardingDynamicLinker;
import jdk.internal.dynalink.support.DefaultPrelinkFilter;
import jdk.internal.dynalink.support.LinkerServicesImpl;
import jdk.internal.dynalink.support.TypeConverterFactory;
+import jdk.internal.dynalink.support.TypeUtilities;
/**
* A factory class for creating {@link DynamicLinker}s. The most usual dynamic linker is a linker that is a composition
@@ -115,7 +117,6 @@ import jdk.internal.dynalink.support.TypeConverterFactory;
* @author Attila Szegedi
*/
public class DynamicLinkerFactory {
-
/**
* Default value for {@link #setUnstableRelinkThreshold(int) unstable relink threshold}.
*/
@@ -130,6 +131,7 @@ public class DynamicLinkerFactory {
private boolean syncOnRelink = false;
private int unstableRelinkThreshold = DEFAULT_UNSTABLE_RELINK_THRESHOLD;
private GuardedInvocationFilter prelinkFilter;
+ private MethodTypeConversionStrategy autoConversionStrategy;
/**
* Sets the class loader for automatic discovery of available linkers. If not set explicitly, then the thread
@@ -259,6 +261,29 @@ public class DynamicLinkerFactory {
}
/**
+ * Sets an object representing the conversion strategy for automatic type conversions. After
+ * {@link TypeConverterFactory#asType(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType)} has
+ * applied all custom conversions to a method handle, it still needs to effect
+ * {@link TypeUtilities#isMethodInvocationConvertible(Class, Class) method invocation conversions} that
+ * can usually be automatically applied as per
+ * {@link java.lang.invoke.MethodHandle#asType(java.lang.invoke.MethodType)}.
+ * However, sometimes language runtimes will want to customize even those conversions for their own call
+ * sites. A typical example is allowing unboxing of null return values, which is by default prohibited by
+ * ordinary {@code MethodHandles.asType}. In this case, a language runtime can install its own custom
+ * automatic conversion strategy, that can deal with null values. Note that when the strategy's
+ * {@link MethodTypeConversionStrategy#asType(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType)}
+ * is invoked, the custom language conversions will already have been applied to the method handle, so by
+ * design the difference between the handle's current method type and the desired final type will always
+ * only be ones that can be subjected to method invocation conversions. The strategy also doesn't need to
+ * invoke a final {@code MethodHandle.asType()} as the converter factory will do that as the final step.
+ * @param autoConversionStrategy the strategy for applying method invocation conversions for the linker
+ * created by this factory.
+ */
+ public void setAutoConversionStrategy(final MethodTypeConversionStrategy autoConversionStrategy) {
+ this.autoConversionStrategy = autoConversionStrategy;
+ }
+
+ /**
* Creates a new dynamic linker consisting of all the prioritized, autodiscovered, and fallback linkers as well as
* the pre-link filter.
*
@@ -324,8 +349,9 @@ public class DynamicLinkerFactory {
prelinkFilter = new DefaultPrelinkFilter();
}
- return new DynamicLinker(new LinkerServicesImpl(new TypeConverterFactory(typeConverters), composite),
- prelinkFilter, runtimeContextArgCount, syncOnRelink, unstableRelinkThreshold);
+ return new DynamicLinker(new LinkerServicesImpl(new TypeConverterFactory(typeConverters,
+ autoConversionStrategy), composite), prelinkFilter, runtimeContextArgCount, syncOnRelink,
+ unstableRelinkThreshold);
}
private static ClassLoader getThreadContextClassLoader() {
diff --git a/src/jdk/internal/dynalink/beans/AbstractJavaLinker.java b/src/jdk/internal/dynalink/beans/AbstractJavaLinker.java
index dab6cbf5..cf1b11d3 100644
--- a/src/jdk/internal/dynalink/beans/AbstractJavaLinker.java
+++ b/src/jdk/internal/dynalink/beans/AbstractJavaLinker.java
@@ -287,10 +287,21 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
*/
private static SingleDynamicMethod createDynamicMethod(final AccessibleObject m) {
if(CallerSensitiveDetector.isCallerSensitive(m)) {
+ // Method has @CallerSensitive annotation
return new CallerSensitiveDynamicMethod(m);
}
+ // Method has no @CallerSensitive annotation
+ final MethodHandle mh;
+ try {
+ mh = unreflectSafely(m);
+ } catch (final IllegalAccessError e) {
+ // java.lang.invoke can in some case conservatively treat as caller sensitive methods that aren't
+ // marked with the annotation. In this case, we'll fall back to treating it as caller sensitive.
+ return new CallerSensitiveDynamicMethod(m);
+ }
+ // Proceed with non-caller sensitive
final Member member = (Member)m;
- return new SimpleDynamicMethod(unreflectSafely(m), member.getDeclaringClass(), member.getName(), m instanceof Constructor);
+ return new SimpleDynamicMethod(mh, member.getDeclaringClass(), member.getName(), m instanceof Constructor);
}
/**
@@ -480,8 +491,9 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
// We want setters that conform to "Object(O, V)". Note, we aren't doing "R(O, V)" as it might not be
// valid for us to convert return values proactively. Also, since we don't know what setters will be
- // invoked, we'll conservatively presume Object return type.
- final MethodType type = callSiteDescriptor.getMethodType().changeReturnType(Object.class);
+ // invoked, we'll conservatively presume Object return type. The one exception is void return.
+ final MethodType origType = callSiteDescriptor.getMethodType();
+ final MethodType type = origType.returnType() == void.class ? origType : origType.changeReturnType(Object.class);
// What's below is basically:
// foldArguments(guardWithTest(isNotNull, invoke, null|nextComponent.invocation),
@@ -497,7 +509,7 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
// Bind property setter handle to the expected setter type and linker services. Type is
// MethodHandle(Object, String, Object)
final MethodHandle boundGetter = MethodHandles.insertArguments(getPropertySetterHandle, 0,
- CallSiteDescriptorFactory.dropParameterTypes(callSiteDescriptor, 1, 2), linkerServices);
+ callSiteDescriptor.changeMethodType(setterType), linkerServices);
// Cast getter to MethodHandle(O, N, V)
final MethodHandle typedGetter = linkerServices.asType(boundGetter, type.changeReturnType(
diff --git a/src/jdk/internal/dynalink/beans/OverloadedMethod.java b/src/jdk/internal/dynalink/beans/OverloadedMethod.java
index 70ec495a..c8d50072 100644
--- a/src/jdk/internal/dynalink/beans/OverloadedMethod.java
+++ b/src/jdk/internal/dynalink/beans/OverloadedMethod.java
@@ -123,7 +123,6 @@ class OverloadedMethod {
varArgMethods = new ArrayList<>(methodHandles.size());
final int argNum = callSiteType.parameterCount();
for(MethodHandle mh: methodHandles) {
- mh = mh.asType(mh.type().changeReturnType(commonRetType));
if(mh.isVarargsCollector()) {
final MethodHandle asFixed = mh.asFixedArity();
if(argNum == asFixed.type().parameterCount()) {
diff --git a/src/jdk/internal/dynalink/linker/MethodTypeConversionStrategy.java b/src/jdk/internal/dynalink/linker/MethodTypeConversionStrategy.java
new file mode 100644
index 00000000..f270c5fc
--- /dev/null
+++ b/src/jdk/internal/dynalink/linker/MethodTypeConversionStrategy.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file, and Oracle licenses the original version of this file under the BSD
+ * license:
+ */
+/*
+ Copyright 2014 Attila Szegedi
+
+ Licensed under both the Apache License, Version 2.0 (the "Apache License")
+ and the BSD License (the "BSD License"), with licensee being free to
+ choose either of the two at their discretion.
+
+ You may not use this file except in compliance with either the Apache
+ License or the BSD License.
+
+ If you choose to use this file in compliance with the Apache License, the
+ following notice applies to you:
+
+ You may obtain a copy of the Apache License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied. See the License for the specific language governing
+ permissions and limitations under the License.
+
+ If you choose to use this file in compliance with the BSD License, the
+ following notice applies to you:
+
+ 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 the copyright holder nor the names of
+ 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 COPYRIGHT HOLDER
+ 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.
+*/
+
+package jdk.internal.dynalink.linker;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodType;
+
+/**
+ * Interface for objects representing a strategy for converting a method handle to a new type.
+ */
+public interface MethodTypeConversionStrategy {
+ /**
+ * Converts a method handle to a new type.
+ * @param target target method handle
+ * @param newType new type
+ * @return target converted to the new type.
+ */
+ public MethodHandle asType(final MethodHandle target, final MethodType newType);
+}
diff --git a/src/jdk/internal/dynalink/support/TypeConverterFactory.java b/src/jdk/internal/dynalink/support/TypeConverterFactory.java
index 79f6549b..736b787c 100644
--- a/src/jdk/internal/dynalink/support/TypeConverterFactory.java
+++ b/src/jdk/internal/dynalink/support/TypeConverterFactory.java
@@ -97,6 +97,7 @@ import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.GuardedTypeConversion;
import jdk.internal.dynalink.linker.GuardingTypeConverterFactory;
import jdk.internal.dynalink.linker.LinkerServices;
+import jdk.internal.dynalink.linker.MethodTypeConversionStrategy;
/**
* A factory for type converters. This class is the main implementation behind the
@@ -109,6 +110,7 @@ public class TypeConverterFactory {
private final GuardingTypeConverterFactory[] factories;
private final ConversionComparator[] comparators;
+ private final MethodTypeConversionStrategy autoConversionStrategy;
private final ClassValue<ClassMap<MethodHandle>> converterMap = new ClassValue<ClassMap<MethodHandle>>() {
@Override
@@ -177,8 +179,24 @@ public class TypeConverterFactory {
* Creates a new type converter factory from the available {@link GuardingTypeConverterFactory} instances.
*
* @param factories the {@link GuardingTypeConverterFactory} instances to compose.
+ * @param autoConversionStrategy conversion strategy for automatic type conversions. After
+ * {@link #asType(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType)} has applied all custom
+ * conversions to a method handle, it still needs to effect
+ * {@link TypeUtilities#isMethodInvocationConvertible(Class, Class) method invocation conversions} that
+ * can usually be automatically applied as per
+ * {@link java.lang.invoke.MethodHandle#asType(java.lang.invoke.MethodType)}.
+ * However, sometimes language runtimes will want to customize even those conversions for their own call
+ * sites. A typical example is allowing unboxing of null return values, which is by default prohibited by
+ * ordinary {@code MethodHandles.asType}. In this case, a language runtime can install its own custom
+ * automatic conversion strategy, that can deal with null values. Note that when the strategy's
+ * {@link MethodTypeConversionStrategy#asType(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType)}
+ * is invoked, the custom language conversions will already have been applied to the method handle, so by
+ * design the difference between the handle's current method type and the desired final type will always
+ * only be ones that can be subjected to method invocation conversions. Can be null, in which case no
+ * custom strategy is employed.
*/
- public TypeConverterFactory(final Iterable<? extends GuardingTypeConverterFactory> factories) {
+ public TypeConverterFactory(final Iterable<? extends GuardingTypeConverterFactory> factories,
+ final MethodTypeConversionStrategy autoConversionStrategy) {
final List<GuardingTypeConverterFactory> l = new LinkedList<>();
final List<ConversionComparator> c = new LinkedList<>();
for(final GuardingTypeConverterFactory factory: factories) {
@@ -189,20 +207,24 @@ public class TypeConverterFactory {
}
this.factories = l.toArray(new GuardingTypeConverterFactory[l.size()]);
this.comparators = c.toArray(new ConversionComparator[c.size()]);
-
+ this.autoConversionStrategy = autoConversionStrategy;
}
/**
* Similar to {@link MethodHandle#asType(MethodType)} except it also hooks in method handles produced by
* {@link GuardingTypeConverterFactory} implementations, providing for language-specific type coercing of
- * parameters. It will apply {@link MethodHandle#asType(MethodType)} for all primitive-to-primitive,
- * wrapper-to-primitive, primitive-to-wrapper conversions as well as for all upcasts. For all other conversions,
- * it'll insert {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)} with composite filters
- * provided by {@link GuardingTypeConverterFactory} implementations.
+ * parameters. For all conversions that are not a JLS method invocation conversion it'll insert
+ * {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)} with composite filters
+ * provided by {@link GuardingTypeConverterFactory} implementations. For the remaining JLS method invocation
+ * conversions, it will invoke {@link MethodTypeConversionStrategy#asType(MethodHandle, MethodType)} first
+ * if an automatic conversion strategy was specified in the
+ * {@link #TypeConverterFactory(Iterable, MethodTypeConversionStrategy) constructor}, and finally apply
+ * {@link MethodHandle#asType(MethodType)} for any remaining conversions.
*
* @param handle target method handle
* @param fromType the types of source arguments
- * @return a method handle that is a suitable combination of {@link MethodHandle#asType(MethodType)} and
+ * @return a method handle that is a suitable combination of {@link MethodHandle#asType(MethodType)},
+ * {@link MethodTypeConversionStrategy#asType(MethodHandle, MethodType)}, and
* {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)} with
* {@link GuardingTypeConverterFactory} produced type converters as filters.
*/
@@ -246,8 +268,12 @@ public class TypeConverterFactory {
}
}
- // Take care of automatic conversions
- return newHandle.asType(fromType);
+ // Give change to automatic conversion strategy, if one is present.
+ final MethodHandle autoConvertedHandle =
+ autoConversionStrategy != null ? autoConversionStrategy.asType(newHandle, fromType) : newHandle;
+
+ // Do a final asType for any conversions that remain.
+ return autoConvertedHandle.asType(fromType);
}
private static MethodHandle applyConverters(final MethodHandle handle, final int pos, final List<MethodHandle> converters) {
diff --git a/src/jdk/internal/dynalink/support/TypeUtilities.java b/src/jdk/internal/dynalink/support/TypeUtilities.java
index 6403f3d5..78d42ab1 100644
--- a/src/jdk/internal/dynalink/support/TypeUtilities.java
+++ b/src/jdk/internal/dynalink/support/TypeUtilities.java
@@ -118,17 +118,13 @@ public class TypeUtilities {
public static Class<?> getCommonLosslessConversionType(final Class<?> c1, final Class<?> c2) {
if(c1 == c2) {
return c1;
+ } else if (c1 == void.class || c2 == void.class) {
+ return Object.class;
} else if(isConvertibleWithoutLoss(c2, c1)) {
return c1;
} else if(isConvertibleWithoutLoss(c1, c2)) {
return c2;
- }
- if(c1 == void.class) {
- return c2;
- } else if(c2 == void.class) {
- return c1;
- }
- if(c1.isPrimitive() && c2.isPrimitive()) {
+ } else if(c1.isPrimitive() && c2.isPrimitive()) {
if((c1 == byte.class && c2 == char.class) || (c1 == char.class && c2 == byte.class)) {
// byte + char = int
return int.class;
@@ -268,20 +264,24 @@ public class TypeUtilities {
}
/**
- * Determines whether a type can be converted to another without losing any
- * precision.
+ * Determines whether a type can be converted to another without losing any precision. As a special case,
+ * void is considered convertible only to Object and void, while anything can be converted to void. This
+ * is because a target type of void means we don't care about the value, so the conversion is always
+ * permissible.
*
* @param sourceType the source type
* @param targetType the target type
* @return true if lossless conversion is possible
*/
public static boolean isConvertibleWithoutLoss(final Class<?> sourceType, final Class<?> targetType) {
- if(targetType.isAssignableFrom(sourceType)) {
+ if(targetType.isAssignableFrom(sourceType) || targetType == void.class) {
return true;
}
if(sourceType.isPrimitive()) {
if(sourceType == void.class) {
- return false; // Void can't be losslessly represented by any type
+ // Void should be losslessly representable by Object, either as null or as a custom value that
+ // can be set with DynamicLinkerFactory.setAutoConversionStrategy.
+ return targetType == Object.class;
}
if(targetType.isPrimitive()) {
return isProperPrimitiveLosslessSubtype(sourceType, targetType);
@@ -520,4 +520,13 @@ public class TypeUtilities {
public static Class<?> getWrapperType(final Class<?> primitiveType) {
return WRAPPER_TYPES.get(primitiveType);
}
+
+ /**
+ * Returns true if the passed type is a wrapper for a primitive type.
+ * @param type the examined type
+ * @return true if the passed type is a wrapper for a primitive type.
+ */
+ public static boolean isWrapperType(final Class<?> type) {
+ return PRIMITIVE_TYPES.containsKey(type);
+ }
}
diff --git a/src/jdk/nashorn/internal/AssertsEnabled.java b/src/jdk/nashorn/internal/AssertsEnabled.java
index f0678664..73d9dfdc 100644
--- a/src/jdk/nashorn/internal/AssertsEnabled.java
+++ b/src/jdk/nashorn/internal/AssertsEnabled.java
@@ -27,8 +27,8 @@ package jdk.nashorn.internal;
/**
* Class that exposes the current state of asserts.
- *
*/
+@SuppressWarnings("all")
public final class AssertsEnabled {
private static boolean assertsEnabled = false;
static {
diff --git a/src/jdk/nashorn/internal/codegen/ApplySpecialization.java b/src/jdk/nashorn/internal/codegen/ApplySpecialization.java
index cdfe34f2..62e58f46 100644
--- a/src/jdk/nashorn/internal/codegen/ApplySpecialization.java
+++ b/src/jdk/nashorn/internal/codegen/ApplySpecialization.java
@@ -29,6 +29,7 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS_VAR;
import static jdk.nashorn.internal.codegen.CompilerConstants.EXPLODED_ARGUMENT_PREFIX;
import java.lang.invoke.MethodType;
+import java.net.URL;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
@@ -93,6 +94,8 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
private final Deque<List<IdentNode>> explodedArguments = new ArrayDeque<>();
+ private final Deque<MethodType> callSiteTypes = new ArrayDeque<>();
+
private static final String ARGUMENTS = ARGUMENTS_VAR.symbolName();
/**
@@ -118,86 +121,113 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
return context.getLogger(this.getClass());
}
+ @SuppressWarnings("serial")
+ private static class TransformFailedException extends RuntimeException {
+ TransformFailedException(final FunctionNode fn, final String message) {
+ super(massageURL(fn.getSource().getURL()) + '.' + fn.getName() + " => " + message, null, false, false);
+ }
+ }
+
+ @SuppressWarnings("serial")
+ private static class AppliesFoundException extends RuntimeException {
+ AppliesFoundException() {
+ super("applies_found", null, false, false);
+ }
+ }
+
+ private static final AppliesFoundException HAS_APPLIES = new AppliesFoundException();
+
+ private boolean hasApplies(final FunctionNode functionNode) {
+ try {
+ functionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
+ @Override
+ public boolean enterFunctionNode(final FunctionNode fn) {
+ return fn == functionNode;
+ }
+
+ @Override
+ public boolean enterCallNode(final CallNode callNode) {
+ if (isApply(callNode)) {
+ throw HAS_APPLIES;
+ }
+ return true;
+ }
+ });
+ } catch (final AppliesFoundException e) {
+ return true;
+ }
+
+ log.fine("There are no applies in ", DebugLogger.quote(functionNode.getName()), " - nothing to do.");
+ return false; // no applies
+ }
+
/**
* Arguments may only be used as args to the apply. Everything else is disqualified
* We cannot control arguments if they escape from the method and go into an unknown
* scope, thus we are conservative and treat any access to arguments outside the
* apply call as a case of "we cannot apply the optimization".
- *
- * @return true if arguments escape
*/
- private boolean argumentsEscape(final FunctionNode functionNode) {
-
- @SuppressWarnings("serial")
- final UnsupportedOperationException uoe = new UnsupportedOperationException() {
- @Override
- public synchronized Throwable fillInStackTrace() {
- return null;
- }
- };
+ private static void checkValidTransform(final FunctionNode functionNode) {
final Set<Expression> argumentsFound = new HashSet<>();
final Deque<Set<Expression>> stack = new ArrayDeque<>();
+
//ensure that arguments is only passed as arg to apply
- try {
- functionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
- private boolean isCurrentArg(final Expression expr) {
- return !stack.isEmpty() && stack.peek().contains(expr); //args to current apply call
- }
+ functionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
+
+ private boolean isCurrentArg(final Expression expr) {
+ return !stack.isEmpty() && stack.peek().contains(expr); //args to current apply call
+ }
+
+ private boolean isArguments(final Expression expr) {
+ if (expr instanceof IdentNode && ARGUMENTS.equals(((IdentNode)expr).getName())) {
+ argumentsFound.add(expr);
+ return true;
+ }
+ return false;
+ }
- private boolean isArguments(final Expression expr) {
- if (expr instanceof IdentNode && ARGUMENTS.equals(((IdentNode)expr).getName())) {
- argumentsFound.add(expr);
+ private boolean isParam(final String name) {
+ for (final IdentNode param : functionNode.getParameters()) {
+ if (param.getName().equals(name)) {
return true;
}
- return false;
}
+ return false;
+ }
- private boolean isParam(final String name) {
- for (final IdentNode param : functionNode.getParameters()) {
- if (param.getName().equals(name)) {
- return true;
- }
- }
- return false;
+ @Override
+ public Node leaveIdentNode(final IdentNode identNode) {
+ if (isParam(identNode.getName())) {
+ throw new TransformFailedException(lc.getCurrentFunction(), "parameter: " + identNode.getName());
}
-
- @Override
- public Node leaveIdentNode(final IdentNode identNode) {
- if (isParam(identNode.getName()) || isArguments(identNode) && !isCurrentArg(identNode)) {
- throw uoe; //avoid filling in stack trace
- }
- return identNode;
+ // it's OK if 'argument' occurs as the current argument of an apply
+ if (isArguments(identNode) && !isCurrentArg(identNode)) {
+ throw new TransformFailedException(lc.getCurrentFunction(), "is 'arguments': " + identNode.getName());
}
+ return identNode;
+ }
- @Override
- public boolean enterCallNode(final CallNode callNode) {
- final Set<Expression> callArgs = new HashSet<>();
- if (isApply(callNode)) {
- final List<Expression> argList = callNode.getArgs();
- if (argList.size() != 2 || !isArguments(argList.get(argList.size() - 1))) {
- throw new UnsupportedOperationException();
- }
- callArgs.addAll(callNode.getArgs());
+ @Override
+ public boolean enterCallNode(final CallNode callNode) {
+ final Set<Expression> callArgs = new HashSet<>();
+ if (isApply(callNode)) {
+ final List<Expression> argList = callNode.getArgs();
+ if (argList.size() != 2 || !isArguments(argList.get(argList.size() - 1))) {
+ throw new TransformFailedException(lc.getCurrentFunction(), "argument pattern not matched: " + argList);
}
- stack.push(callArgs);
- return true;
+ callArgs.addAll(callNode.getArgs());
}
-
- @Override
- public Node leaveCallNode(final CallNode callNode) {
- stack.pop();
- return callNode;
- }
- });
- } catch (final UnsupportedOperationException e) {
- if (!argumentsFound.isEmpty()) {
- log.fine("'arguments' is used but escapes, or is reassigned in '" + functionNode.getName() + "'. Aborting");
+ stack.push(callArgs);
+ return true;
}
- return true; //bad
- }
- return false;
+ @Override
+ public Node leaveCallNode(final CallNode callNode) {
+ stack.pop();
+ return callNode;
+ }
+ });
}
@Override
@@ -224,12 +254,14 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
final CallNode newCallNode = callNode.setArgs(newArgs).setIsApplyToCall();
- log.fine("Transformed ",
- callNode,
- " from apply to call => ",
- newCallNode,
- " in ",
- DebugLogger.quote(lc.getCurrentFunction().getName()));
+ if (log.isEnabled()) {
+ log.fine("Transformed ",
+ callNode,
+ " from apply to call => ",
+ newCallNode,
+ " in ",
+ DebugLogger.quote(lc.getCurrentFunction().getName()));
+ }
return newCallNode;
}
@@ -237,12 +269,12 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
return callNode;
}
- private boolean pushExplodedArgs(final FunctionNode functionNode) {
+ private void pushExplodedArgs(final FunctionNode functionNode) {
int start = 0;
final MethodType actualCallSiteType = compiler.getCallSiteType(functionNode);
if (actualCallSiteType == null) {
- return false;
+ throw new TransformFailedException(lc.getCurrentFunction(), "No callsite type");
}
assert actualCallSiteType.parameterType(actualCallSiteType.parameterCount() - 1) != Object[].class : "error vararg callsite passed to apply2call " + functionNode.getName() + " " + actualCallSiteType;
@@ -264,8 +296,8 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
}
}
+ callSiteTypes.push(actualCallSiteType);
explodedArguments.push(newParams);
- return true;
}
@Override
@@ -288,11 +320,30 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
return false;
}
- if (argumentsEscape(functionNode)) {
+ if (!hasApplies(functionNode)) {
+ return false;
+ }
+
+ if (log.isEnabled()) {
+ log.info("Trying to specialize apply to call in '",
+ functionNode.getName(),
+ "' params=",
+ functionNode.getParameters(),
+ " id=",
+ functionNode.getId(),
+ " source=",
+ massageURL(functionNode.getSource().getURL()));
+ }
+
+ try {
+ checkValidTransform(functionNode);
+ pushExplodedArgs(functionNode);
+ } catch (final TransformFailedException e) {
+ log.info("Failure: ", e.getMessage());
return false;
}
- return pushExplodedArgs(functionNode);
+ return true;
}
/**
@@ -300,8 +351,8 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
* @return true if successful, false otherwise
*/
@Override
- public Node leaveFunctionNode(final FunctionNode functionNode0) {
- FunctionNode newFunctionNode = functionNode0;
+ public Node leaveFunctionNode(final FunctionNode functionNode) {
+ FunctionNode newFunctionNode = functionNode;
final String functionName = newFunctionNode.getName();
if (changed.contains(newFunctionNode.getId())) {
@@ -310,17 +361,18 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
setParameters(lc, explodedArguments.peek());
if (log.isEnabled()) {
- log.info("Successfully specialized apply to call in '",
+ log.info("Success: ",
+ massageURL(newFunctionNode.getSource().getURL()),
+ '.',
functionName,
- " params=",
- explodedArguments.peek(),
"' id=",
newFunctionNode.getId(),
- " source=",
- newFunctionNode.getSource().getURL());
+ " params=",
+ callSiteTypes.peek());
}
}
+ callSiteTypes.pop();
explodedArguments.pop();
return newFunctionNode.setState(lc, CompilationState.BUILTINS_TRANSFORMED);
@@ -331,4 +383,15 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
return f instanceof AccessNode && "apply".equals(((AccessNode)f).getProperty());
}
+ private static String massageURL(final URL url) {
+ if (url == null) {
+ return "<null>";
+ }
+ final String str = url.toString();
+ final int slash = str.lastIndexOf('/');
+ if (slash == -1) {
+ return str;
+ }
+ return str.substring(slash + 1);
+ }
}
diff --git a/src/jdk/nashorn/internal/codegen/AssignSymbols.java b/src/jdk/nashorn/internal/codegen/AssignSymbols.java
index 88fd89bb..a4fb7bd1 100644
--- a/src/jdk/nashorn/internal/codegen/AssignSymbols.java
+++ b/src/jdk/nashorn/internal/codegen/AssignSymbols.java
@@ -189,7 +189,7 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
* @param body the body of the FunctionNode we are entering
*/
private void acceptDeclarations(final FunctionNode functionNode, final Block body) {
- // This visitor will assign symbol to all declared variables, except "var" declarations in for loop initializers.
+ // This visitor will assign symbol to all declared variables.
body.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
@Override
protected boolean enterDefault(final Node node) {
@@ -200,16 +200,17 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
@Override
public Node leaveVarNode(final VarNode varNode) {
- if (varNode.isStatement()) {
- final IdentNode ident = varNode.getName();
- final Block block = varNode.isBlockScoped() ? getLexicalContext().getCurrentBlock() : body;
- final Symbol symbol = defineSymbol(block, ident.getName(), ident, varNode.getSymbolFlags());
- if (varNode.isFunctionDeclaration()) {
- symbol.setIsFunctionDeclaration();
- }
- return varNode.setName(ident.setSymbol(symbol));
+ final IdentNode ident = varNode.getName();
+ final boolean blockScoped = varNode.isBlockScoped();
+ if (blockScoped && lc.inUnprotectedSwitchContext()) {
+ throwUnprotectedSwitchError(varNode);
+ }
+ final Block block = blockScoped ? lc.getCurrentBlock() : body;
+ final Symbol symbol = defineSymbol(block, ident.getName(), ident, varNode.getSymbolFlags());
+ if (varNode.isFunctionDeclaration()) {
+ symbol.setIsFunctionDeclaration();
}
- return varNode;
+ return varNode.setName(ident.setSymbol(symbol));
}
});
}
@@ -356,6 +357,10 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
throwParserException(ECMAErrors.getMessage("syntax.error.redeclare.variable", name), origin);
} else {
symbol.setHasBeenDeclared();
+ // Set scope flag on top-level block scoped symbols
+ if (function.isProgram() && function.getBody() == block) {
+ symbol.setIsScope();
+ }
}
} else if ((flags & IS_INTERNAL) != 0) {
// Always create a new definition.
@@ -540,7 +545,7 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
final int flags;
if (varNode.isAnonymousFunctionDeclaration()) {
flags = IS_INTERNAL;
- } else if (lc.getCurrentFunction().isProgram()) {
+ } else if (!varNode.isBlockScoped() && lc.getCurrentFunction().isProgram()) {
flags = IS_SCOPE;
} else {
flags = 0;
@@ -905,7 +910,7 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
@Override
public Node leaveSwitchNode(final SwitchNode switchNode) {
// We only need a symbol for the tag if it's not an integer switch node
- if(!switchNode.isInteger()) {
+ if(!switchNode.isUniqueInteger()) {
switchNode.setTag(newObjectInternal(SWITCH_TAG_PREFIX));
}
return switchNode;
@@ -1044,6 +1049,15 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
return !(units == null || units.isEmpty());
}
+ private void throwUnprotectedSwitchError(final VarNode varNode) {
+ // Block scoped declarations in switch statements without explicit blocks should be declared
+ // in a common block that contains all the case clauses. We cannot support this without a
+ // fundamental rewrite of how switch statements are handled (case nodes contain blocks and are
+ // directly contained by switch node). As a temporary solution we throw a reference error here.
+ final String msg = ECMAErrors.getMessage("syntax.error.unprotected.switch.declaration", varNode.isLet() ? "let" : "const");
+ throwParserException(msg, varNode);
+ }
+
private void throwParserException(final String message, final Node origin) {
if (origin == null) {
throw new ParserException(message);
diff --git a/src/jdk/nashorn/internal/codegen/AstSerializer.java b/src/jdk/nashorn/internal/codegen/AstSerializer.java
index 19197a26..dc35f964 100644
--- a/src/jdk/nashorn/internal/codegen/AstSerializer.java
+++ b/src/jdk/nashorn/internal/codegen/AstSerializer.java
@@ -48,11 +48,13 @@ final class AstSerializer {
private static final int COMPRESSION_LEVEL = Options.getIntProperty("nashorn.serialize.compression", 4);
static byte[] serialize(final FunctionNode fn) {
final ByteArrayOutputStream out = new ByteArrayOutputStream();
- try (final ObjectOutputStream oout = new ObjectOutputStream(new DeflaterOutputStream(out,
- new Deflater(COMPRESSION_LEVEL)))) {
+ final Deflater deflater = new Deflater(COMPRESSION_LEVEL);
+ try (final ObjectOutputStream oout = new ObjectOutputStream(new DeflaterOutputStream(out, deflater))) {
oout.writeObject(removeInnerFunctionBodies(fn));
} catch (final IOException e) {
throw new AssertionError("Unexpected exception serializing function", e);
+ } finally {
+ deflater.end();
}
return out.toByteArray();
}
diff --git a/src/jdk/nashorn/internal/codegen/CodeGenerator.java b/src/jdk/nashorn/internal/codegen/CodeGenerator.java
index d6743631..59aa258e 100644
--- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java
+++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java
@@ -208,6 +208,8 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
private static final Type ITERATOR_TYPE = Type.typeFor(ITERATOR_CLASS);
private static final Type EXCEPTION_TYPE = Type.typeFor(CompilerConstants.EXCEPTION_PREFIX.type());
+ private static final Integer INT_ZERO = Integer.valueOf(0);
+
/** Constant data & installation. The only reason the compiler keeps this is because it is assigned
* by reflection in class installation */
private final Compiler compiler;
@@ -463,10 +465,10 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
// If this is either __FILE__, __DIR__, or __LINE__ then load the property initially as Object as we'd convert
// it anyway for replaceLocationPropertyPlaceholder.
if(identNode.isCompileTimePropertyName()) {
- method.dynamicGet(Type.OBJECT, identNode.getSymbol().getName(), flags, identNode.isFunction());
+ method.dynamicGet(Type.OBJECT, identNode.getSymbol().getName(), flags, identNode.isFunction(), false);
replaceCompileTimeProperty();
} else {
- dynamicGet(identNode.getSymbol().getName(), flags, identNode.isFunction());
+ dynamicGet(identNode.getSymbol().getName(), flags, identNode.isFunction(), false);
}
}
}
@@ -484,7 +486,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
private MethodEmitter storeFastScopeVar(final Symbol symbol, final int flags) {
loadFastScopeProto(symbol, true);
- method.dynamicSet(symbol.getName(), flags | CALLSITE_FAST_SCOPE);
+ method.dynamicSet(symbol.getName(), flags | CALLSITE_FAST_SCOPE, false);
return method;
}
@@ -569,9 +571,11 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
// Operands' load type should not be narrower than the narrowest of the individual operand types, nor narrower
// than the lower explicit bound, but it should also not be wider than
- final Type narrowestOperandType = Type.narrowest(Type.widest(lhs.getType(), rhs.getType()), explicitOperandBounds.widest);
+ final Type lhsType = undefinedToNumber(lhs.getType());
+ final Type rhsType = undefinedToNumber(rhs.getType());
+ final Type narrowestOperandType = Type.narrowest(Type.widest(lhsType, rhsType), explicitOperandBounds.widest);
final TypeBounds operandBounds = explicitOperandBounds.notNarrowerThan(narrowestOperandType);
- if (noToPrimitiveConversion(lhs.getType(), explicitOperandBounds.widest) || rhs.isLocal()) {
+ if (noToPrimitiveConversion(lhsType, explicitOperandBounds.widest) || rhs.isLocal()) {
// Can reorder. We might still need to separate conversion, but at least we can do it with reordering
if (forceConversionSeparation) {
// Can reorder, but can't move conversion into the operand as the operation depends on operands
@@ -592,10 +596,10 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
// Can't reorder. Load and convert separately.
final TypeBounds safeConvertBounds = TypeBounds.UNBOUNDED.notNarrowerThan(narrowestOperandType);
loadExpression(lhs, safeConvertBounds, baseAlreadyOnStack);
- final Type lhsType = method.peekType();
+ final Type lhsLoadedType = method.peekType();
loadExpression(rhs, safeConvertBounds, false);
final Type convertedLhsType = operandBounds.within(method.peekType());
- if (convertedLhsType != lhsType) {
+ if (convertedLhsType != lhsLoadedType) {
// Do it conditionally, so that if conversion is a no-op we don't introduce a SWAP, SWAP.
method.swap().convert(convertedLhsType).swap();
}
@@ -607,13 +611,16 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
return method;
}
+ private static final Type undefinedToNumber(final Type type) {
+ return type == Type.UNDEFINED ? Type.NUMBER : type;
+ }
+
private static final class TypeBounds {
final Type narrowest;
final Type widest;
static final TypeBounds UNBOUNDED = new TypeBounds(Type.UNKNOWN, Type.OBJECT);
static final TypeBounds INT = exact(Type.INT);
- static final TypeBounds NUMBER = exact(Type.NUMBER);
static final TypeBounds OBJECT = exact(Type.OBJECT);
static final TypeBounds BOOLEAN = exact(Type.BOOLEAN);
@@ -738,7 +745,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
@Override
void consumeStack() {
final int flags = getCallSiteFlags();
- dynamicGet(accessNode.getProperty(), flags, accessNode.isFunction());
+ dynamicGet(accessNode.getProperty(), flags, accessNode.isFunction(), accessNode.isIndex());
}
}.emit(baseAlreadyOnStack ? 1 : 0);
return false;
@@ -1442,7 +1449,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
// NOTE: not using a nested OptimisticOperation on this dynamicGet, as we expect to get back
// a callable object. Nobody in their right mind would optimistically type this call site.
assert !node.isOptimistic();
- method.dynamicGet(node.getType(), node.getProperty(), flags, true);
+ method.dynamicGet(node.getType(), node.getProperty(), flags, true, node.isIndex());
method.swap();
argCount = loadArgs(args);
}
@@ -2014,6 +2021,19 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
final Expression test = ifNode.getTest();
final Block pass = ifNode.getPass();
final Block fail = ifNode.getFail();
+
+ if (Expression.isAlwaysTrue(test)) {
+ loadAndDiscard(test);
+ pass.accept(this);
+ return false;
+ } else if (Expression.isAlwaysFalse(test)) {
+ loadAndDiscard(test);
+ if (fail != null) {
+ fail.accept(this);
+ }
+ return false;
+ }
+
final boolean hasFailConversion = LocalVariableConversion.hasLiveConversion(ifNode);
final Label failLabel = new Label("if_fail");
@@ -2033,7 +2053,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
method.beforeJoinPoint(ifNode);
}
- if(afterLabel != null) {
+ if(afterLabel != null && afterLabel.isReachable()) {
method.label(afterLabel);
}
@@ -2810,7 +2830,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
Label defaultLabel = defaultCase != null ? defaultCase.getEntry() : breakLabel;
final boolean hasSkipConversion = LocalVariableConversion.hasLiveConversion(switchNode);
- if (switchNode.isInteger()) {
+ if (switchNode.isUniqueInteger()) {
// Tree for sorting values.
final TreeMap<Integer, Label> tree = new TreeMap<>();
@@ -3144,14 +3164,13 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
if (isFastScope(identSymbol)) {
storeFastScopeVar(identSymbol, flags);
} else {
- method.dynamicSet(identNode.getName(), flags);
+ method.dynamicSet(identNode.getName(), flags, false);
}
} else {
final Type identType = identNode.getType();
if(identType == Type.UNDEFINED) {
- // The symbol must not be slotted; the initializer is either itself undefined (explicit assignment of
- // undefined to undefined), or the left hand side is a dead variable.
- assert !identNode.getSymbol().isScope();
+ // The initializer is either itself undefined (explicit assignment of undefined to undefined),
+ // or the left hand side is a dead variable.
assert init.getType() == Type.UNDEFINED || identNode.getSymbol().slotCount() == 0;
loadAndDiscard(init);
return false;
@@ -3263,6 +3282,13 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
emitContinueLabel(continueLabel, liveLocalsOnContinue);
}
+ if (loopNode.hasPerIterationScope() && lc.getCurrentBlock().needsScope()) {
+ // ES6 for loops with LET init need a new scope for each iteration. We just create a shallow copy here.
+ method.loadCompilerConstant(SCOPE);
+ method.invoke(virtualCallNoLookup(ScriptObject.class, "copy", ScriptObject.class));
+ method.storeCompilerConstant(SCOPE);
+ }
+
if(method.isReachable()) {
if(modify != null) {
lineNumber(loopNode);
@@ -3567,8 +3593,9 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
operandBounds = new TypeBounds(binaryNode.getType(), Type.OBJECT);
} else {
// Non-optimistic, non-FP +. Allow it to overflow.
- operandBounds = new TypeBounds(binaryNode.getWidestOperandType(), Type.OBJECT);
- forceConversionSeparation = binaryNode.getWidestOperationType().narrowerThan(resultBounds.widest);
+ final Type widestOperationType = binaryNode.getWidestOperationType();
+ operandBounds = new TypeBounds(Type.narrowest(binaryNode.getWidestOperandType(), resultBounds.widest), widestOperationType);
+ forceConversionSeparation = widestOperationType.narrowerThan(resultBounds.widest);
}
loadBinaryOperands(binaryNode.lhs(), binaryNode.rhs(), operandBounds, false, forceConversionSeparation);
}
@@ -3683,8 +3710,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
final Expression lhs = assignNode.lhs();
final Expression rhs = assignNode.rhs();
final Type widestOperationType = assignNode.getWidestOperationType();
- final Type widest = assignNode.isTokenType(TokenType.ASSIGN_ADD) ? Type.OBJECT : widestOperationType;
- final TypeBounds bounds = new TypeBounds(assignNode.getType(), widest);
+ final TypeBounds bounds = new TypeBounds(assignNode.getType(), widestOperationType);
new OptimisticOperation(assignNode, bounds) {
@Override
void loadStack() {
@@ -3817,7 +3843,12 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
private void doSHR() {
// TODO: make SHR optimistic
- method.shr().convert(Type.LONG).load(JSType.MAX_UINT).and();
+ method.shr();
+ toUint();
+ }
+
+ private void toUint() {
+ JSType.TO_UINT32_I.invoke(method);
}
private void loadASSIGN_SUB(final BinaryNode binaryNode) {
@@ -3849,12 +3880,8 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
operandBounds = numericBounds;
} else {
final boolean isOptimistic = isValid(getProgramPoint());
- if(isOptimistic) {
+ if(isOptimistic || node.isTokenType(TokenType.DIV) || node.isTokenType(TokenType.MOD)) {
operandBounds = new TypeBounds(node.getType(), Type.NUMBER);
- } else if(node.isTokenType(TokenType.DIV) || node.isTokenType(TokenType.MOD)) {
- // Non-optimistic division must always take double arguments as its result must also be
- // double.
- operandBounds = TypeBounds.NUMBER;
} else {
// Non-optimistic, non-FP subtraction or multiplication. Allow them to overflow.
operandBounds = new TypeBounds(Type.narrowest(node.getWidestOperandType(),
@@ -3879,8 +3906,18 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
}
private void loadBIT_OR(final BinaryNode binaryNode) {
- loadBinaryOperands(binaryNode);
- method.or();
+ // Optimize x|0 to (int)x
+ if (isRhsZero(binaryNode)) {
+ loadExpressionAsType(binaryNode.lhs(), Type.INT);
+ } else {
+ loadBinaryOperands(binaryNode);
+ method.or();
+ }
+ }
+
+ private static boolean isRhsZero(final BinaryNode binaryNode) {
+ final Expression rhs = binaryNode.rhs();
+ return rhs instanceof LiteralNode && INT_ZERO.equals(((LiteralNode<?>)rhs).getValue());
}
private void loadBIT_XOR(final BinaryNode binaryNode) {
@@ -3957,8 +3994,14 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
}
private void loadSHR(final BinaryNode binaryNode) {
- loadBinaryOperands(binaryNode);
- doSHR();
+ // Optimize x >>> 0 to (uint)x
+ if (isRhsZero(binaryNode)) {
+ loadExpressionAsType(binaryNode.lhs(), Type.INT);
+ toUint();
+ } else {
+ loadBinaryOperands(binaryNode);
+ doSHR();
+ }
}
private void loadSUB(final BinaryNode binaryNode, final TypeBounds resultBounds) {
@@ -4225,7 +4268,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
if (isFastScope(symbol)) {
storeFastScopeVar(symbol, flags);
} else {
- method.dynamicSet(node.getName(), flags);
+ method.dynamicSet(node.getName(), flags, false);
}
} else {
final Type storeType = assignNode.getType();
@@ -4242,7 +4285,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
@Override
public boolean enterAccessNode(final AccessNode node) {
- method.dynamicSet(node.getProperty(), getCallSiteFlags());
+ method.dynamicSet(node.getProperty(), getCallSiteFlags(), node.isIndex());
return false;
}
@@ -4580,11 +4623,11 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
* @param isMethod whether we're preferrably retrieving a function
* @return the current method emitter
*/
- MethodEmitter dynamicGet(final String name, final int flags, final boolean isMethod) {
+ MethodEmitter dynamicGet(final String name, final int flags, final boolean isMethod, final boolean isIndex) {
if(isOptimistic) {
- return method.dynamicGet(getOptimisticCoercedType(), name, getOptimisticFlags(flags), isMethod);
+ return method.dynamicGet(getOptimisticCoercedType(), name, getOptimisticFlags(flags), isMethod, isIndex);
}
- return method.dynamicGet(resultBounds.within(expression.getType()), name, nonOptimisticFlags(flags), isMethod);
+ return method.dynamicGet(resultBounds.within(expression.getType()), name, nonOptimisticFlags(flags), isMethod, isIndex);
}
MethodEmitter dynamicGetIndex(final int flags, final boolean isMethod) {
diff --git a/src/jdk/nashorn/internal/codegen/CodeGeneratorLexicalContext.java b/src/jdk/nashorn/internal/codegen/CodeGeneratorLexicalContext.java
index 08c4b65c..87a0802c 100644
--- a/src/jdk/nashorn/internal/codegen/CodeGeneratorLexicalContext.java
+++ b/src/jdk/nashorn/internal/codegen/CodeGeneratorLexicalContext.java
@@ -31,7 +31,6 @@ import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;
-
import jdk.nashorn.internal.IntDeque;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.Block;
@@ -250,7 +249,7 @@ final class CodeGeneratorLexicalContext extends LexicalContext {
static Type getTypeForSlotDescriptor(final char typeDesc) {
// Recognizing both lowercase and uppercase as we're using both to signify symbol boundaries; see
// MethodEmitter.markSymbolBoundariesInLvarTypesDescriptor().
- switch(typeDesc) {
+ switch (typeDesc) {
case 'I':
case 'i':
return Type.INT;
diff --git a/src/jdk/nashorn/internal/codegen/Compiler.java b/src/jdk/nashorn/internal/codegen/Compiler.java
index 196862f0..740022b2 100644
--- a/src/jdk/nashorn/internal/codegen/Compiler.java
+++ b/src/jdk/nashorn/internal/codegen/Compiler.java
@@ -389,6 +389,7 @@ public final class Compiler implements Loggable {
* @param continuationEntryPoints continuation entry points for restof method
* @param runtimeScope runtime scope for recompilation type lookup in {@code TypeEvaluator}
*/
+ @SuppressWarnings("unused")
public Compiler(
final Context context,
final ScriptEnvironment env,
diff --git a/src/jdk/nashorn/internal/codegen/FoldConstants.java b/src/jdk/nashorn/internal/codegen/FoldConstants.java
index fe81b2b4..092b788f 100644
--- a/src/jdk/nashorn/internal/codegen/FoldConstants.java
+++ b/src/jdk/nashorn/internal/codegen/FoldConstants.java
@@ -26,12 +26,16 @@
package jdk.nashorn.internal.codegen;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.BlockStatement;
+import jdk.nashorn.internal.ir.CaseNode;
import jdk.nashorn.internal.ir.EmptyNode;
+import jdk.nashorn.internal.ir.Expression;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.ir.IfNode;
@@ -40,6 +44,7 @@ import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.Statement;
+import jdk.nashorn.internal.ir.SwitchNode;
import jdk.nashorn.internal.ir.TernaryNode;
import jdk.nashorn.internal.ir.UnaryNode;
import jdk.nashorn.internal.ir.VarNode;
@@ -126,11 +131,37 @@ final class FoldConstants extends NodeVisitor<LexicalContext> implements Loggabl
public Node leaveTernaryNode(final TernaryNode ternaryNode) {
final Node test = ternaryNode.getTest();
if (test instanceof LiteralNode.PrimitiveLiteralNode) {
- return ((LiteralNode.PrimitiveLiteralNode<?>)test).isTrue() ? ternaryNode.getTrueExpression() : ternaryNode.getFalseExpression();
+ return (((LiteralNode.PrimitiveLiteralNode<?>)test).isTrue() ? ternaryNode.getTrueExpression() : ternaryNode.getFalseExpression()).getExpression();
}
return ternaryNode;
}
+ @Override
+ public Node leaveSwitchNode(final SwitchNode switchNode) {
+ return switchNode.setUniqueInteger(lc, isUniqueIntegerSwitchNode(switchNode));
+ }
+
+ private static boolean isUniqueIntegerSwitchNode(final SwitchNode switchNode) {
+ final Set<Integer> alreadySeen = new HashSet<>();
+ for (final CaseNode caseNode : switchNode.getCases()) {
+ final Expression test = caseNode.getTest();
+ if (test != null && !isUniqueIntegerLiteral(test, alreadySeen)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static boolean isUniqueIntegerLiteral(final Expression expr, final Set<Integer> alreadySeen) {
+ if (expr instanceof LiteralNode) {
+ final Object value = ((LiteralNode<?>)expr).getValue();
+ if (value instanceof Integer) {
+ return alreadySeen.add((Integer)value);
+ }
+ }
+ return false;
+ }
+
/**
* Helper class to evaluate constant expressions at compile time This is
* also a simplifier used by BinaryNode visits, UnaryNode visits and
@@ -291,7 +322,7 @@ final class FoldConstants extends NodeVisitor<LexicalContext> implements Loggabl
value = lhs.getNumber() - rhs.getNumber();
break;
case SHR:
- return LiteralNode.newInstance(token, finish, (lhs.getInt32() >>> rhs.getInt32()) & JSType.MAX_UINT);
+ return LiteralNode.newInstance(token, finish, JSType.toUint32(lhs.getInt32() >>> rhs.getInt32()));
case SAR:
return LiteralNode.newInstance(token, finish, lhs.getInt32() >> rhs.getInt32());
case SHL:
diff --git a/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java b/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java
index ea073623..ac3c2934 100644
--- a/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java
+++ b/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java
@@ -83,7 +83,6 @@ import jdk.nashorn.internal.ir.UnaryNode;
import jdk.nashorn.internal.ir.VarNode;
import jdk.nashorn.internal.ir.WhileNode;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
-import jdk.nashorn.internal.parser.Token;
import jdk.nashorn.internal.parser.TokenType;
/**
@@ -94,6 +93,13 @@ import jdk.nashorn.internal.parser.TokenType;
* variable to its widest used type after the join point. That would eliminate some widenings of undefined variables to
* object, most notably those used only in loops. We need a full liveness analysis for that. Currently, we can establish
* per-type liveness, which eliminates most of unwanted dead widenings.
+ * NOTE: the way this class is implemented, it actually processes the AST in two passes. The first pass is top-down and
+ * implemented in {@code enterXxx} methods. This pass does not mutate the AST (except for one occurrence, noted below),
+ * as being able to find relevant labels for control flow joins is sensitive to their reference identity, and mutated
+ * label-carrying nodes will create copies of their labels. A second bottom-up pass applying the changes is implemented
+ * in the separate visitor sitting in {@link #leaveFunctionNode(FunctionNode)}. This visitor will also instantiate new
+ * instances of the calculator to be run on nested functions (when not lazy compiling).
+ *
*/
final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
@@ -236,12 +242,12 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
private byte conversions;
void recordConversion(final LvarType from, final LvarType to) {
- switch(from) {
+ switch (from) {
case UNDEFINED:
return;
case INT:
case BOOLEAN:
- switch(to) {
+ switch (to) {
case LONG:
recordConversion(I2L);
return;
@@ -256,7 +262,7 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
return;
}
case LONG:
- switch(to) {
+ switch (to) {
case DOUBLE:
recordConversion(L2D);
return;
@@ -399,48 +405,53 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
@Override
public boolean enterBinaryNode(final BinaryNode binaryNode) {
+ // NOTE: regardless of operator's lexical associativity, lhs is always evaluated first.
final Expression lhs = binaryNode.lhs();
- final Expression rhs = binaryNode.rhs();
final boolean isAssignment = binaryNode.isAssignment();
-
- final TokenType tokenType = Token.descType(binaryNode.getToken());
- if(tokenType.isLeftAssociative()) {
- assert !isAssignment;
- final boolean isLogical = binaryNode.isLogical();
- final Label joinLabel = isLogical ? new Label("") : null;
- lhs.accept(this);
- if(isLogical) {
- jumpToLabel((JoinPredecessor)lhs, joinLabel);
- }
- rhs.accept(this);
- if(isLogical) {
- jumpToLabel((JoinPredecessor)rhs, joinLabel);
- }
- joinOnLabel(joinLabel);
- } else {
- rhs.accept(this);
- if(isAssignment) {
- if(lhs instanceof BaseNode) {
- ((BaseNode)lhs).getBase().accept(this);
- if(lhs instanceof IndexNode) {
- ((IndexNode)lhs).getIndex().accept(this);
- } else {
- assert lhs instanceof AccessNode;
- }
+ LvarType lhsTypeOnLoad = null;
+ if(isAssignment) {
+ if(lhs instanceof BaseNode) {
+ ((BaseNode)lhs).getBase().accept(this);
+ if(lhs instanceof IndexNode) {
+ ((IndexNode)lhs).getIndex().accept(this);
} else {
- assert lhs instanceof IdentNode;
- if(binaryNode.isSelfModifying()) {
- ((IdentNode)lhs).accept(this);
- }
+ assert lhs instanceof AccessNode;
}
} else {
- lhs.accept(this);
+ assert lhs instanceof IdentNode;
+ if(binaryNode.isSelfModifying()) {
+ final IdentNode ident = ((IdentNode)lhs);
+ ident.accept(this);
+ // Self-assignment can cause a change in the type of the variable. For purposes of evaluating
+ // the type of the operation, we must use its type as it was when it was loaded. If we didn't
+ // do this, some awkward expressions would end up being calculated incorrectly, e.g.
+ // "var x; x += x = 0;". In this case we have undefined+int so the result type is double (NaN).
+ // However, if we used the type of "x" on LHS after we evaluated RHS, we'd see int+int, so the
+ // result type would be either optimistic int or pessimistic long, which would be wrong.
+ lhsTypeOnLoad = getLocalVariableTypeIfBytecode(ident.getSymbol());
+ }
}
+ } else {
+ lhs.accept(this);
+ }
+
+ final boolean isLogical = binaryNode.isLogical();
+ assert !(isAssignment && isLogical); // there are no logical assignment operators in JS
+ final Label joinLabel = isLogical ? new Label("") : null;
+ if(isLogical) {
+ jumpToLabel((JoinPredecessor)lhs, joinLabel);
+ }
+
+ final Expression rhs = binaryNode.rhs();
+ rhs.accept(this);
+ if(isLogical) {
+ jumpToLabel((JoinPredecessor)rhs, joinLabel);
}
+ joinOnLabel(joinLabel);
if(isAssignment && lhs instanceof IdentNode) {
if(binaryNode.isSelfModifying()) {
- onSelfAssignment((IdentNode)lhs, binaryNode);
+ onSelfAssignment((IdentNode)lhs, binaryNode, lhsTypeOnLoad);
} else {
onAssignment((IdentNode)lhs, rhs);
}
@@ -705,7 +716,7 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
// Control flow is different for all-integer cases where we dispatch by switch table, and for all other cases
// where we do sequential comparison. Note that CaseNode objects act as join points.
- final boolean isInteger = switchNode.isInteger();
+ final boolean isInteger = switchNode.isUniqueInteger();
final Label breakLabel = switchNode.getBreakLabel();
final boolean hasDefault = switchNode.getDefaultCase() != null;
@@ -920,7 +931,8 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
if(unaryNode.isSelfModifying()) {
if(expr instanceof IdentNode) {
- onSelfAssignment((IdentNode)expr, unaryNode);
+ final IdentNode ident = (IdentNode)expr;
+ onSelfAssignment(ident, unaryNode, getLocalVariableTypeIfBytecode(ident.getSymbol()));
}
}
return false;
@@ -974,12 +986,41 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
return types;
}
+ /**
+ * Returns the current type of the local variable represented by the symbol. This is the most strict of all
+ * {@code getLocalVariableType*} methods, as it will throw an assertion if the type is null. Therefore, it is only
+ * safe to be invoked on symbols known to be bytecode locals, and only after they have been initialized.
+ * Regardless, it is recommended to use this method in majority of cases, as because of its strictness it is the
+ * best suited for catching missing type calculation bugs early.
+ * @param symbol a symbol representing a bytecode local variable.
+ * @return the current type of the local variable represented by the symbol
+ */
private LvarType getLocalVariableType(final Symbol symbol) {
final LvarType type = getLocalVariableTypeOrNull(symbol);
assert type != null;
return type;
}
+ /**
+ * Gets the type for a local variable if it is a bytecode local, otherwise null. Can be used in circumstances where
+ * the type is irrelevant if the symbol is not a bytecode local. Note that for bytecode locals, it delegates to
+ * {@link #getLocalVariableType(Symbol)}, so it will still assert that the type for such variable is already
+ * defined (that is, not null).
+ * @param symbol the symbol representing the variable.
+ * @return the current variable type, if it is a bytecode local, otherwise null.
+ */
+ private LvarType getLocalVariableTypeIfBytecode(final Symbol symbol) {
+ return symbol.isBytecodeLocal() ? getLocalVariableType(symbol) : null;
+ }
+
+ /**
+ * Gets the type for a variable represented by a symbol, or null if the type is not know. This is the least strict
+ * of all local variable type getters, and as such its use is discouraged except in initialization scenarios (where
+ * a just-defined symbol might still be null).
+ * @param symbol the symbol
+ * @return the current type for the symbol, or null if the type is not known either because the symbol has not been
+ * initialized, or because the symbol does not represent a bytecode local variable.
+ */
private LvarType getLocalVariableTypeOrNull(final Symbol symbol) {
return localVariableTypes.get(symbol);
}
@@ -1359,13 +1400,13 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
jumpToCatchBlock(identNode);
}
- private void onSelfAssignment(final IdentNode identNode, final Expression assignment) {
+ private void onSelfAssignment(final IdentNode identNode, final Expression assignment, final LvarType typeOnLoad) {
final Symbol symbol = identNode.getSymbol();
assert symbol != null : identNode.getName();
if(!symbol.isBytecodeLocal()) {
return;
}
- final LvarType type = toLvarType(getType(assignment));
+ final LvarType type = toLvarType(getType(assignment, symbol, typeOnLoad.type));
// Self-assignment never produce either a boolean or undefined
assert type != null && type != LvarType.UNDEFINED && type != LvarType.BOOLEAN;
setType(symbol, type);
@@ -1425,6 +1466,7 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
* @param symbol the symbol representing the variable
* @param type the type
*/
+ @SuppressWarnings("unused")
private void setType(final Symbol symbol, final LvarType type) {
if(getLocalVariableTypeOrNull(symbol) == type) {
return;
@@ -1445,13 +1487,24 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
symbolIsUsed(symbol, getLocalVariableType(symbol));
}
+ /**
+ * Gets the type of the expression, dependent on the current types of the local variables.
+ *
+ * @param expr the expression
+ * @return the current type of the expression dependent on the current types of the local variables.
+ */
private Type getType(final Expression expr) {
return expr.getType(getSymbolToType());
}
+ /**
+ * Returns a function object from symbols to their types, used by the expressions to evaluate their type.
+ * {@link BinaryNode} specifically uses identity of the function to cache type calculations. This method makes
+ * sure to return the same function object while the local variable types don't change, and create a new function
+ * object if the local variable types have been changed.
+ * @return a function object representing a mapping from symbols to their types.
+ */
private Function<Symbol, Type> getSymbolToType() {
- // BinaryNode uses identity of the function to cache type calculations. Therefore, we must use different
- // function instances for different localVariableTypes instances.
if(symbolToType.isStale()) {
symbolToType = new SymbolToType();
}
@@ -1469,4 +1522,41 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
return boundTypes != localVariableTypes;
}
}
+
+ /**
+ * Gets the type of the expression, dependent on the current types of the local variables and a single overridden
+ * symbol type. Used by type calculation on compound operators to ensure the type of the LHS at the time it was
+ * loaded (which can potentially be different after RHS evaluation, e.g. "var x; x += x = 0;") is preserved for
+ * the calculation.
+ *
+ * @param expr the expression
+ * @param overriddenSymbol the overridden symbol
+ * @param overriddenType the overridden type
+ * @return the current type of the expression dependent on the current types of the local variables and the single
+ * potentially overridden type.
+ */
+ private Type getType(final Expression expr, final Symbol overriddenSymbol, final Type overriddenType) {
+ return expr.getType(getSymbolToType(overriddenSymbol, overriddenType));
+ }
+
+ private Function<Symbol, Type> getSymbolToType(final Symbol overriddenSymbol, final Type overriddenType) {
+ return getLocalVariableType(overriddenSymbol).type == overriddenType ? getSymbolToType() :
+ new SymbolToTypeOverride(overriddenSymbol, overriddenType);
+ }
+
+ private class SymbolToTypeOverride implements Function<Symbol, Type> {
+ private final Function<Symbol, Type> originalSymbolToType = getSymbolToType();
+ private final Symbol overriddenSymbol;
+ private final Type overriddenType;
+
+ SymbolToTypeOverride(final Symbol overriddenSymbol, final Type overriddenType) {
+ this.overriddenSymbol = overriddenSymbol;
+ this.overriddenType = overriddenType;
+ }
+
+ @Override
+ public Type apply(final Symbol symbol) {
+ return symbol == overriddenSymbol ? overriddenType : originalSymbolToType.apply(symbol);
+ }
+ }
}
diff --git a/src/jdk/nashorn/internal/codegen/Lower.java b/src/jdk/nashorn/internal/codegen/Lower.java
index 724c6f1e..a30e79a4 100644
--- a/src/jdk/nashorn/internal/codegen/Lower.java
+++ b/src/jdk/nashorn/internal/codegen/Lower.java
@@ -34,6 +34,8 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.ListIterator;
+import java.util.regex.Pattern;
+import jdk.nashorn.internal.ir.AccessNode;
import jdk.nashorn.internal.ir.BaseNode;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.Block;
@@ -52,6 +54,7 @@ import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.IfNode;
+import jdk.nashorn.internal.ir.IndexNode;
import jdk.nashorn.internal.ir.JumpStatement;
import jdk.nashorn.internal.ir.LabelNode;
import jdk.nashorn.internal.ir.LexicalContext;
@@ -93,6 +96,10 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
private final DebugLogger log;
+ // Conservative pattern to test if element names consist of characters valid for identifiers.
+ // This matches any non-zero length alphanumeric string including _ and $ and not starting with a digit.
+ private static Pattern SAFE_PROPERTY_NAME = Pattern.compile("[a-zA-Z_$][\\w$]*");
+
/**
* Constructor.
*/
@@ -140,7 +147,7 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
}
});
- this.log = initLogger(compiler.getContext());
+ this.log = initLogger(compiler.getContext());
}
@Override
@@ -181,6 +188,28 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
}
@Override
+ public Node leaveIndexNode(final IndexNode indexNode) {
+ final String name = getConstantPropertyName(indexNode.getIndex());
+ if (name != null) {
+ // If index node is a constant property name convert index node to access node.
+ assert Token.descType(indexNode.getToken()) == TokenType.LBRACKET;
+ return new AccessNode(indexNode.getToken(), indexNode.getFinish(), indexNode.getBase(), name);
+ }
+ return super.leaveIndexNode(indexNode);
+ }
+
+ // If expression is a primitive literal that is not an array index and does return its string value. Else return null.
+ private static String getConstantPropertyName(final Expression expression) {
+ if (expression instanceof LiteralNode.PrimitiveLiteralNode) {
+ final Object value = ((LiteralNode) expression).getValue();
+ if (value instanceof String && SAFE_PROPERTY_NAME.matcher((String) value).matches()) {
+ return (String) value;
+ }
+ }
+ return null;
+ }
+
+ @Override
public Node leaveExpressionStatement(final ExpressionStatement expressionStatement) {
final Expression expr = expressionStatement.getExpression();
ExpressionStatement node = expressionStatement;
@@ -275,7 +304,7 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
@Override
public Node leaveSwitchNode(final SwitchNode switchNode) {
- if(!switchNode.isInteger()) {
+ if(!switchNode.isUniqueInteger()) {
// Wrap it in a block so its internally created tag is restricted in scope
addStatementEnclosedInBlock(switchNode);
} else {
@@ -525,7 +554,7 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
if (isAlwaysTrue(test)) {
//turn it into a for node without a test.
- final ForNode forNode = (ForNode)new ForNode(whileNode.getLineNumber(), whileNode.getToken(), whileNode.getFinish(), body, ForNode.IS_FOR).accept(this);
+ final ForNode forNode = (ForNode)new ForNode(whileNode.getLineNumber(), whileNode.getToken(), whileNode.getFinish(), body, 0).accept(this);
lc.replace(whileNode, forNode);
return forNode;
}
diff --git a/src/jdk/nashorn/internal/codegen/MapCreator.java b/src/jdk/nashorn/internal/codegen/MapCreator.java
index 3ba3f630..1bec86fc 100644
--- a/src/jdk/nashorn/internal/codegen/MapCreator.java
+++ b/src/jdk/nashorn/internal/codegen/MapCreator.java
@@ -152,6 +152,10 @@ public class MapCreator<T> {
flags |= Property.NOT_WRITABLE;
}
+ if (symbol.isBlockScoped()) {
+ flags |= Property.IS_LEXICAL_BINDING;
+ }
+
// Mark symbol as needing declaration. Access before declaration will throw a ReferenceError.
if (symbol.isBlockScoped() && symbol.isScope()) {
flags |= Property.NEEDS_DECLARATION;
diff --git a/src/jdk/nashorn/internal/codegen/MethodEmitter.java b/src/jdk/nashorn/internal/codegen/MethodEmitter.java
index 87bb297f..362a7363 100644
--- a/src/jdk/nashorn/internal/codegen/MethodEmitter.java
+++ b/src/jdk/nashorn/internal/codegen/MethodEmitter.java
@@ -2214,10 +2214,10 @@ public class MethodEmitter implements Emitter {
* @param name name of property
* @param flags call site flags
* @param isMethod should it prefer retrieving methods
- *
+ * @param isIndex is this an index operation?
* @return the method emitter
*/
- MethodEmitter dynamicGet(final Type valueType, final String name, final int flags, final boolean isMethod) {
+ MethodEmitter dynamicGet(final Type valueType, final String name, final int flags, final boolean isMethod, final boolean isIndex) {
debug("dynamic_get", name, valueType, getProgramPoint(flags));
Type type = valueType;
@@ -2226,8 +2226,8 @@ public class MethodEmitter implements Emitter {
}
popType(Type.SCOPE);
- method.visitInvokeDynamicInsn((isMethod ? "dyn:getMethod|getProp|getElem:" : "dyn:getProp|getElem|getMethod:") +
- NameCodec.encode(name), Type.getMethodDescriptor(type, Type.OBJECT), LINKERBOOTSTRAP, flags);
+ method.visitInvokeDynamicInsn(dynGetOperation(isMethod, isIndex) + ':' + NameCodec.encode(name),
+ Type.getMethodDescriptor(type, Type.OBJECT), LINKERBOOTSTRAP, flags);
pushType(type);
convert(valueType); //most probably a nop
@@ -2240,8 +2240,9 @@ public class MethodEmitter implements Emitter {
*
* @param name name of property
* @param flags call site flags
+ * @param isIndex is this an index operation?
*/
- void dynamicSet(final String name, final int flags) {
+ void dynamicSet(final String name, final int flags, final boolean isIndex) {
assert !isOptimistic(flags);
debug("dynamic_set", name, peekType());
@@ -2253,7 +2254,8 @@ public class MethodEmitter implements Emitter {
popType(type);
popType(Type.SCOPE);
- method.visitInvokeDynamicInsn("dyn:setProp|setElem:" + NameCodec.encode(name), methodDescriptor(void.class, Object.class, type.getTypeClass()), LINKERBOOTSTRAP, flags);
+ method.visitInvokeDynamicInsn(dynSetOperation(isIndex) + ':' + NameCodec.encode(name),
+ methodDescriptor(void.class, Object.class, type.getTypeClass()), LINKERBOOTSTRAP, flags);
}
/**
@@ -2286,7 +2288,7 @@ public class MethodEmitter implements Emitter {
final String signature = Type.getMethodDescriptor(resultType, Type.OBJECT /*e.g STRING->OBJECT*/, index);
- method.visitInvokeDynamicInsn(isMethod ? "dyn:getMethod|getElem|getProp" : "dyn:getElem|getProp|getMethod", signature, LINKERBOOTSTRAP, flags);
+ method.visitInvokeDynamicInsn(dynGetOperation(isMethod, true), signature, LINKERBOOTSTRAP, flags);
pushType(resultType);
if (result.isBoolean()) {
@@ -2500,6 +2502,18 @@ public class MethodEmitter implements Emitter {
}
}
+ private static String dynGetOperation(final boolean isMethod, final boolean isIndex) {
+ if (isMethod) {
+ return isIndex ? "dyn:getMethod|getElem|getProp" : "dyn:getMethod|getProp|getElem";
+ } else {
+ return isIndex ? "dyn:getElem|getProp|getMethod" : "dyn:getProp|getElem|getMethod";
+ }
+ }
+
+ private static String dynSetOperation(final boolean isIndex) {
+ return isIndex ? "dyn:setElem|setProp" : "dyn:setProp|setElem";
+ }
+
private Type emitLocalVariableConversion(final LocalVariableConversion conversion, final boolean onlySymbolLiveValue) {
final Type from = conversion.getFrom();
final Type to = conversion.getTo();
diff --git a/src/jdk/nashorn/internal/codegen/OptimisticTypesPersistence.java b/src/jdk/nashorn/internal/codegen/OptimisticTypesPersistence.java
index 05a76872..53a107e6 100644
--- a/src/jdk/nashorn/internal/codegen/OptimisticTypesPersistence.java
+++ b/src/jdk/nashorn/internal/codegen/OptimisticTypesPersistence.java
@@ -33,6 +33,8 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -221,11 +223,37 @@ public final class OptimisticTypesPersistence {
private static void reportError(final String msg, final File file, final Exception e) {
final long now = System.currentTimeMillis();
if(now - lastReportedError > ERROR_REPORT_THRESHOLD) {
- getLogger().warning(String.format("Failed to %s %s", msg, file), e);
+ reportError(String.format("Failed to %s %s", msg, file), e);
lastReportedError = now;
}
}
+ /**
+ * Logs an error message with warning severity (reasoning being that we're reporting an error that'll disable the
+ * type info cache, but it's only logged as a warning because that doesn't prevent Nashorn from running, it just
+ * disables a performance-enhancing cache).
+ * @param msg the message to log
+ * @param e the exception that represents the error.
+ */
+ private static void reportError(final String msg, final Exception e) {
+ getLogger().warning(msg, "\n", exceptionToString(e));
+ }
+
+ /**
+ * A helper that prints an exception stack trace into a string. We have to do this as if we just pass the exception
+ * to {@link DebugLogger#warning(Object...)}, it will only log the exception message and not the stack, making
+ * problems harder to diagnose.
+ * @param e the exception
+ * @return the string representation of {@link Exception#printStackTrace()} output.
+ */
+ private static String exceptionToString(final Exception e) {
+ final StringWriter sw = new StringWriter();
+ final PrintWriter pw = new PrintWriter(sw, false);
+ e.printStackTrace(pw);
+ pw.flush();
+ return sw.toString();
+ }
+
private static File createBaseCacheDir() {
if(MAX_FILES == 0 || Options.getBooleanProperty("nashorn.typeInfo.disabled")) {
return null;
@@ -233,7 +261,7 @@ public final class OptimisticTypesPersistence {
try {
return createBaseCacheDirPrivileged();
} catch(final Exception e) {
- getLogger().warning("Failed to create cache dir", e);
+ reportError("Failed to create cache dir", e);
return null;
}
}
@@ -267,7 +295,7 @@ public final class OptimisticTypesPersistence {
try {
return createCacheDirPrivileged(baseDir);
} catch(final Exception e) {
- getLogger().warning("Failed to create cache dir", e);
+ reportError("Failed to create cache dir", e);
return null;
}
}
@@ -280,7 +308,7 @@ public final class OptimisticTypesPersistence {
try {
versionDirName = getVersionDirName();
} catch(final Exception e) {
- getLogger().warning("Failed to calculate version dir name", e);
+ reportError("Failed to calculate version dir name", e);
return null;
}
final File versionDir = new File(baseDir, versionDirName);
@@ -323,10 +351,17 @@ public final class OptimisticTypesPersistence {
* per-code-version directory. Normally, this will create the SHA-1 digest of the nashorn.jar. In case the classpath
* for nashorn is local directory (e.g. during development), this will create the string "dev-" followed by the
* timestamp of the most recent .class file.
- * @return
+ *
+ * @return digest of currently running nashorn
+ * @throws Exception if digest could not be created
*/
- private static String getVersionDirName() throws Exception {
- final URL url = OptimisticTypesPersistence.class.getResource("");
+ public static String getVersionDirName() throws Exception {
+ // NOTE: getResource("") won't work if the JAR file doesn't have directory entries (and JAR files in JDK distro
+ // don't, or at least it's a bad idea counting on it). Alternatively, we could've tried
+ // getResource("OptimisticTypesPersistence.class") but behavior of getResource with regard to its willingness
+ // to hand out URLs to .class files is also unspecified. Therefore, the most robust way to obtain an URL to our
+ // package is to have a small non-class anchor file and start out from its URL.
+ final URL url = OptimisticTypesPersistence.class.getResource("anchor.properties");
final String protocol = url.getProtocol();
if (protocol.equals("jar")) {
// Normal deployment: nashorn.jar
diff --git a/src/jdk/nashorn/internal/codegen/SharedScopeCall.java b/src/jdk/nashorn/internal/codegen/SharedScopeCall.java
index 56da4e00..078324cc 100644
--- a/src/jdk/nashorn/internal/codegen/SharedScopeCall.java
+++ b/src/jdk/nashorn/internal/codegen/SharedScopeCall.java
@@ -156,7 +156,7 @@ class SharedScopeCall {
assert !isCall || valueType.isObject(); // Callables are always objects
// If flags are optimistic, but we're doing a call, remove optimistic flags from the getter, as they obviously
// only apply to the call.
- method.dynamicGet(valueType, symbol.getName(), isCall ? CodeGenerator.nonOptimisticFlags(flags) : flags, isCall);
+ method.dynamicGet(valueType, symbol.getName(), isCall ? CodeGenerator.nonOptimisticFlags(flags) : flags, isCall, false);
// If this is a get we're done, otherwise call the value as function.
if (isCall) {
diff --git a/src/jdk/nashorn/internal/codegen/SpillObjectCreator.java b/src/jdk/nashorn/internal/codegen/SpillObjectCreator.java
index 4fa51091..40d12dfb 100644
--- a/src/jdk/nashorn/internal/codegen/SpillObjectCreator.java
+++ b/src/jdk/nashorn/internal/codegen/SpillObjectCreator.java
@@ -88,7 +88,7 @@ public final class SpillObjectCreator extends ObjectCreator<Expression> {
final Property property = propertyMap.findProperty(key);
if (property != null) {
// normal property key
- property.setCurrentType(JSType.unboxedFieldType(constantValue));
+ property.setType(JSType.unboxedFieldType(constantValue));
final int slot = property.getSlot();
if (!OBJECT_FIELDS_ONLY && constantValue instanceof Number) {
jpresetValues[slot] = ObjectClassGenerator.pack((Number)constantValue);
diff --git a/src/jdk/nashorn/internal/codegen/TypeEvaluator.java b/src/jdk/nashorn/internal/codegen/TypeEvaluator.java
index 4f3bc07f..d5282a8b 100644
--- a/src/jdk/nashorn/internal/codegen/TypeEvaluator.java
+++ b/src/jdk/nashorn/internal/codegen/TypeEvaluator.java
@@ -117,7 +117,7 @@ final class TypeEvaluator {
}
final Property property = find.getProperty();
- final Class<?> propertyClass = property.getCurrentType();
+ final Class<?> propertyClass = property.getType();
if (propertyClass == null) {
// propertyClass == null means its value is Undefined. It is probably not initialized yet, so we won't make
// a type assumption yet.
diff --git a/src/jdk/nashorn/internal/codegen/anchor.properties b/src/jdk/nashorn/internal/codegen/anchor.properties
new file mode 100644
index 00000000..1a930682
--- /dev/null
+++ b/src/jdk/nashorn/internal/codegen/anchor.properties
@@ -0,0 +1,27 @@
+#
+# Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation. Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle in the LICENSE file that accompanied this code.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+
+
+# This file exists only so OptimisticTypesPersistence.getVersionDirName() can take its URL.
diff --git a/src/jdk/nashorn/internal/codegen/types/IntType.java b/src/jdk/nashorn/internal/codegen/types/IntType.java
index dfa77467..43b06806 100644
--- a/src/jdk/nashorn/internal/codegen/types/IntType.java
+++ b/src/jdk/nashorn/internal/codegen/types/IntType.java
@@ -55,6 +55,7 @@ import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.nashorn.internal.codegen.CompilerConstants;
+import jdk.nashorn.internal.runtime.JSType;
/**
* Type class: INT
@@ -230,19 +231,21 @@ class IntType extends BitwiseType {
@Override
public Type div(final MethodVisitor method, final int programPoint) {
- // Never perform non-optimistic integer division in JavaScript.
- assert programPoint != INVALID_PROGRAM_POINT;
-
- method.visitInvokeDynamicInsn("idiv", "(II)I", MATHBOOTSTRAP, programPoint);
+ if (programPoint == INVALID_PROGRAM_POINT) {
+ JSType.DIV_ZERO.invoke(method);
+ } else {
+ method.visitInvokeDynamicInsn("idiv", "(II)I", MATHBOOTSTRAP, programPoint);
+ }
return INT;
}
@Override
public Type rem(final MethodVisitor method, final int programPoint) {
- // Never perform non-optimistic integer remainder in JavaScript.
- assert programPoint != INVALID_PROGRAM_POINT;
-
- method.visitInvokeDynamicInsn("irem", "(II)I", MATHBOOTSTRAP, programPoint);
+ if (programPoint == INVALID_PROGRAM_POINT) {
+ JSType.REM_ZERO.invoke(method);
+ } else {
+ method.visitInvokeDynamicInsn("irem", "(II)I", MATHBOOTSTRAP, programPoint);
+ }
return INT;
}
diff --git a/src/jdk/nashorn/internal/codegen/types/LongType.java b/src/jdk/nashorn/internal/codegen/types/LongType.java
index 7c3b2675..aa2ceb23 100644
--- a/src/jdk/nashorn/internal/codegen/types/LongType.java
+++ b/src/jdk/nashorn/internal/codegen/types/LongType.java
@@ -170,19 +170,21 @@ class LongType extends BitwiseType {
@Override
public Type div(final MethodVisitor method, final int programPoint) {
- // Never perform non-optimistic integer division in JavaScript.
- assert programPoint != INVALID_PROGRAM_POINT;
-
- method.visitInvokeDynamicInsn("ldiv", "(JJ)J", MATHBOOTSTRAP, programPoint);
+ if (programPoint == INVALID_PROGRAM_POINT) {
+ JSType.DIV_ZERO_LONG.invoke(method);
+ } else {
+ method.visitInvokeDynamicInsn("ldiv", "(JJ)J", MATHBOOTSTRAP, programPoint);
+ }
return LONG;
}
@Override
public Type rem(final MethodVisitor method, final int programPoint) {
- // Never perform non-optimistic integer remainder in JavaScript.
- assert programPoint != INVALID_PROGRAM_POINT;
-
- method.visitInvokeDynamicInsn("lrem", "(JJ)J", MATHBOOTSTRAP, programPoint);
+ if (programPoint == INVALID_PROGRAM_POINT) {
+ JSType.REM_ZERO_LONG.invoke(method);
+ } else {
+ method.visitInvokeDynamicInsn("lrem", "(JJ)J", MATHBOOTSTRAP, programPoint);
+ }
return LONG;
}
diff --git a/src/jdk/nashorn/internal/codegen/types/Type.java b/src/jdk/nashorn/internal/codegen/types/Type.java
index 332ee55e..2cf2d9b8 100644
--- a/src/jdk/nashorn/internal/codegen/types/Type.java
+++ b/src/jdk/nashorn/internal/codegen/types/Type.java
@@ -356,7 +356,7 @@ public abstract class Type implements Comparable<Type>, BytecodeOps, Serializabl
final int pp = input.readInt();
final int typeChar = input.readByte();
final Type type;
- switch(typeChar) {
+ switch (typeChar) {
case 'L': type = Type.OBJECT; break;
case 'D': type = Type.NUMBER; break;
case 'J': type = Type.LONG; break;
@@ -376,13 +376,13 @@ public abstract class Type implements Comparable<Type>, BytecodeOps, Serializabl
}
private static jdk.internal.org.objectweb.asm.Type lookupInternalType(final Class<?> type) {
- final Map<Class<?>, jdk.internal.org.objectweb.asm.Type> cache = INTERNAL_TYPE_CACHE;
- jdk.internal.org.objectweb.asm.Type itype = cache.get(type);
+ final Map<Class<?>, jdk.internal.org.objectweb.asm.Type> c = INTERNAL_TYPE_CACHE;
+ jdk.internal.org.objectweb.asm.Type itype = c.get(type);
if (itype != null) {
return itype;
}
itype = jdk.internal.org.objectweb.asm.Type.getType(type);
- cache.put(type, itype);
+ c.put(type, itype);
return itype;
}
@@ -586,6 +586,7 @@ public abstract class Type implements Comparable<Type>, BytecodeOps, Serializabl
public int getSlots() {
return slots;
}
+
/**
* Returns the widest or most common of two types
*
@@ -609,6 +610,18 @@ public abstract class Type implements Comparable<Type>, BytecodeOps, Serializabl
}
/**
+ * Returns the widest or most common of two types, given as classes
+ *
+ * @param type0 type one
+ * @param type1 type two
+ *
+ * @return the widest type
+ */
+ public static Class<?> widest(final Class<?> type0, final Class<?> type1) {
+ return widest(Type.typeFor(type0), Type.typeFor(type1)).getTypeClass();
+ }
+
+ /**
* When doing widening for return types of a function or a ternary operator, it is not valid to widen a boolean to
* anything other than object. Note that this wouldn't be necessary if {@code Type.widest} did not allow
* boolean-to-number widening. Eventually, we should address it there, but it affects too many other parts of the
@@ -1142,6 +1155,10 @@ public abstract class Type implements Comparable<Type>, BytecodeOps, Serializabl
return type;
}
+ /**
+ * Read resolve
+ * @return resolved type
+ */
protected final Object readResolve() {
return Type.typeFor(clazz);
}
diff --git a/src/jdk/nashorn/internal/ir/AccessNode.java b/src/jdk/nashorn/internal/ir/AccessNode.java
index 315ee395..b8b64820 100644
--- a/src/jdk/nashorn/internal/ir/AccessNode.java
+++ b/src/jdk/nashorn/internal/ir/AccessNode.java
@@ -28,6 +28,8 @@ 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.Token;
+import jdk.nashorn.internal.parser.TokenType;
/**
* IR representation of a property access (period operator.)
@@ -101,6 +103,14 @@ public final class AccessNode extends BaseNode {
return property;
}
+ /**
+ * Return true if this node represents an index operation normally represented as {@link IndexNode}.
+ * @return true if an index access.
+ */
+ public boolean isIndex() {
+ return Token.descType(getToken()) == TokenType.LBRACKET;
+ }
+
private AccessNode setBase(final Expression base) {
if (this.base == base) {
return this;
diff --git a/src/jdk/nashorn/internal/ir/BinaryNode.java b/src/jdk/nashorn/internal/ir/BinaryNode.java
index c2456363..f625fe1a 100644
--- a/src/jdk/nashorn/internal/ir/BinaryNode.java
+++ b/src/jdk/nashorn/internal/ir/BinaryNode.java
@@ -264,6 +264,10 @@ public final class BinaryNode extends Expression implements Assignment<Expressio
case COMMARIGHT: {
return rhs.getType(localVariableTypes);
}
+ case AND:
+ case OR:{
+ return Type.widestReturnType(lhs.getType(localVariableTypes), rhs.getType(localVariableTypes));
+ }
default:
if (isComparison()) {
return Type.BOOLEAN;
@@ -337,10 +341,7 @@ public final class BinaryNode extends Expression implements Assignment<Expressio
@Override
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterBinaryNode(this)) {
- if(tokenType().isLeftAssociative()) {
- return visitor.leaveBinaryNode(setLHS((Expression)lhs.accept(visitor)).setRHS((Expression)rhs.accept(visitor)));
- }
- return visitor.leaveBinaryNode(setRHS((Expression)rhs.accept(visitor)).setLHS((Expression)lhs.accept(visitor)));
+ return visitor.leaveBinaryNode(setLHS((Expression)lhs.accept(visitor)).setRHS((Expression)rhs.accept(visitor)));
}
return this;
diff --git a/src/jdk/nashorn/internal/ir/ForNode.java b/src/jdk/nashorn/internal/ir/ForNode.java
index 2847947c..ef6f11db 100644
--- a/src/jdk/nashorn/internal/ir/ForNode.java
+++ b/src/jdk/nashorn/internal/ir/ForNode.java
@@ -45,14 +45,14 @@ public final class ForNode extends LoopNode {
/** Iterator symbol. */
private Symbol iterator;
- /** 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;
+ public static final int IS_FOR_IN = 1 << 0;
/** Is this a normal for each in loop? */
- public static final int IS_FOR_EACH = 1 << 2;
+ public static final int IS_FOR_EACH = 1 << 1;
+
+ /** Does this loop need a per-iteration scope because its init contain a LET declaration? */
+ public static final int PER_ITERATION_SCOPE = 1 << 2;
private final int flags;
@@ -273,4 +273,18 @@ public final class ForNode extends LoopNode {
JoinPredecessor setLocalVariableConversionChanged(final LexicalContext lc, final LocalVariableConversion conversion) {
return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion));
}
+
+ @Override
+ public boolean hasPerIterationScope() {
+ return (flags & PER_ITERATION_SCOPE) != 0;
+ }
+
+ /**
+ * Set the per-iteration-scope flag on this node.
+ * @param lc lexical context
+ * @return the node with flag set
+ */
+ public ForNode setPerIterationScope(final LexicalContext lc) {
+ return setFlags(lc, flags | PER_ITERATION_SCOPE);
+ }
}
diff --git a/src/jdk/nashorn/internal/ir/LexicalContext.java b/src/jdk/nashorn/internal/ir/LexicalContext.java
index 2b59a07c..487d6aeb 100644
--- a/src/jdk/nashorn/internal/ir/LexicalContext.java
+++ b/src/jdk/nashorn/internal/ir/LexicalContext.java
@@ -597,6 +597,20 @@ public class LexicalContext {
throw new AssertionError(target + " was expected in lexical context " + LexicalContext.this + " but wasn't");
}
+ /**
+ * Checks whether the current context is inside a switch statement without explicit blocks (curly braces).
+ * @return true if in unprotected switch statement
+ */
+ public boolean inUnprotectedSwitchContext() {
+ for (int i = sp; i > 0; i--) {
+ final LexicalContextNode next = stack[i];
+ if (next instanceof Block) {
+ return stack[i - 1] instanceof SwitchNode;
+ }
+ }
+ return false;
+ }
+
@Override
public String toString() {
final StringBuffer sb = new StringBuffer();
diff --git a/src/jdk/nashorn/internal/ir/LoopNode.java b/src/jdk/nashorn/internal/ir/LoopNode.java
index e6436ad9..5991a32a 100644
--- a/src/jdk/nashorn/internal/ir/LoopNode.java
+++ b/src/jdk/nashorn/internal/ir/LoopNode.java
@@ -176,4 +176,10 @@ public abstract class LoopNode extends BreakableStatement {
* @return new loop node if changed otherwise the same
*/
public abstract LoopNode setControlFlowEscapes(final LexicalContext lc, final boolean controlFlowEscapes);
+
+ /**
+ * Does this loop have a LET declaration and hence require a per-iteration scope?
+ * @return true if a per-iteration scope is required.
+ */
+ public abstract boolean hasPerIterationScope();
}
diff --git a/src/jdk/nashorn/internal/ir/RuntimeNode.java b/src/jdk/nashorn/internal/ir/RuntimeNode.java
index fd34c3ec..4eca8ff0 100644
--- a/src/jdk/nashorn/internal/ir/RuntimeNode.java
+++ b/src/jdk/nashorn/internal/ir/RuntimeNode.java
@@ -27,7 +27,6 @@ package jdk.nashorn.internal.ir;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -468,11 +467,7 @@ public class RuntimeNode extends Expression implements Optimistic {
@Override
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterRuntimeNode(this)) {
- final List<Expression> newArgs = new ArrayList<>();
- for (final Node arg : args) {
- newArgs.add((Expression)arg.accept(visitor));
- }
- return visitor.leaveRuntimeNode(setArgs(newArgs));
+ return visitor.leaveRuntimeNode(setArgs(Node.accept(visitor, args)));
}
return this;
diff --git a/src/jdk/nashorn/internal/ir/SwitchNode.java b/src/jdk/nashorn/internal/ir/SwitchNode.java
index 8936764c..ca447587 100644
--- a/src/jdk/nashorn/internal/ir/SwitchNode.java
+++ b/src/jdk/nashorn/internal/ir/SwitchNode.java
@@ -48,6 +48,10 @@ public final class SwitchNode extends BreakableStatement {
/** Switch default index. */
private final int defaultCaseIndex;
+ /** True if all cases are 32-bit signed integer constants, without repetitions. It's a prerequisite for
+ * using a tableswitch/lookupswitch when generating code. */
+ private final boolean uniqueInteger;
+
/** Tag symbol. */
private Symbol tag;
@@ -66,15 +70,17 @@ public final class SwitchNode extends BreakableStatement {
this.expression = expression;
this.cases = cases;
this.defaultCaseIndex = defaultCase == null ? -1 : cases.indexOf(defaultCase);
+ this.uniqueInteger = false;
}
private SwitchNode(final SwitchNode switchNode, final Expression expression, final List<CaseNode> cases,
- final int defaultCaseIndex, final LocalVariableConversion conversion) {
+ final int defaultCaseIndex, final LocalVariableConversion conversion, final boolean uniqueInteger) {
super(switchNode, conversion);
this.expression = expression;
this.cases = cases;
this.defaultCaseIndex = defaultCaseIndex;
- this.tag = switchNode.getTag(); //TODO are symbols inhereted as references?
+ this.tag = switchNode.getTag(); //TODO are symbols inherited as references?
+ this.uniqueInteger = uniqueInteger;
}
@Override
@@ -83,7 +89,7 @@ public final class SwitchNode extends BreakableStatement {
for (final CaseNode caseNode : cases) {
newCases.add(new CaseNode(caseNode, caseNode.getTest(), caseNode.getBody(), caseNode.getLocalVariableConversion()));
}
- return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, newCases, defaultCaseIndex, conversion));
+ return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, newCases, defaultCaseIndex, conversion, uniqueInteger));
}
@Override
@@ -151,7 +157,7 @@ public final class SwitchNode extends BreakableStatement {
if (this.cases == cases) {
return this;
}
- return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion));
+ return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger));
}
/**
@@ -183,7 +189,7 @@ public final class SwitchNode extends BreakableStatement {
if (this.expression == expression) {
return this;
}
- return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion));
+ return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger));
}
/**
@@ -205,25 +211,30 @@ public final class SwitchNode extends BreakableStatement {
}
/**
- * Returns true if all cases of this switch statement are 32-bit signed integer constants.
- * @return true if all cases of this switch statement are 32-bit signed integer constants.
+ * Returns true if all cases of this switch statement are 32-bit signed integer constants, without repetitions.
+ * @return true if all cases of this switch statement are 32-bit signed integer constants, without repetitions.
*/
- public boolean isInteger() {
- for (final CaseNode caseNode : cases) {
- final Expression test = caseNode.getTest();
- if (test != null && !isIntegerLiteral(test)) {
- return false;
- }
+ public boolean isUniqueInteger() {
+ return uniqueInteger;
+ }
+
+ /**
+ * Sets whether all cases of this switch statement are 32-bit signed integer constants, without repetitions.
+ * @param lc lexical context
+ * @param uniqueInteger if true, all cases of this switch statement have been determined to be 32-bit signed
+ * integer constants, without repetitions.
+ * @return this switch node, if the value didn't change, or a new switch node with the changed value
+ */
+ public SwitchNode setUniqueInteger(final LexicalContext lc, final boolean uniqueInteger) {
+ if(this.uniqueInteger == uniqueInteger) {
+ return this;
}
- return true;
+ return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger));
}
@Override
JoinPredecessor setLocalVariableConversionChanged(final LexicalContext lc, final LocalVariableConversion conversion) {
- return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion));
+ return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex, conversion, uniqueInteger));
}
- private static boolean isIntegerLiteral(final Expression expr) {
- return expr instanceof LiteralNode && ((LiteralNode<?>)expr).getValue() instanceof Integer;
- }
}
diff --git a/src/jdk/nashorn/internal/ir/VarNode.java b/src/jdk/nashorn/internal/ir/VarNode.java
index 44d7d4c7..1cee8cb5 100644
--- a/src/jdk/nashorn/internal/ir/VarNode.java
+++ b/src/jdk/nashorn/internal/ir/VarNode.java
@@ -45,19 +45,16 @@ public final class VarNode extends Statement implements Assignment<IdentNode> {
/** Is this a var statement (as opposed to a "var" in a for loop statement) */
private final int flags;
- /** Flag that determines if this function node is a statement */
- public static final int IS_STATEMENT = 1 << 0;
-
/** Flag for ES6 LET declaration */
- public static final int IS_LET = 1 << 1;
+ public static final int IS_LET = 1 << 0;
/** Flag for ES6 CONST declaration */
- public static final int IS_CONST = 1 << 2;
+ public static final int IS_CONST = 1 << 1;
/** 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 << 3;
+ public static final int IS_LAST_FUNCTION_DECLARATION = 1 << 2;
/**
* Constructor
@@ -69,7 +66,7 @@ public final class VarNode extends Statement implements Assignment<IdentNode> {
* @param init init node or null if just a declaration
*/
public VarNode(final int lineNumber, final long token, final int finish, final IdentNode name, final Expression init) {
- this(lineNumber, token, finish, name, init, IS_STATEMENT);
+ this(lineNumber, token, finish, name, init, 0);
}
private VarNode(final VarNode varNode, final IdentNode name, final Expression init, final int flags) {
@@ -260,14 +257,6 @@ public final class VarNode extends Statement implements Assignment<IdentNode> {
}
/**
- * Returns true if this is a var statement (as opposed to a var initializer in a for loop).
- * @return true if this is a var statement (as opposed to a var initializer in a for loop).
- */
- public boolean isStatement() {
- return (flags & IS_STATEMENT) != 0;
- }
-
- /**
* Returns true if this is a function declaration.
* @return true if this is a function declaration.
*/
diff --git a/src/jdk/nashorn/internal/ir/WhileNode.java b/src/jdk/nashorn/internal/ir/WhileNode.java
index 9a0981fb..7e60fee0 100644
--- a/src/jdk/nashorn/internal/ir/WhileNode.java
+++ b/src/jdk/nashorn/internal/ir/WhileNode.java
@@ -148,4 +148,9 @@ public final class WhileNode extends LoopNode {
}
return test == null;
}
+
+ @Override
+ public boolean hasPerIterationScope() {
+ return false;
+ }
}
diff --git a/src/jdk/nashorn/internal/objects/ArrayBufferView.java b/src/jdk/nashorn/internal/objects/ArrayBufferView.java
index e33fac3b..fa3b8071 100644
--- a/src/jdk/nashorn/internal/objects/ArrayBufferView.java
+++ b/src/jdk/nashorn/internal/objects/ArrayBufferView.java
@@ -28,7 +28,6 @@ package jdk.nashorn.internal.objects;
import static jdk.nashorn.internal.runtime.ECMAErrors.rangeError;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
-
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import jdk.internal.dynalink.CallSiteDescriptor;
@@ -44,6 +43,9 @@ import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.arrays.ArrayData;
import jdk.nashorn.internal.runtime.arrays.TypedArrayData;
+/**
+ * ArrayBufferView, es6 class or TypedArray implementation
+ */
@ScriptClass("ArrayBufferView")
public abstract class ArrayBufferView extends ScriptObject {
private final NativeArrayBuffer buffer;
@@ -71,6 +73,13 @@ public abstract class ArrayBufferView extends ScriptObject {
setArray(data);
}
+ /**
+ * Constructor
+ *
+ * @param buffer underlying NativeArrayBuffer
+ * @param byteOffset byte offset for buffer
+ * @param elementLength element length in bytes
+ */
protected ArrayBufferView(final NativeArrayBuffer buffer, final int byteOffset, final int elementLength) {
this(buffer, byteOffset, elementLength, Global.instance());
}
@@ -89,22 +98,42 @@ public abstract class ArrayBufferView extends ScriptObject {
return factory().bytesPerElement;
}
+ /**
+ * Buffer getter as per spec
+ * @param self ArrayBufferView instance
+ * @return buffer
+ */
@Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE)
public static Object buffer(final Object self) {
return ((ArrayBufferView)self).buffer;
}
+ /**
+ * Buffer offset getter as per spec
+ * @param self ArrayBufferView instance
+ * @return buffer offset
+ */
@Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE)
public static int byteOffset(final Object self) {
return ((ArrayBufferView)self).byteOffset;
}
+ /**
+ * Byte length getter as per spec
+ * @param self ArrayBufferView instance
+ * @return array buffer view length in bytes
+ */
@Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE)
public static int byteLength(final Object self) {
final ArrayBufferView view = (ArrayBufferView)self;
return ((TypedArrayData<?>)view.getArray()).getElementLength() * view.bytesPerElement();
}
+ /**
+ * Length getter as per spec
+ * @param self ArrayBufferView instance
+ * @return length in elements
+ */
@Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE)
public static int length(final Object self) {
return ((ArrayBufferView)self).elementLength();
@@ -119,15 +148,29 @@ public abstract class ArrayBufferView extends ScriptObject {
return ((TypedArrayData<?>)getArray()).getElementLength();
}
+ /**
+ * Factory class for byte ArrayBufferViews
+ */
protected static abstract class Factory {
final int bytesPerElement;
final int maxElementLength;
+ /**
+ * Constructor
+ *
+ * @param bytesPerElement number of bytes per element for this buffer
+ */
public Factory(final int bytesPerElement) {
this.bytesPerElement = bytesPerElement;
this.maxElementLength = Integer.MAX_VALUE / bytesPerElement;
}
+ /**
+ * Factory method
+ *
+ * @param elementLength number of elements
+ * @return new ArrayBufferView
+ */
public final ArrayBufferView construct(final int elementLength) {
if (elementLength > maxElementLength) {
throw rangeError("inappropriate.array.buffer.length", JSType.toString(elementLength));
@@ -135,15 +178,47 @@ public abstract class ArrayBufferView extends ScriptObject {
return construct(new NativeArrayBuffer(elementLength * bytesPerElement), 0, elementLength);
}
- public abstract ArrayBufferView construct(NativeArrayBuffer buffer, int byteOffset, int elementLength);
-
- public abstract TypedArrayData<?> createArrayData(ByteBuffer nb, int start, int end);
-
+ /**
+ * Factory method
+ *
+ * @param buffer underlying buffer
+ * @param byteOffset byte offset
+ * @param elementLength number of elements
+ *
+ * @return new ArrayBufferView
+ */
+ public abstract ArrayBufferView construct(final NativeArrayBuffer buffer, final int byteOffset, final int elementLength);
+
+ /**
+ * Factory method for array data
+ *
+ * @param nb underlying nativebuffer
+ * @param start start element
+ * @param end end element
+ *
+ * @return new array data
+ */
+ public abstract TypedArrayData<?> createArrayData(final ByteBuffer nb, final int start, final int end);
+
+ /**
+ * Get the class name for this type of buffer
+ *
+ * @return class name
+ */
public abstract String getClassName();
}
+ /**
+ * Get the factor for this kind of buffer
+ * @return Factory
+ */
protected abstract Factory factory();
+ /**
+ * Get the prototype for this ArrayBufferView
+ * @param global global instance
+ * @return prototype
+ */
protected abstract ScriptObject getPrototype(final Global global);
@Override
@@ -151,10 +226,23 @@ public abstract class ArrayBufferView extends ScriptObject {
return factory().getClassName();
}
+ /**
+ * Check if this array contains floats
+ * @return true if float array (or double)
+ */
protected boolean isFloatArray() {
return false;
}
+ /**
+ * Inheritable constructor implementation
+ *
+ * @param newObj is this a new constructor
+ * @param args arguments
+ * @param factory factory
+ *
+ * @return new ArrayBufferView
+ */
protected static ArrayBufferView constructorImpl(final boolean newObj, final Object[] args, final Factory factory) {
final Object arg0 = args.length != 0 ? args[0] : 0;
final ArrayBufferView dest;
@@ -200,6 +288,15 @@ public abstract class ArrayBufferView extends ScriptObject {
return dest;
}
+ /**
+ * Inheritable implementation of set, if no efficient implementation is available
+ *
+ * @param self ArrayBufferView instance
+ * @param array array
+ * @param offset0 array offset
+ *
+ * @return result of setter
+ */
protected static Object setImpl(final Object self, final Object array, final Object offset0) {
final ArrayBufferView dest = (ArrayBufferView)self;
final int length;
@@ -244,6 +341,15 @@ public abstract class ArrayBufferView extends ScriptObject {
return (int)(length & Integer.MAX_VALUE);
}
+ /**
+ * Implementation of subarray if no efficient override exists
+ *
+ * @param self ArrayBufferView instance
+ * @param begin0 begin index
+ * @param end0 end index
+ *
+ * @return sub array
+ */
protected static ScriptObject subarrayImpl(final Object self, final Object begin0, final Object end0) {
final ArrayBufferView arrayView = (ArrayBufferView)self;
final int byteOffset = arrayView.byteOffset;
diff --git a/src/jdk/nashorn/internal/objects/Global.java b/src/jdk/nashorn/internal/objects/Global.java
index eb56de0e..bb3e13d2 100644
--- a/src/jdk/nashorn/internal/objects/Global.java
+++ b/src/jdk/nashorn/internal/objects/Global.java
@@ -29,10 +29,12 @@ import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
+
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
import java.lang.invoke.SwitchPoint;
import java.lang.reflect.Field;
import java.util.ArrayList;
@@ -41,9 +43,9 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicReference;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
+import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
import jdk.nashorn.api.scripting.ClassFilter;
@@ -54,6 +56,7 @@ import jdk.nashorn.internal.objects.annotations.Property;
import jdk.nashorn.internal.objects.annotations.ScriptClass;
import jdk.nashorn.internal.runtime.ConsString;
import jdk.nashorn.internal.runtime.Context;
+import jdk.nashorn.internal.runtime.ECMAErrors;
import jdk.nashorn.internal.runtime.GlobalConstants;
import jdk.nashorn.internal.runtime.GlobalFunctions;
import jdk.nashorn.internal.runtime.JSType;
@@ -70,6 +73,7 @@ import jdk.nashorn.internal.runtime.Specialization;
import jdk.nashorn.internal.runtime.arrays.ArrayData;
import jdk.nashorn.internal.runtime.linker.Bootstrap;
import jdk.nashorn.internal.runtime.linker.InvokeByName;
+import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
import jdk.nashorn.internal.runtime.regexp.RegExpResult;
import jdk.nashorn.internal.scripts.JO;
@@ -411,13 +415,14 @@ public final class Global extends ScriptObject implements Scope {
// Used to store the last RegExp result to support deprecated RegExp constructor properties
private RegExpResult lastRegExpResult;
- private static final MethodHandle EVAL = findOwnMH_S("eval", Object.class, Object.class, Object.class);
- private static final MethodHandle NO_SUCH_PROPERTY = findOwnMH_S(NO_SUCH_PROPERTY_NAME, Object.class, Object.class, Object.class);
- private static final MethodHandle PRINT = findOwnMH_S("print", Object.class, Object.class, Object[].class);
- private static final MethodHandle PRINTLN = findOwnMH_S("println", Object.class, Object.class, Object[].class);
- private static final MethodHandle LOAD = findOwnMH_S("load", Object.class, Object.class, Object.class);
- private static final MethodHandle LOADWITHNEWGLOBAL = findOwnMH_S("loadWithNewGlobal", Object.class, Object.class, Object[].class);
- private static final MethodHandle EXIT = findOwnMH_S("exit", Object.class, Object.class, Object.class);
+ private static final MethodHandle EVAL = findOwnMH_S("eval", Object.class, Object.class, Object.class);
+ private static final MethodHandle NO_SUCH_PROPERTY = findOwnMH_S(NO_SUCH_PROPERTY_NAME, Object.class, Object.class, Object.class);
+ private static final MethodHandle PRINT = findOwnMH_S("print", Object.class, Object.class, Object[].class);
+ private static final MethodHandle PRINTLN = findOwnMH_S("println", Object.class, Object.class, Object[].class);
+ private static final MethodHandle LOAD = findOwnMH_S("load", Object.class, Object.class, Object.class);
+ private static final MethodHandle LOAD_WITH_NEW_GLOBAL = findOwnMH_S("loadWithNewGlobal", Object.class, Object.class, Object[].class);
+ private static final MethodHandle EXIT = findOwnMH_S("exit", Object.class, Object.class, Object.class);
+ private static final MethodHandle LEXICAL_SCOPE_FILTER = findOwnMH_S("lexicalScopeFilter", Object.class, Object.class);
// initialized by nasgen
private static PropertyMap $nasgenmap$;
@@ -430,6 +435,12 @@ public final class Global extends ScriptObject implements Scope {
// current ScriptEngine associated - can be null.
private ScriptEngine engine;
+ // ES6 global lexical scope.
+ private final LexicalScope lexicalScope;
+
+ // Switchpoint for non-constant global callsites in the presence of ES6 lexical scope.
+ private SwitchPoint lexicalScopeSwitchPoint;
+
/**
* Set the current script context
* @param scontext script context
@@ -438,9 +449,6 @@ public final class Global extends ScriptObject implements Scope {
this.scontext = scontext;
}
- // global constants for this global - they can be replaced with MethodHandle.constant until invalidated
- private static AtomicReference<GlobalConstants> gcsInstance = new AtomicReference<>();
-
@Override
protected Context getContext() {
return context;
@@ -470,11 +478,7 @@ public final class Global extends ScriptObject implements Scope {
super(checkAndGetMap(context));
this.context = context;
this.setIsScope();
- //we can only share one instance of Global constants between globals, or we consume way too much
- //memory - this is good enough for most programs
- while (gcsInstance.get() == null) {
- gcsInstance.compareAndSet(null, new GlobalConstants(context.getLogger(GlobalConstants.class)));
- }
+ this.lexicalScope = context.getEnv()._es6 ? new LexicalScope(this) : null;
}
/**
@@ -493,15 +497,6 @@ public final class Global extends ScriptObject implements Scope {
}
/**
- * Return the global constants map for fields that
- * can be accessed as MethodHandle.constant
- * @return constant map
- */
- public static GlobalConstants getConstants() {
- return gcsInstance.get();
- }
-
- /**
* Check if we have a Global instance
* @return true if one exists
*/
@@ -1712,6 +1707,133 @@ public final class Global extends ScriptObject implements Scope {
splitState = state;
}
+ /**
+ * Return the ES6 global scope for lexically declared bindings.
+ * @return the ES6 lexical global scope.
+ */
+ public final ScriptObject getLexicalScope() {
+ assert context.getEnv()._es6;
+ return lexicalScope;
+ }
+
+ @Override
+ public void addBoundProperties(final ScriptObject source, final jdk.nashorn.internal.runtime.Property[] properties) {
+ PropertyMap ownMap = getMap();
+ LexicalScope lexicalScope = null;
+ PropertyMap lexicalMap = null;
+ boolean hasLexicalDefinitions = false;
+
+ if (context.getEnv()._es6) {
+ lexicalScope = (LexicalScope) getLexicalScope();
+ lexicalMap = lexicalScope.getMap();
+
+ for (final jdk.nashorn.internal.runtime.Property property : properties) {
+ if (property.isLexicalBinding()) {
+ hasLexicalDefinitions = true;
+ }
+ // ES6 15.1.8 steps 6. and 7.
+ final jdk.nashorn.internal.runtime.Property globalProperty = ownMap.findProperty(property.getKey());
+ if (globalProperty != null && !globalProperty.isConfigurable() && property.isLexicalBinding()) {
+ throw ECMAErrors.syntaxError("redeclare.variable", property.getKey());
+ }
+ final jdk.nashorn.internal.runtime.Property lexicalProperty = lexicalMap.findProperty(property.getKey());
+ if (lexicalProperty != null && !property.isConfigurable()) {
+ throw ECMAErrors.syntaxError("redeclare.variable", property.getKey());
+ }
+ }
+ }
+
+ for (final jdk.nashorn.internal.runtime.Property property : properties) {
+ if (property.isLexicalBinding()) {
+ assert lexicalScope != null;
+ lexicalMap = lexicalScope.addBoundProperty(lexicalMap, source, property);
+
+ if (ownMap.findProperty(property.getKey()) != null) {
+ // If property exists in the global object invalidate any global constant call sites.
+ invalidateGlobalConstant(property.getKey());
+ }
+ } else {
+ ownMap = addBoundProperty(ownMap, source, property);
+ }
+ }
+
+ setMap(ownMap);
+
+ if (hasLexicalDefinitions) {
+ lexicalScope.setMap(lexicalMap);
+ invalidateLexicalSwitchPoint();
+ }
+ }
+
+ @Override
+ public GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
+ final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
+ final boolean isScope = NashornCallSiteDescriptor.isScope(desc);
+
+ if (lexicalScope != null && isScope && !NashornCallSiteDescriptor.isApplyToCall(desc)) {
+ if (lexicalScope.hasOwnProperty(name)) {
+ return lexicalScope.findGetMethod(desc, request, operator);
+ }
+ }
+
+ final GuardedInvocation invocation = super.findGetMethod(desc, request, operator);
+
+ // We want to avoid adding our generic lexical scope switchpoint to global constant invocations,
+ // because those are invalidated per-key in the addBoundProperties method above.
+ // We therefor check if the invocation does already have a switchpoint and the property is non-inherited,
+ // assuming this only applies to global constants. If other non-inherited properties will
+ // start using switchpoints some time in the future we'll have to revisit this.
+ if (isScope && context.getEnv()._es6 && (invocation.getSwitchPoints() == null || !hasOwnProperty(name))) {
+ return invocation.addSwitchPoint(getLexicalScopeSwitchPoint());
+ }
+
+ return invocation;
+ }
+
+ @Override
+ public GuardedInvocation findSetMethod(final CallSiteDescriptor desc, final LinkRequest request) {
+ final boolean isScope = NashornCallSiteDescriptor.isScope(desc);
+
+ if (lexicalScope != null && isScope) {
+ final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
+ if (lexicalScope.hasOwnProperty(name)) {
+ return lexicalScope.findSetMethod(desc, request);
+ }
+ }
+
+ final GuardedInvocation invocation = super.findSetMethod(desc, request);
+
+ if (isScope && context.getEnv()._es6) {
+ return invocation.addSwitchPoint(getLexicalScopeSwitchPoint());
+ }
+
+ return invocation;
+ }
+
+ private synchronized SwitchPoint getLexicalScopeSwitchPoint() {
+ SwitchPoint switchPoint = lexicalScopeSwitchPoint;
+ if (switchPoint == null || switchPoint.hasBeenInvalidated()) {
+ switchPoint = lexicalScopeSwitchPoint = new SwitchPoint();
+ }
+ return switchPoint;
+ }
+
+ private synchronized void invalidateLexicalSwitchPoint() {
+ if (lexicalScopeSwitchPoint != null) {
+ context.getLogger(GlobalConstants.class).info("Invalidating non-constant globals on lexical scope update");
+ SwitchPoint.invalidateAll(new SwitchPoint[]{ lexicalScopeSwitchPoint });
+ }
+ }
+
+
+ @SuppressWarnings("unused")
+ private static Object lexicalScopeFilter(final Object self) {
+ if (self instanceof Global) {
+ return ((Global) self).getLexicalScope();
+ }
+ return self;
+ }
+
private <T extends ScriptObject> T initConstructorAndSwitchPoint(final String name, final Class<T> clazz) {
final T func = initConstructor(name, clazz);
tagBuiltinProperties(name, func);
@@ -1757,7 +1879,7 @@ public final class Global extends ScriptObject implements Scope {
this.unescape = ScriptFunctionImpl.makeFunction("unescape", GlobalFunctions.UNESCAPE);
this.print = ScriptFunctionImpl.makeFunction("print", env._print_no_newline ? PRINT : PRINTLN);
this.load = ScriptFunctionImpl.makeFunction("load", LOAD);
- this.loadWithNewGlobal = ScriptFunctionImpl.makeFunction("loadWithNewGlobal", LOADWITHNEWGLOBAL);
+ this.loadWithNewGlobal = ScriptFunctionImpl.makeFunction("loadWithNewGlobal", LOAD_WITH_NEW_GLOBAL);
this.exit = ScriptFunctionImpl.makeFunction("exit", EXIT);
this.quit = ScriptFunctionImpl.makeFunction("quit", EXIT);
@@ -2223,4 +2345,36 @@ public final class Global extends ScriptObject implements Scope {
protected boolean isGlobal() {
return true;
}
+
+ /**
+ * A class representing the ES6 global lexical scope.
+ */
+ private static class LexicalScope extends ScriptObject {
+
+ LexicalScope(final ScriptObject proto) {
+ super(proto, PropertyMap.newMap());
+ }
+
+ @Override
+ protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
+ return filterInvocation(super.findGetMethod(desc, request, operator));
+ }
+
+ @Override
+ protected GuardedInvocation findSetMethod(final CallSiteDescriptor desc, final LinkRequest request) {
+ return filterInvocation(super.findSetMethod(desc, request));
+ }
+
+ @Override
+ protected PropertyMap addBoundProperty(final PropertyMap propMap, final ScriptObject source, final jdk.nashorn.internal.runtime.Property property) {
+ // We override this method just to make it callable by Global
+ return super.addBoundProperty(propMap, source, property);
+ }
+
+ private static GuardedInvocation filterInvocation(final GuardedInvocation invocation) {
+ final MethodType type = invocation.getInvocation().type();
+ return invocation.asType(type.changeParameterType(0, Object.class)).filterArguments(0, LEXICAL_SCOPE_FILTER);
+ }
+ }
+
}
diff --git a/src/jdk/nashorn/internal/objects/NativeArray.java b/src/jdk/nashorn/internal/objects/NativeArray.java
index c83e8883..047d612a 100644
--- a/src/jdk/nashorn/internal/objects/NativeArray.java
+++ b/src/jdk/nashorn/internal/objects/NativeArray.java
@@ -33,8 +33,8 @@ import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex;
import static jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator.arrayLikeIterator;
import static jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator.reverseArrayLikeIterator;
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_STRICT;
+
import java.lang.invoke.MethodHandle;
-import java.lang.invoke.SwitchPoint;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -92,16 +92,6 @@ public final class NativeArray extends ScriptObject implements OptimisticBuiltin
private static final Object CALL_CMP = new Object();
private static final Object TO_LOCALE_STRING = new Object();
- private SwitchPoint lengthMadeNotWritableSwitchPoint;
- private PushLinkLogic pushLinkLogic;
- private PopLinkLogic popLinkLogic;
-
- /**
- * Index for the modification SwitchPoint that triggers when length
- * becomes not writable
- */
- private static final int LENGTH_NOT_WRITABLE_SWITCHPOINT = 0;
-
/*
* Constructors.
*/
@@ -130,7 +120,9 @@ public final class NativeArray extends ScriptObject implements OptimisticBuiltin
this(ArrayData.allocate(array.length));
ArrayData arrayData = this.getArray();
- arrayData.ensure(array.length - 1);
+ if (array.length > 0) {
+ arrayData.ensure(array.length - 1);
+ }
for (int index = 0; index < array.length; index++) {
final Object value = array[index];
@@ -266,13 +258,84 @@ public final class NativeArray extends ScriptObject implements OptimisticBuiltin
@Override
public Object getLength() {
- final long length = getArray().length() & JSType.MAX_UINT;
- if(length < Integer.MAX_VALUE) {
+ final long length = JSType.toUint32(getArray().length());
+ if (length < Integer.MAX_VALUE) {
return (int)length;
}
return length;
}
+ private boolean defineLength(final long oldLen, final PropertyDescriptor oldLenDesc, final PropertyDescriptor desc, final boolean reject) {
+ // Step 3a
+ if (!desc.has(VALUE)) {
+ return super.defineOwnProperty("length", desc, reject);
+ }
+
+ // Step 3b
+ final PropertyDescriptor newLenDesc = desc;
+
+ // Step 3c and 3d - get new length and convert to long
+ final long newLen = NativeArray.validLength(newLenDesc.getValue(), true);
+
+ // Step 3e
+ newLenDesc.setValue(newLen);
+
+ // Step 3f
+ // increasing array length - just need to set new length value (and attributes if any) and return
+ if (newLen >= oldLen) {
+ return super.defineOwnProperty("length", newLenDesc, reject);
+ }
+
+ // Step 3g
+ if (!oldLenDesc.isWritable()) {
+ if (reject) {
+ throw typeError("property.not.writable", "length", ScriptRuntime.safeToString(this));
+ }
+ return false;
+ }
+
+ // Step 3h and 3i
+ final boolean newWritable = !newLenDesc.has(WRITABLE) || newLenDesc.isWritable();
+ if (!newWritable) {
+ newLenDesc.setWritable(true);
+ }
+
+ // Step 3j and 3k
+ final boolean succeeded = super.defineOwnProperty("length", newLenDesc, reject);
+ if (!succeeded) {
+ return false;
+ }
+
+ // Step 3l
+ // make sure that length is set till the point we can delete the old elements
+ long o = oldLen;
+ while (newLen < o) {
+ o--;
+ final boolean deleteSucceeded = delete(o, false);
+ if (!deleteSucceeded) {
+ newLenDesc.setValue(o + 1);
+ if (!newWritable) {
+ newLenDesc.setWritable(false);
+ }
+ super.defineOwnProperty("length", newLenDesc, false);
+ if (reject) {
+ throw typeError("property.not.writable", "length", ScriptRuntime.safeToString(this));
+ }
+ return false;
+ }
+ }
+
+ // Step 3m
+ if (!newWritable) {
+ // make 'length' property not writable
+ final ScriptObject newDesc = Global.newEmptyInstance();
+ newDesc.set(WRITABLE, false, 0);
+ return super.defineOwnProperty("length", newDesc, false);
+ }
+
+ return true;
+ }
+
/**
* ECMA 15.4.5.1 [[DefineOwnProperty]] ( P, Desc, Throw )
*/
@@ -286,82 +349,16 @@ public final class NativeArray extends ScriptObject implements OptimisticBuiltin
// Step 2
// get old length and convert to long
- long oldLen = NativeArray.validLength(oldLenDesc.getValue(), true);
+ final long oldLen = NativeArray.validLength(oldLenDesc.getValue(), true);
// Step 3
if ("length".equals(key)) {
// check for length being made non-writable
+ final boolean result = defineLength(oldLen, oldLenDesc, desc, reject);
if (desc.has(WRITABLE) && !desc.isWritable()) {
setIsLengthNotWritable();
}
-
- // Step 3a
- if (!desc.has(VALUE)) {
- return super.defineOwnProperty("length", desc, reject);
- }
-
- // Step 3b
- final PropertyDescriptor newLenDesc = desc;
-
- // Step 3c and 3d - get new length and convert to long
- final long newLen = NativeArray.validLength(newLenDesc.getValue(), true);
-
- // Step 3e
- newLenDesc.setValue(newLen);
-
- // Step 3f
- // increasing array length - just need to set new length value (and attributes if any) and return
- if (newLen >= oldLen) {
- return super.defineOwnProperty("length", newLenDesc, reject);
- }
-
- // Step 3g
- if (!oldLenDesc.isWritable()) {
- if (reject) {
- throw typeError("property.not.writable", "length", ScriptRuntime.safeToString(this));
- }
- return false;
- }
-
- // Step 3h and 3i
- final boolean newWritable = !newLenDesc.has(WRITABLE) || newLenDesc.isWritable();
- if (!newWritable) {
- newLenDesc.setWritable(true);
- }
-
- // Step 3j and 3k
- final boolean succeeded = super.defineOwnProperty("length", newLenDesc, reject);
- if (!succeeded) {
- return false;
- }
-
- // Step 3l
- // make sure that length is set till the point we can delete the old elements
- while (newLen < oldLen) {
- oldLen--;
- final boolean deleteSucceeded = delete(oldLen, false);
- if (!deleteSucceeded) {
- newLenDesc.setValue(oldLen + 1);
- if (!newWritable) {
- newLenDesc.setWritable(false);
- }
- super.defineOwnProperty("length", newLenDesc, false);
- if (reject) {
- throw typeError("property.not.writable", "length", ScriptRuntime.safeToString(this));
- }
- return false;
- }
- }
-
- // Step 3m
- if (!newWritable) {
- // make 'length' property not writable
- final ScriptObject newDesc = Global.newEmptyInstance();
- newDesc.set(WRITABLE, false, 0);
- return super.defineOwnProperty("length", newDesc, false);
- }
-
- return true;
+ return result;
}
// Step 4a
@@ -437,23 +434,7 @@ public final class NativeArray extends ScriptObject implements OptimisticBuiltin
@Override
public void setIsLengthNotWritable() {
super.setIsLengthNotWritable();
- /*
- * Switchpoints are created lazily. If we link any push or pop site,
- * we need to create the "length made not writable" switchpoint, if it
- * doesn't exist.
- *
- * If the switchpoint already exists, we will find it here, and invalidate
- * it, invalidating all previous callsites that use it.
- *
- * If the switchpoint doesn't exist, no push/pop has been linked so far,
- * because that would create it too. We invalidate it immediately and the
- * check link logic for all future callsites will fail immediately at link
- * time
- */
- if (lengthMadeNotWritableSwitchPoint == null) {
- lengthMadeNotWritableSwitchPoint = new SwitchPoint();
- }
- SwitchPoint.invalidateAll(new SwitchPoint[] { lengthMadeNotWritableSwitchPoint });
+ setArray(ArrayData.setIsLengthNotWritable(getArray()));
}
/**
@@ -476,7 +457,7 @@ public final class NativeArray extends ScriptObject implements OptimisticBuiltin
@Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_CONFIGURABLE)
public static Object length(final Object self) {
if (isArray(self)) {
- return ((ScriptObject) self).getArray().length() & JSType.MAX_UINT;
+ return JSType.toUint32(((ScriptObject) self).getArray().length());
}
return 0;
@@ -490,7 +471,7 @@ public final class NativeArray extends ScriptObject implements OptimisticBuiltin
@Setter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_CONFIGURABLE)
public static void length(final Object self, final Object length) {
if (isArray(self)) {
- ((ScriptObject) self).setLength(validLength(length, true));
+ ((ScriptObject)self).setLength(validLength(length, true));
}
}
@@ -757,12 +738,86 @@ public final class NativeArray extends ScriptObject implements OptimisticBuiltin
* ECMA 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ... ] ] ] )
*
* @param self self reference
+ * @param arg argument
+ * @return resulting NativeArray
+ */
+ @SpecializedFunction(linkLogic=ConcatLinkLogic.class)
+ public static NativeArray concat(final Object self, final int arg) {
+ final ContinuousArrayData newData = getContinuousArrayDataCCE(self, Integer.class).copy(); //get at least an integer data copy of this data
+ newData.fastPush(arg); //add an integer to its end
+ return new NativeArray(newData);
+ }
+
+ /**
+ * ECMA 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ... ] ] ] )
+ *
+ * @param self self reference
+ * @param arg argument
+ * @return resulting NativeArray
+ */
+ @SpecializedFunction(linkLogic=ConcatLinkLogic.class)
+ public static NativeArray concat(final Object self, final long arg) {
+ final ContinuousArrayData newData = getContinuousArrayDataCCE(self, Long.class).copy(); //get at least a long array data copy of this data
+ newData.fastPush(arg); //add a long at the end
+ return new NativeArray(newData);
+ }
+
+ /**
+ * ECMA 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ... ] ] ] )
+ *
+ * @param self self reference
+ * @param arg argument
+ * @return resulting NativeArray
+ */
+ @SpecializedFunction(linkLogic=ConcatLinkLogic.class)
+ public static NativeArray concat(final Object self, final double arg) {
+ final ContinuousArrayData newData = getContinuousArrayDataCCE(self, Double.class).copy(); //get at least a number array data copy of this data
+ newData.fastPush(arg); //add a double at the end
+ return new NativeArray(newData);
+ }
+
+ /**
+ * ECMA 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ... ] ] ] )
+ *
+ * @param self self reference
+ * @param arg argument
+ * @return resulting NativeArray
+ */
+ @SpecializedFunction(linkLogic=ConcatLinkLogic.class)
+ public static NativeArray concat(final Object self, final Object arg) {
+ //arg is [NativeArray] of same type.
+ final ContinuousArrayData selfData = getContinuousArrayDataCCE(self);
+ final ContinuousArrayData newData;
+
+ if (arg instanceof NativeArray) {
+ final ContinuousArrayData argData = (ContinuousArrayData)((NativeArray)arg).getArray();
+ if (argData.isEmpty()) {
+ newData = selfData.copy();
+ } else if (selfData.isEmpty()) {
+ newData = argData.copy();
+ } else {
+ final Class<?> widestElementType = selfData.widest(argData).getBoxedElementType();
+ newData = ((ContinuousArrayData)selfData.convert(widestElementType)).fastConcat((ContinuousArrayData)argData.convert(widestElementType));
+ }
+ } else {
+ newData = getContinuousArrayDataCCE(self, Object.class).copy();
+ newData.fastPush(arg);
+ }
+
+ return new NativeArray(newData);
+ }
+
+ /**
+ * ECMA 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ... ] ] ] )
+ *
+ * @param self self reference
* @param args arguments
* @return resulting NativeArray
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
public static NativeArray concat(final Object self, final Object... args) {
final ArrayList<Object> list = new ArrayList<>();
+
concatToList(list, Global.toObject(self));
for (final Object obj : args) {
@@ -1228,10 +1283,13 @@ public final class NativeArray extends ScriptObject implements OptimisticBuiltin
// 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 (long i = 0; i < len; i = array.nextIndex(i)) {
- if (array.has((int) i)) {
- src.add(array.getObject((int) i));
+
+ for (final Iterator<Long> iter = array.indexIterator(); iter.hasNext(); ) {
+ final long index = iter.next();
+ if (index >= len) {
+ break;
}
+ src.add(array.getObject((int)index));
}
final Object[] sorted = sort(src.toArray(), comparefn);
@@ -1689,16 +1747,18 @@ public final class NativeArray extends ScriptObject implements OptimisticBuiltin
@Override
public SpecializedFunction.LinkLogic getLinkLogic(final Class<? extends LinkLogic> clazz) {
if (clazz == PushLinkLogic.class) {
- return pushLinkLogic == null ? new PushLinkLogic(this) : pushLinkLogic;
+ return PushLinkLogic.INSTANCE;
} else if (clazz == PopLinkLogic.class) {
- return popLinkLogic == null ? new PopLinkLogic(this) : pushLinkLogic;
+ return PopLinkLogic.INSTANCE;
+ } else if (clazz == ConcatLinkLogic.class) {
+ return ConcatLinkLogic.INSTANCE;
}
return null;
}
@Override
public boolean hasPerInstanceAssumptions() {
- return true; //length switchpoint
+ return true; //length writable switchpoint
}
/**
@@ -1707,21 +1767,7 @@ public final class NativeArray extends ScriptObject implements OptimisticBuiltin
* modification switchpoint which is touched when length is written.
*/
private static abstract class ArrayLinkLogic extends SpecializedFunction.LinkLogic {
- private final NativeArray array;
-
- protected ArrayLinkLogic(final NativeArray array) {
- this.array = array;
- }
-
- private SwitchPoint getSwitchPoint() {
- return array.lengthMadeNotWritableSwitchPoint;
- }
-
- private SwitchPoint newSwitchPoint() {
- assert array.lengthMadeNotWritableSwitchPoint == null;
- final SwitchPoint sp = new SwitchPoint();
- array.lengthMadeNotWritableSwitchPoint = sp;
- return sp;
+ protected ArrayLinkLogic() {
}
protected static ContinuousArrayData getContinuousArrayData(final Object self) {
@@ -1742,58 +1788,37 @@ public final class NativeArray extends ScriptObject implements OptimisticBuiltin
public Class<? extends Throwable> getRelinkException() {
return ClassCastException.class;
}
+ }
- @Override
- public boolean hasModificationSwitchPoints() {
- return getSwitchPoint() != null;
- }
+ /**
+ * This is linker logic for optimistic concatenations
+ */
+ private static final class ConcatLinkLogic extends ArrayLinkLogic {
+ private static final LinkLogic INSTANCE = new ConcatLinkLogic();
@Override
- public boolean hasModificationSwitchPoint(final int index) {
- assert index == LENGTH_NOT_WRITABLE_SWITCHPOINT;
- return hasModificationSwitchPoints();
- }
+ public boolean canLink(final Object self, final CallSiteDescriptor desc, final LinkRequest request) {
+ final Object[] args = request.getArguments();
- @Override
- public SwitchPoint getOrCreateModificationSwitchPoint(final int index) {
- assert index == LENGTH_NOT_WRITABLE_SWITCHPOINT;
- SwitchPoint sp = getSwitchPoint();
- if (sp == null) {
- sp = newSwitchPoint();
+ if (args.length != 3) { //single argument check
+ return false;
}
- return sp;
- }
- @Override
- public SwitchPoint[] getOrCreateModificationSwitchPoints() {
- return new SwitchPoint[] { getOrCreateModificationSwitchPoint(LENGTH_NOT_WRITABLE_SWITCHPOINT) };
- }
-
- @Override
- public void invalidateModificationSwitchPoint(final int index) {
- assert index == LENGTH_NOT_WRITABLE_SWITCHPOINT;
- invalidateModificationSwitchPoints();
- }
-
- @Override
- public void invalidateModificationSwitchPoints() {
- final SwitchPoint sp = getSwitchPoint();
- assert sp != null : "trying to invalidate non-existant modified SwitchPoint";
- if (!sp.hasBeenInvalidated()) {
- SwitchPoint.invalidateAll(new SwitchPoint[] { sp });
+ final ContinuousArrayData selfData = getContinuousArrayData(self);
+ if (selfData == null) {
+ return false;
}
- }
- @Override
- public boolean hasInvalidatedModificationSwitchPoint(final int index) {
- assert index == LENGTH_NOT_WRITABLE_SWITCHPOINT;
- return hasInvalidatedModificationSwitchPoints();
- }
+ final Object arg = args[2];
+ //args[2] continuousarray or non arraydata, let past non array datas
+ if (arg instanceof NativeArray) {
+ final ContinuousArrayData argData = getContinuousArrayData(arg);
+ if (argData == null) {
+ return false;
+ }
+ }
- @Override
- public boolean hasInvalidatedModificationSwitchPoints() {
- final SwitchPoint sp = getSwitchPoint();
- return sp != null && !sp.hasBeenInvalidated();
+ return true;
}
}
@@ -1801,9 +1826,7 @@ public final class NativeArray extends ScriptObject implements OptimisticBuiltin
* This is linker logic for optimistic pushes
*/
private static final class PushLinkLogic extends ArrayLinkLogic {
- private PushLinkLogic(final NativeArray array) {
- super(array);
- }
+ private static final LinkLogic INSTANCE = new PushLinkLogic();
@Override
public boolean canLink(final Object self, final CallSiteDescriptor desc, final LinkRequest request) {
@@ -1815,9 +1838,7 @@ public final class NativeArray extends ScriptObject implements OptimisticBuiltin
* This is linker logic for optimistic pops
*/
private static final class PopLinkLogic extends ArrayLinkLogic {
- private PopLinkLogic(final NativeArray array) {
- super(array);
- }
+ private static final LinkLogic INSTANCE = new PopLinkLogic();
/**
* We need to check if we are dealing with a continuous non empty array data here,
@@ -1864,6 +1885,14 @@ public final class NativeArray extends ScriptObject implements OptimisticBuiltin
throw new ClassCastException();
}
+ private static final ContinuousArrayData getContinuousArrayDataCCE(final Object self) {
+ try {
+ return (ContinuousArrayData)((NativeArray)self).getArray();
+ } catch (final NullPointerException e) {
+ throw new ClassCastException();
+ }
+ }
+
private static final ContinuousArrayData getContinuousArrayDataCCE(final Object self, final Class<?> elementType) {
try {
return (ContinuousArrayData)((NativeArray)self).getArray(elementType); //ensure element type can fit "elementType"
diff --git a/src/jdk/nashorn/internal/objects/NativeArrayBuffer.java b/src/jdk/nashorn/internal/objects/NativeArrayBuffer.java
index 4d16ca54..e3c5c00d 100644
--- a/src/jdk/nashorn/internal/objects/NativeArrayBuffer.java
+++ b/src/jdk/nashorn/internal/objects/NativeArrayBuffer.java
@@ -26,7 +26,6 @@
package jdk.nashorn.internal.objects;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
-
import java.nio.ByteBuffer;
import jdk.nashorn.internal.objects.annotations.Attribute;
import jdk.nashorn.internal.objects.annotations.Constructor;
@@ -34,6 +33,7 @@ import jdk.nashorn.internal.objects.annotations.Function;
import jdk.nashorn.internal.objects.annotations.Getter;
import jdk.nashorn.internal.objects.annotations.ScriptClass;
import jdk.nashorn.internal.objects.annotations.SpecializedFunction;
+import jdk.nashorn.internal.objects.annotations.Where;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptObject;
@@ -138,6 +138,19 @@ public final class NativeArrayBuffer extends ScriptObject {
}
/**
+ * Returns true if an object is an ArrayBufferView
+ *
+ * @param self self
+ * @param obj object to check
+ *
+ * @return true if obj is an ArrayBufferView
+ */
+ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
+ public static boolean isView(final Object self, final Object obj) {
+ return obj instanceof ArrayBufferView;
+ }
+
+ /**
* Slice function
* @param self native array buffer
* @param begin0 start byte index
diff --git a/src/jdk/nashorn/internal/objects/NativeDataView.java b/src/jdk/nashorn/internal/objects/NativeDataView.java
index 0adde064..14c11f69 100644
--- a/src/jdk/nashorn/internal/objects/NativeDataView.java
+++ b/src/jdk/nashorn/internal/objects/NativeDataView.java
@@ -27,6 +27,7 @@ package jdk.nashorn.internal.objects;
import static jdk.nashorn.internal.runtime.ECMAErrors.rangeError;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
+
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import jdk.nashorn.internal.objects.annotations.Attribute;
@@ -104,10 +105,10 @@ public class NativeDataView extends ScriptObject {
private NativeDataView(final NativeArrayBuffer arrBuf, final ByteBuffer buf, final int offset, final int length) {
super(Global.instance().getDataViewPrototype(), $nasgenmap$);
- this.buffer = arrBuf;
+ this.buffer = arrBuf;
this.byteOffset = offset;
this.byteLength = length;
- this.buf = buf;
+ this.buf = buf;
}
/**
@@ -134,14 +135,14 @@ public class NativeDataView extends ScriptObject {
throw typeError("not.an.arraybuffer.in.dataview");
}
- final NativeArrayBuffer arrBuf = (NativeArrayBuffer) args[0];
+ final NativeArrayBuffer arrBuf = (NativeArrayBuffer)args[0];
switch (args.length) {
- case 1:
- return new NativeDataView(arrBuf);
- case 2:
- return new NativeDataView(arrBuf, JSType.toInt32(args[1]));
- default:
- return new NativeDataView(arrBuf, JSType.toInt32(args[1]), JSType.toInt32(args[2]));
+ case 1:
+ return new NativeDataView(arrBuf);
+ case 2:
+ return new NativeDataView(arrBuf, JSType.toInt32(args[1]));
+ default:
+ return new NativeDataView(arrBuf, JSType.toInt32(args[1]), JSType.toInt32(args[2]));
}
}
@@ -432,7 +433,7 @@ public class NativeDataView extends ScriptObject {
@SpecializedFunction
public static long getUint32(final Object self, final int byteOffset) {
try {
- return JSType.MAX_UINT & getBuffer(self, false).getInt(JSType.toInt32(byteOffset));
+ return JSType.toUint32(getBuffer(self, false).getInt(JSType.toInt32(byteOffset)));
} catch (final IllegalArgumentException iae) {
throw rangeError(iae, "dataview.offset");
}
@@ -449,7 +450,7 @@ public class NativeDataView extends ScriptObject {
@SpecializedFunction
public static long getUint32(final Object self, final int byteOffset, final boolean littleEndian) {
try {
- return JSType.MAX_UINT & getBuffer(self, littleEndian).getInt(JSType.toInt32(byteOffset));
+ return JSType.toUint32(getBuffer(self, littleEndian).getInt(JSType.toInt32(byteOffset)));
} catch (final IllegalArgumentException iae) {
throw rangeError(iae, "dataview.offset");
}
@@ -994,7 +995,7 @@ public class NativeDataView extends ScriptObject {
private static NativeDataView checkSelf(final Object self) {
if (!(self instanceof NativeDataView)) {
- throw typeError("not.an.arraybuffer", ScriptRuntime.safeToString(self));
+ throw typeError("not.an.arraybuffer.in.dataview", ScriptRuntime.safeToString(self));
}
return (NativeDataView)self;
}
diff --git a/src/jdk/nashorn/internal/objects/NativeDate.java b/src/jdk/nashorn/internal/objects/NativeDate.java
index c85016a5..bb499c26 100644
--- a/src/jdk/nashorn/internal/objects/NativeDate.java
+++ b/src/jdk/nashorn/internal/objects/NativeDate.java
@@ -1045,7 +1045,8 @@ public final class NativeDate extends ScriptObject {
// ECMA 15.9.1.2 TimeWithinDay (t)
private static double timeWithinDay(final double t) {
- return t % msPerDay;
+ final double val = t % msPerDay;
+ return val < 0? val + msPerDay : val;
}
// ECMA 15.9.1.3 InLeapYear (t)
diff --git a/src/jdk/nashorn/internal/objects/NativeDebug.java b/src/jdk/nashorn/internal/objects/NativeDebug.java
index 3d8f1095..c42e7843 100644
--- a/src/jdk/nashorn/internal/objects/NativeDebug.java
+++ b/src/jdk/nashorn/internal/objects/NativeDebug.java
@@ -39,6 +39,7 @@ import jdk.nashorn.internal.runtime.PropertyListeners;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
+import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.events.RuntimeEvent;
import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
@@ -66,6 +67,36 @@ public final class NativeDebug extends ScriptObject {
}
/**
+ * Return the ArrayData class for this ScriptObject
+ * @param self self
+ * @param obj script object to check
+ * @return ArrayData class, or undefined if no ArrayData is present
+ */
+ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
+ public static Object getArrayDataClass(final Object self, final Object obj) {
+ try {
+ return ((ScriptObject)obj).getArray().getClass();
+ } catch (final ClassCastException e) {
+ return ScriptRuntime.UNDEFINED;
+ }
+ }
+
+ /**
+ * Return the ArrayData for this ScriptObject
+ * @param self self
+ * @param obj script object to check
+ * @return ArrayData, ArrayDatas have toString methods, return Undefined if data missing
+ */
+ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
+ public static Object getArrayData(final Object self, final Object obj) {
+ try {
+ return ((ScriptObject)obj).getArray();
+ } catch (final ClassCastException e) {
+ return ScriptRuntime.UNDEFINED;
+ }
+ }
+
+ /**
* Nashorn extension: get context, context utility
*
* @param self self reference
diff --git a/src/jdk/nashorn/internal/objects/NativeFloat32Array.java b/src/jdk/nashorn/internal/objects/NativeFloat32Array.java
index e5ef0b5a..ef7a1ff8 100644
--- a/src/jdk/nashorn/internal/objects/NativeFloat32Array.java
+++ b/src/jdk/nashorn/internal/objects/NativeFloat32Array.java
@@ -90,6 +90,11 @@ public final class NativeFloat32Array extends ArrayBufferView {
}
@Override
+ public Class<?> getBoxedElementType() {
+ return Double.class;
+ }
+
+ @Override
protected MethodHandle getGetElem() {
return GET_ELEM;
}
diff --git a/src/jdk/nashorn/internal/objects/NativeFloat64Array.java b/src/jdk/nashorn/internal/objects/NativeFloat64Array.java
index 1ad61b27..d0413d08 100644
--- a/src/jdk/nashorn/internal/objects/NativeFloat64Array.java
+++ b/src/jdk/nashorn/internal/objects/NativeFloat64Array.java
@@ -99,6 +99,11 @@ public final class NativeFloat64Array extends ArrayBufferView {
return double.class;
}
+ @Override
+ public Class<?> getBoxedElementType() {
+ return Double.class;
+ }
+
private double getElem(final int index) {
try {
return nb.get(index);
diff --git a/src/jdk/nashorn/internal/objects/NativeFunction.java b/src/jdk/nashorn/internal/objects/NativeFunction.java
index c4a79a56..bd2f2dd9 100644
--- a/src/jdk/nashorn/internal/objects/NativeFunction.java
+++ b/src/jdk/nashorn/internal/objects/NativeFunction.java
@@ -48,6 +48,7 @@ import jdk.nashorn.internal.runtime.ScriptEnvironment;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.ScriptRuntime;
+import jdk.nashorn.internal.runtime.linker.Bootstrap;
/**
* ECMA 15.3 Function Objects
@@ -204,11 +205,7 @@ public final class NativeFunction {
* @return function with bound arguments
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
- public static ScriptFunction bind(final Object self, final Object... args) {
- if (!(self instanceof ScriptFunction)) {
- throw typeError("not.a.function", ScriptRuntime.safeToString(self));
- }
-
+ public static Object bind(final Object self, final Object... args) {
final Object thiz = (args.length == 0) ? UNDEFINED : args[0];
Object[] arguments;
@@ -219,7 +216,7 @@ public final class NativeFunction {
arguments = ScriptRuntime.EMPTY_ARRAY;
}
- return ((ScriptFunctionImpl)self).makeBoundFunction(thiz, arguments);
+ return Bootstrap.bindCallable(self, thiz, arguments);
}
/**
diff --git a/src/jdk/nashorn/internal/objects/NativeInt16Array.java b/src/jdk/nashorn/internal/objects/NativeInt16Array.java
index 06148848..e4cdce51 100644
--- a/src/jdk/nashorn/internal/objects/NativeInt16Array.java
+++ b/src/jdk/nashorn/internal/objects/NativeInt16Array.java
@@ -100,6 +100,11 @@ public final class NativeInt16Array extends ArrayBufferView {
return int.class;
}
+ @Override
+ public Class<?> getBoxedElementType() {
+ return Integer.class;
+ }
+
private int getElem(final int index) {
try {
return nb.get(index);
diff --git a/src/jdk/nashorn/internal/objects/NativeInt32Array.java b/src/jdk/nashorn/internal/objects/NativeInt32Array.java
index 5074dc68..9e664ed7 100644
--- a/src/jdk/nashorn/internal/objects/NativeInt32Array.java
+++ b/src/jdk/nashorn/internal/objects/NativeInt32Array.java
@@ -118,6 +118,11 @@ public final class NativeInt32Array extends ArrayBufferView {
}
@Override
+ public Class<?> getBoxedElementType() {
+ return Integer.class;
+ }
+
+ @Override
public int getInt(final int index) {
return getElem(index);
}
diff --git a/src/jdk/nashorn/internal/objects/NativeInt8Array.java b/src/jdk/nashorn/internal/objects/NativeInt8Array.java
index 319168c0..c336d274 100644
--- a/src/jdk/nashorn/internal/objects/NativeInt8Array.java
+++ b/src/jdk/nashorn/internal/objects/NativeInt8Array.java
@@ -98,6 +98,11 @@ public final class NativeInt8Array extends ArrayBufferView {
return int.class;
}
+ @Override
+ public Class<?> getBoxedElementType() {
+ return Integer.class;
+ }
+
private int getElem(final int index) {
try {
return nb.get(index);
diff --git a/src/jdk/nashorn/internal/objects/NativeObject.java b/src/jdk/nashorn/internal/objects/NativeObject.java
index 5667e6ac..c086442d 100644
--- a/src/jdk/nashorn/internal/objects/NativeObject.java
+++ b/src/jdk/nashorn/internal/objects/NativeObject.java
@@ -673,7 +673,7 @@ public final class NativeObject {
for (final Property prop : properties) {
if (prop.isEnumerable()) {
final Object value = sourceObj.get(prop.getKey());
- prop.setCurrentType(Object.class);
+ prop.setType(Object.class);
prop.setValue(sourceObj, sourceObj, value, false);
propList.add(prop);
}
@@ -805,7 +805,7 @@ public final class NativeObject {
// name and object linked with BeansLinker. (Actually, an even stronger assumption is true: return value is
// constant for any given method name and object's class.)
return MethodHandles.dropArguments(MethodHandles.constant(Object.class,
- Bootstrap.bindDynamicMethod(methodGetter.invoke(source), source)), 0, Object.class);
+ Bootstrap.bindCallable(methodGetter.invoke(source), source, null)), 0, Object.class);
} catch(RuntimeException|Error e) {
throw e;
} catch(final Throwable t) {
diff --git a/src/jdk/nashorn/internal/objects/NativeRegExpExecResult.java b/src/jdk/nashorn/internal/objects/NativeRegExpExecResult.java
index ffd6055b..467399ae 100644
--- a/src/jdk/nashorn/internal/objects/NativeRegExpExecResult.java
+++ b/src/jdk/nashorn/internal/objects/NativeRegExpExecResult.java
@@ -74,7 +74,7 @@ public final class NativeRegExpExecResult extends ScriptObject {
@Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_CONFIGURABLE)
public static Object length(final Object self) {
if (self instanceof ScriptObject) {
- return ((ScriptObject)self).getArray().length() & JSType.MAX_UINT;
+ return JSType.toUint32(((ScriptObject)self).getArray().length());
}
return 0;
diff --git a/src/jdk/nashorn/internal/objects/NativeString.java b/src/jdk/nashorn/internal/objects/NativeString.java
index 850af622..7fb86a4a 100644
--- a/src/jdk/nashorn/internal/objects/NativeString.java
+++ b/src/jdk/nashorn/internal/objects/NativeString.java
@@ -29,6 +29,7 @@ import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.JSType.isRepresentableAsInt;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
+
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
@@ -572,7 +573,7 @@ public final class NativeString extends ScriptObject implements OptimisticBuilti
try {
return ((CharSequence)self).charAt(pos);
} catch (final IndexOutOfBoundsException e) {
- throw new ClassCastException();
+ throw new ClassCastException(); //invalid char, out of bounds, force relink
}
}
@@ -1380,7 +1381,6 @@ public final class NativeString extends ScriptObject implements OptimisticBuilti
* sequence and that we are in range
*/
private static final class CharCodeAtLinkLogic extends SpecializedFunction.LinkLogic {
-
private static final CharCodeAtLinkLogic INSTANCE = new CharCodeAtLinkLogic();
@Override
@@ -1389,7 +1389,7 @@ public final class NativeString extends ScriptObject implements OptimisticBuilti
//check that it's a char sequence or throw cce
final CharSequence cs = (CharSequence)self;
//check that the index, representable as an int, is inside the array
- final int intIndex = JSType.toInteger(request.getArguments()[1]);
+ final int intIndex = JSType.toInteger(request.getArguments()[2]);
return intIndex >= 0 && intIndex < cs.length(); //can link
} catch (final ClassCastException | IndexOutOfBoundsException e) {
//fallthru
diff --git a/src/jdk/nashorn/internal/objects/NativeUint16Array.java b/src/jdk/nashorn/internal/objects/NativeUint16Array.java
index 7f8a1923..2fc2c51a 100644
--- a/src/jdk/nashorn/internal/objects/NativeUint16Array.java
+++ b/src/jdk/nashorn/internal/objects/NativeUint16Array.java
@@ -124,6 +124,11 @@ public final class NativeUint16Array extends ArrayBufferView {
}
@Override
+ public Class<?> getBoxedElementType() {
+ return Integer.class;
+ }
+
+ @Override
public int getInt(final int index) {
return getElem(index);
}
diff --git a/src/jdk/nashorn/internal/objects/NativeUint32Array.java b/src/jdk/nashorn/internal/objects/NativeUint32Array.java
index 4772b977..4c83cc01 100644
--- a/src/jdk/nashorn/internal/objects/NativeUint32Array.java
+++ b/src/jdk/nashorn/internal/objects/NativeUint32Array.java
@@ -105,7 +105,7 @@ public final class NativeUint32Array extends ArrayBufferView {
private long getElem(final int index) {
try {
- return nb.get(index) & JSType.MAX_UINT;
+ return JSType.toUint32(nb.get(index));
} catch (final IndexOutOfBoundsException e) {
throw new ClassCastException(); //force relink - this works for unoptimistic too
}
@@ -133,6 +133,11 @@ public final class NativeUint32Array extends ArrayBufferView {
}
@Override
+ public Class<?> getBoxedElementType() {
+ return Integer.class;
+ }
+
+ @Override
public int getInt(final int index) {
return (int)getLong(index);
}
diff --git a/src/jdk/nashorn/internal/objects/NativeUint8Array.java b/src/jdk/nashorn/internal/objects/NativeUint8Array.java
index be7eb368..6e69ebad 100644
--- a/src/jdk/nashorn/internal/objects/NativeUint8Array.java
+++ b/src/jdk/nashorn/internal/objects/NativeUint8Array.java
@@ -124,6 +124,11 @@ public final class NativeUint8Array extends ArrayBufferView {
}
@Override
+ public Class<?> getBoxedElementType() {
+ return Integer.class;
+ }
+
+ @Override
public int getInt(final int index) {
return getElem(index);
}
diff --git a/src/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java b/src/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java
index 125ac0a2..2dff2612 100644
--- a/src/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java
+++ b/src/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java
@@ -103,6 +103,11 @@ public final class NativeUint8ClampedArray extends ArrayBufferView {
return int.class;
}
+ @Override
+ public Class<?> getBoxedElementType() {
+ return int.class;
+ }
+
private int getElem(final int index) {
try {
return nb.get(index) & 0xff;
diff --git a/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java b/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java
index 36e7716b..f147234b 100644
--- a/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java
+++ b/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java
@@ -30,7 +30,6 @@ import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
import java.lang.invoke.MethodHandle;
import java.util.ArrayList;
-
import jdk.nashorn.internal.runtime.AccessorProperty;
import jdk.nashorn.internal.runtime.GlobalFunctions;
import jdk.nashorn.internal.runtime.Property;
@@ -237,13 +236,13 @@ public class ScriptFunctionImpl extends ScriptFunction {
/**
* Same as {@link ScriptFunction#makeBoundFunction(Object, Object[])}. The only reason we override it is so that we
- * can expose it to methods in this package.
+ * can expose it.
* @param self the self to bind to this function. Can be null (in which case, null is bound as this).
* @param args additional arguments to bind to this function. Can be null or empty to not bind additional arguments.
* @return a function with the specified self and parameters bound.
*/
@Override
- protected ScriptFunction makeBoundFunction(final Object self, final Object[] args) {
+ public ScriptFunction makeBoundFunction(final Object self, final Object[] args) {
return super.makeBoundFunction(self, args);
}
diff --git a/src/jdk/nashorn/internal/objects/annotations/SpecializedFunction.java b/src/jdk/nashorn/internal/objects/annotations/SpecializedFunction.java
index 36f47b27..62e6e99f 100644
--- a/src/jdk/nashorn/internal/objects/annotations/SpecializedFunction.java
+++ b/src/jdk/nashorn/internal/objects/annotations/SpecializedFunction.java
@@ -30,7 +30,6 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.invoke.MethodHandle;
-import java.lang.invoke.SwitchPoint;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.linker.LinkRequest;
import jdk.nashorn.internal.runtime.ScriptFunction;
@@ -62,10 +61,6 @@ public @interface SpecializedFunction {
*/
public static final LinkLogic EMPTY_INSTANCE = new Empty();
- private static final SwitchPoint[] INVALIDATED_SWITCHPOINTS = new SwitchPoint[0];
-
- private SwitchPoint[] modificationSwitchPoints; //cache
-
/** Empty link logic class - allow all linking, no guards */
private static final class Empty extends LinkLogic {
@Override
@@ -167,92 +162,6 @@ public @interface SpecializedFunction {
}
/**
- * Return the modification SwitchPoint of a particular index from this OptimisticBuiltins
- * If none exists, one is created and that one is return.
- *
- * The implementor must map indexes to specific SwitchPoints for specific events and keep
- * track of what they mean, for example NativeArray.LENGTH_NOT_WRITABLE_SWITCHPOINT
- * might be a useful index mapping
- *
- * @param index index for SwitchPoint to get or create
- * @return modification SwitchPoint of particular index for the receiver
- */
- public SwitchPoint getOrCreateModificationSwitchPoint(final int index) {
- return null;
- }
-
- /**
- * Return the modification SwitchPoint from this OptimisticBuiltins. If none
- * exists, one is created and that one is return.
- *
- * @return modification SwitchPoint for the receiver
- */
- public SwitchPoint[] getOrCreateModificationSwitchPoints() {
- return null;
- }
-
- /**
- * Hook to invalidate a modification SwitchPoint by index.
- *
- * @param index index for SwitchPoint to invalidate
- */
- public void invalidateModificationSwitchPoint(final int index) {
- //empty
- }
-
- /**
- * Hook to invalidate all modification SwitchPoints for a receiver
- */
- public void invalidateModificationSwitchPoints() {
- //empty
- }
-
- /**
- * Check whether the receiver has an invalidated modification SwitchPoint.
- *
- * @param index index for the modification SwitchPoint
- * @return true if the particular SwitchPoint at the index is invalidated
- */
- public boolean hasInvalidatedModificationSwitchPoint(final int index) {
- return false;
- }
-
- /**
- * Check whether at least one of the modification SwitchPoints has been
- * invalidated
- * @return true if one of the SwitchPoints has been invalidated
- */
- public boolean hasInvalidatedModificationSwitchPoints() {
- return false;
- }
-
- /**
- * Check whether this OptimisticBuiltins has a SwitchPoints of particular
- * index.
- *
- * As creation overhead for a SwitchPoint is non-zero, we have to create them lazily instead of,
- * e.g. in the constructor of every subclass.
- *
- * @param index index for the modification SwitchPoint
- * @return true if a modification SwitchPoint exists, no matter if it has been invalidated or not
- */
- public boolean hasModificationSwitchPoint(final int index) {
- return false;
- }
-
- /**
- * Check whether this OptimisticBuiltins has SwitchPoints.
- *
- * As creation overhead for a SwitchPoint is non-zero, we have to create them lazily instead of,
- * e.g. in the constructor of every subclass.
- *
- * @return true if a modification SwitchPoint exists, no matter if it has been invalidated or not
- */
- public boolean hasModificationSwitchPoints() {
- return false;
- }
-
- /**
* Check, given a link request and a receiver, if this specialization
* fits This is used by the linker in {@link ScriptFunction} to figure
* out if an optimistic builtin can be linked when first discovered
@@ -265,47 +174,9 @@ public @interface SpecializedFunction {
* pick a non specialized target
*/
public boolean checkLinkable(final Object self, final CallSiteDescriptor desc, final LinkRequest request) {
- // no matter what the modification switchpoints are, if any of them are invalidated,
- // we can't link. Side effect is that if it's the first time we see this callsite,
- // we have to create the SwitchPoint(s) so future modification switchpoint invalidations
- // relink it
- final SwitchPoint[] sps = getOrCreateModificationSwitchPoints(self);
- if (sps == INVALIDATED_SWITCHPOINTS) {
- // nope, can't do the fast link as this assumption
- // has been invalidated already, e.g. length of an
- // array set to not writable
- return false;
- }
- modificationSwitchPoints = sps; //cache
-
// check the link guard, if it says we can link, go ahead
return canLink(self, desc, request);
}
-
- private SwitchPoint[] getOrCreateModificationSwitchPoints(final Object self) {
- final SwitchPoint[] sps = getOrCreateModificationSwitchPoints(); //ask for all my switchpoints
- if (sps != null) { //switchpoint exists, but some may be invalidated
- for (final SwitchPoint sp : sps) {
- if (sp.hasBeenInvalidated()) {
- return INVALIDATED_SWITCHPOINTS;
- }
- }
- }
- return sps;
- }
-
- /**
- * Get the cached modification switchpoints. Only possible to do after a link
- * check call has been performed, one that has answered "true", or you will get the
- * wrong information.
- *
- * Should be used only from {@link ScriptFunction#findCallMethod}
- *
- * @return cached modification switchpoints for this callsite, null if none
- */
- public SwitchPoint[] getModificationSwitchPoints() {
- return modificationSwitchPoints == null ? null : modificationSwitchPoints.clone();
- }
}
/**
diff --git a/src/jdk/nashorn/internal/parser/JSONParser.java b/src/jdk/nashorn/internal/parser/JSONParser.java
index a5cae0ed..7faf89a8 100644
--- a/src/jdk/nashorn/internal/parser/JSONParser.java
+++ b/src/jdk/nashorn/internal/parser/JSONParser.java
@@ -32,7 +32,6 @@ import static jdk.nashorn.internal.parser.TokenType.ESCSTRING;
import static jdk.nashorn.internal.parser.TokenType.RBRACE;
import static jdk.nashorn.internal.parser.TokenType.RBRACKET;
import static jdk.nashorn.internal.parser.TokenType.STRING;
-
import java.util.ArrayList;
import java.util.List;
import jdk.nashorn.internal.ir.Expression;
diff --git a/src/jdk/nashorn/internal/parser/Parser.java b/src/jdk/nashorn/internal/parser/Parser.java
index 3162e184..12ad59e3 100644
--- a/src/jdk/nashorn/internal/parser/Parser.java
+++ b/src/jdk/nashorn/internal/parser/Parser.java
@@ -559,7 +559,7 @@ loop:
// Set up new block. Captures first token.
Block newBlock = newBlock();
try {
- statement();
+ statement(false, false, true);
} finally {
newBlock = restoreBlock(newBlock);
}
@@ -707,20 +707,9 @@ loop:
FunctionNode.Kind.SCRIPT,
functionLine);
- // If ES6 block scope is enabled add a per-script block for top-level LET and CONST declarations.
- final int startLine = start;
- Block outer = useBlockScope() ? newBlock() : null;
functionDeclarations = new ArrayList<>();
-
- try {
- sourceElements(allowPropertyFunction);
- addFunctionDeclarations(script);
- } finally {
- if (outer != null) {
- outer = restoreBlock(outer);
- appendStatement(new BlockStatement(startLine, outer));
- }
- }
+ sourceElements(allowPropertyFunction);
+ addFunctionDeclarations(script);
functionDeclarations = null;
expect(EOF);
@@ -783,7 +772,7 @@ loop:
try {
// Get the next element.
- statement(true, allowPropertyFunction);
+ statement(true, allowPropertyFunction, false);
allowPropertyFunction = false;
// check for directive prologues
@@ -873,13 +862,15 @@ loop:
* Parse any of the basic statement types.
*/
private void statement() {
- statement(false, false);
+ statement(false, false, false);
}
/**
* @param topLevel does this statement occur at the "top level" of a script or a function?
+ * @param allowPropertyFunction allow property "get" and "set" functions?
+ * @param singleStatement are we in a single statement context?
*/
- private void statement(final boolean topLevel, final boolean allowPropertyFunction) {
+ private void statement(final boolean topLevel, final boolean allowPropertyFunction, final boolean singleStatement) {
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.
@@ -943,6 +934,9 @@ loop:
break;
default:
if (useBlockScope() && (type == LET || type == CONST)) {
+ if (singleStatement) {
+ throw error(AbstractParser.message("expected.stmt", type.getName() + " declaration"), token);
+ }
variableStatement(type, true);
break;
}
@@ -1068,7 +1062,7 @@ loop:
next();
final List<VarNode> vars = new ArrayList<>();
- int varFlags = VarNode.IS_STATEMENT;
+ int varFlags = 0;
if (varType == LET) {
varFlags |= VarNode.IS_LET;
} else if (varType == CONST) {
@@ -1221,7 +1215,7 @@ loop:
Block outer = useBlockScope() ? newBlock() : null;
// Create FOR node, capturing FOR token.
- ForNode forNode = new ForNode(line, token, Token.descPosition(token), null, ForNode.IS_FOR);
+ ForNode forNode = new ForNode(line, token, Token.descPosition(token), null, 0);
lc.push(forNode);
try {
@@ -1241,19 +1235,22 @@ loop:
switch (type) {
case VAR:
- // Var statements captured in for outer block.
+ // Var declaration captured in for outer block.
vars = variableStatement(type, false);
break;
case SEMICOLON:
break;
default:
if (useBlockScope() && (type == LET || type == CONST)) {
- // LET/CONST captured in container block created above.
+ if (type == LET) {
+ forNode = forNode.setPerIterationScope(lc);
+ }
+ // LET/CONST declaration captured in container block created above.
vars = variableStatement(type, false);
break;
}
if (env._const_as_var && type == CONST) {
- // Var statements captured in for outer block.
+ // Var declaration captured in for outer block.
vars = variableStatement(TokenType.VAR, false);
break;
}
@@ -1334,11 +1331,12 @@ loop:
appendStatement(forNode);
} finally {
lc.pop(forNode);
- if (outer != null) {
- outer.setFinish(forNode.getFinish());
- outer = restoreBlock(outer);
- appendStatement(new BlockStatement(startLine, outer));
- }
+ }
+
+ if (outer != null) {
+ outer.setFinish(forNode.getFinish());
+ outer = restoreBlock(outer);
+ appendStatement(new BlockStatement(startLine, outer));
}
}
@@ -2646,7 +2644,7 @@ loop:
// name is null, generate anonymous name
boolean isAnonymous = false;
if (name == null) {
- final String tmpName = getDefaultValidFunctionName(functionLine);
+ final String tmpName = getDefaultValidFunctionName(functionLine, isStatement);
name = new IdentNode(functionToken, Token.descPosition(functionToken), tmpName);
isAnonymous = true;
}
@@ -2655,7 +2653,15 @@ loop:
final List<IdentNode> parameters = formalParameterList();
expect(RPAREN);
- FunctionNode functionNode = functionBody(functionToken, name, parameters, FunctionNode.Kind.NORMAL, functionLine);
+ FunctionNode functionNode;
+ // Hide the current default name across function boundaries. E.g. "x3 = function x1() { function() {}}"
+ // If we didn't hide the current default name, then the innermost anonymous function would receive "x3".
+ hideDefaultName();
+ try {
+ functionNode = functionBody(functionToken, name, parameters, FunctionNode.Kind.NORMAL, functionLine);
+ } finally {
+ defaultNames.pop();
+ }
if (isStatement) {
if (topLevel || useBlockScope()) {
@@ -2710,11 +2716,7 @@ loop:
}
if (isStatement) {
- int varFlags = VarNode.IS_STATEMENT;
- if (!topLevel && useBlockScope()) {
- // mark ES6 block functions as lexically scoped
- varFlags |= VarNode.IS_LET;
- }
+ final int varFlags = (topLevel || !useBlockScope()) ? 0 : VarNode.IS_LET;
final VarNode varNode = new VarNode(functionLine, functionToken, finish, name, functionNode, varFlags);
if (topLevel) {
functionDeclarations.add(varNode);
@@ -2728,9 +2730,17 @@ loop:
return functionNode;
}
- private String getDefaultValidFunctionName(final int functionLine) {
+ private String getDefaultValidFunctionName(final int functionLine, final boolean isStatement) {
final String defaultFunctionName = getDefaultFunctionName();
- return isValidIdentifier(defaultFunctionName) ? defaultFunctionName : ANON_FUNCTION_PREFIX.symbolName() + functionLine;
+ if (isValidIdentifier(defaultFunctionName)) {
+ if (isStatement) {
+ // The name will be used as the LHS of a symbol assignment. We add the anonymous function
+ // prefix to ensure that it can't clash with another variable.
+ return ANON_FUNCTION_PREFIX.symbolName() + defaultFunctionName;
+ }
+ return defaultFunctionName;
+ }
+ return ANON_FUNCTION_PREFIX.symbolName() + functionLine;
}
private static boolean isValidIdentifier(final String name) {
@@ -2764,6 +2774,10 @@ loop:
private void markDefaultNameUsed() {
defaultNames.pop();
+ hideDefaultName();
+ }
+
+ private void hideDefaultName() {
// Can be any value as long as getDefaultFunctionName doesn't recognize it as something it can extract a value
// from. Can't be null
defaultNames.push("");
diff --git a/src/jdk/nashorn/internal/runtime/AccessorProperty.java b/src/jdk/nashorn/internal/runtime/AccessorProperty.java
index e2935368..a9afeb93 100644
--- a/src/jdk/nashorn/internal/runtime/AccessorProperty.java
+++ b/src/jdk/nashorn/internal/runtime/AccessorProperty.java
@@ -145,13 +145,6 @@ public class AccessorProperty extends Property {
transient MethodHandle objectSetter;
/**
- * Current type of this object, in object only mode, this is an Object.class. In dual-fields mode
- * null means undefined, and primitive types are allowed. The reason a special type is used for
- * undefined, is that are no bits left to represent it in primitive types
- */
- private Class<?> currentType;
-
- /**
* Delegate constructor for bound properties. This is used for properties created by
* {@link ScriptRuntime#mergeScope} and the Nashorn {@code Object.bindProperties} method.
* The former is used to add a script's defined globals to the current global scope while
@@ -171,7 +164,7 @@ public class AccessorProperty extends Property {
this.objectSetter = bindTo(property.objectSetter, delegate);
property.GETTER_CACHE = new MethodHandle[NOOF_TYPES];
// Properties created this way are bound to a delegate
- setCurrentType(property.getCurrentType());
+ setType(property.getType());
}
/**
@@ -248,7 +241,7 @@ public class AccessorProperty extends Property {
objectGetter = getter.type() != Lookup.GET_OBJECT_TYPE ? MH.asType(getter, Lookup.GET_OBJECT_TYPE) : getter;
objectSetter = setter != null && setter.type() != Lookup.SET_OBJECT_TYPE ? MH.asType(setter, Lookup.SET_OBJECT_TYPE) : setter;
- setCurrentType(OBJECT_FIELDS_ONLY ? Object.class : getterType);
+ setType(OBJECT_FIELDS_ONLY ? Object.class : getterType);
}
/**
@@ -317,7 +310,7 @@ public class AccessorProperty extends Property {
*/
public AccessorProperty(final String key, final int flags, final Class<?> structure, final int slot, final Class<?> initialType) {
this(key, flags, structure, slot);
- setCurrentType(OBJECT_FIELDS_ONLY ? Object.class : initialType);
+ setType(OBJECT_FIELDS_ONLY ? Object.class : initialType);
}
/**
@@ -330,13 +323,13 @@ public class AccessorProperty extends Property {
protected AccessorProperty(final AccessorProperty property, final Class<?> newType) {
super(property, property.getFlags());
- this.GETTER_CACHE = newType != property.getCurrentType() ? new MethodHandle[NOOF_TYPES] : property.GETTER_CACHE;
+ this.GETTER_CACHE = newType != property.getLocalType() ? new MethodHandle[NOOF_TYPES] : property.GETTER_CACHE;
this.primitiveGetter = property.primitiveGetter;
this.primitiveSetter = property.primitiveSetter;
this.objectGetter = property.objectGetter;
this.objectSetter = property.objectSetter;
- setCurrentType(newType);
+ setType(newType);
}
/**
@@ -345,7 +338,7 @@ public class AccessorProperty extends Property {
* @param property source property
*/
protected AccessorProperty(final AccessorProperty property) {
- this(property, property.getCurrentType());
+ this(property, property.getLocalType());
}
/**
@@ -354,7 +347,7 @@ public class AccessorProperty extends Property {
* @param initialValue initial value
*/
protected final void setInitialValue(final ScriptObject owner, final Object initialValue) {
- setCurrentType(JSType.unboxedFieldType(initialValue));
+ setType(JSType.unboxedFieldType(initialValue));
if (initialValue instanceof Integer) {
invokeSetter(owner, ((Integer)initialValue).intValue());
} else if (initialValue instanceof Long) {
@@ -370,7 +363,7 @@ public class AccessorProperty extends Property {
* Initialize the type of a property
*/
protected final void initializeType() {
- setCurrentType(OBJECT_FIELDS_ONLY ? Object.class : null);
+ setType(OBJECT_FIELDS_ONLY ? Object.class : null);
}
private void readObject(final ObjectInputStream s) throws IOException, ClassNotFoundException {
@@ -557,12 +550,12 @@ public class AccessorProperty extends Property {
} else {
getter = debug(
createGetter(
- getCurrentType(),
+ getLocalType(),
type,
primitiveGetter,
objectGetter,
INVALID_PROGRAM_POINT),
- getCurrentType(),
+ getLocalType(),
type,
"get");
getterCache[i] = getter;
@@ -582,18 +575,18 @@ public class AccessorProperty extends Property {
return debug(
createGetter(
- getCurrentType(),
+ getLocalType(),
type,
primitiveGetter,
objectGetter,
programPoint),
- getCurrentType(),
+ getLocalType(),
type,
"get");
}
private MethodHandle getOptimisticPrimitiveGetter(final Class<?> type, final int programPoint) {
- final MethodHandle g = getGetter(getCurrentType());
+ final MethodHandle g = getGetter(getLocalType());
return MH.asType(OptimisticReturnFilters.filterOptimisticReturnValue(g, type, programPoint), g.type().changeReturnType(type));
}
@@ -631,7 +624,7 @@ public class AccessorProperty extends Property {
}
private MethodHandle generateSetter(final Class<?> forType, final Class<?> type) {
- return debug(createSetter(forType, type, primitiveSetter, objectSetter), getCurrentType(), type, "set");
+ return debug(createSetter(forType, type, primitiveSetter, objectSetter), getLocalType(), type, "set");
}
/**
@@ -639,7 +632,7 @@ public class AccessorProperty extends Property {
* @return true if undefined
*/
protected final boolean isUndefined() {
- return getCurrentType() == null;
+ return getLocalType() == null;
}
@Override
@@ -647,7 +640,7 @@ public class AccessorProperty extends Property {
checkUndeclared();
final int typeIndex = getAccessorTypeIndex(type);
- final int currentTypeIndex = getAccessorTypeIndex(getCurrentType());
+ final int currentTypeIndex = getAccessorTypeIndex(getLocalType());
//if we are asking for an object setter, but are still a primitive type, we might try to box it
MethodHandle mh;
@@ -656,13 +649,13 @@ public class AccessorProperty extends Property {
final PropertyMap newMap = getWiderMap(currentMap, newProperty);
final MethodHandle widerSetter = newProperty.getSetter(type, newMap);
- final Class<?> ct = getCurrentType();
+ final Class<?> ct = getLocalType();
mh = MH.filterArguments(widerSetter, 0, MH.insertArguments(debugReplace(ct, type, currentMap, newMap) , 1, newMap));
if (ct != null && ct.isPrimitive() && !type.isPrimitive()) {
mh = ObjectClassGenerator.createGuardBoxedPrimitiveSetter(ct, generateSetter(ct, ct), mh);
}
} else {
- final Class<?> forType = isUndefined() ? type : getCurrentType();
+ final Class<?> forType = isUndefined() ? type : getLocalType();
mh = generateSetter(!forType.isPrimitive() ? Object.class : forType, type);
}
@@ -681,24 +674,13 @@ public class AccessorProperty extends Property {
return false;
}
// Return true for currently undefined even if non-writable/configurable to allow initialization of ES6 CONST.
- return getCurrentType() == null || (getCurrentType() != Object.class && (isConfigurable() || isWritable()));
+ return getLocalType() == null || (getLocalType() != Object.class && (isConfigurable() || isWritable()));
}
private boolean needsInvalidator(final int typeIndex, final int currentTypeIndex) {
return canChangeType() && typeIndex > currentTypeIndex;
}
- @Override
- public final void setCurrentType(final Class<?> currentType) {
- assert currentType != boolean.class : "no boolean storage support yet - fix this";
- this.currentType = currentType == null ? null : currentType.isPrimitive() ? currentType : Object.class;
- }
-
- @Override
- public Class<?> getCurrentType() {
- return currentType;
- }
-
private MethodHandle debug(final MethodHandle mh, final Class<?> forType, final Class<?> type, final String tag) {
if (!Context.DEBUG || !Global.hasInstance()) {
return mh;
diff --git a/src/jdk/nashorn/internal/runtime/CodeStore.java b/src/jdk/nashorn/internal/runtime/CodeStore.java
index 0748ccc3..4e745ff4 100644
--- a/src/jdk/nashorn/internal/runtime/CodeStore.java
+++ b/src/jdk/nashorn/internal/runtime/CodeStore.java
@@ -41,6 +41,7 @@ import java.security.PrivilegedExceptionAction;
import java.util.Iterator;
import java.util.Map;
import java.util.ServiceLoader;
+import jdk.nashorn.internal.codegen.OptimisticTypesPersistence;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.runtime.logging.DebugLogger;
import jdk.nashorn.internal.runtime.logging.Loggable;
@@ -81,10 +82,9 @@ public abstract class CodeStore implements Loggable {
* Returns a new code store instance.
*
* @param context the current context
- * @return The instance
- * @throws IOException If an error occurs
+ * @return The instance, or null if code store could not be created
*/
- public static CodeStore newCodeStore(final Context context) throws IOException {
+ public static CodeStore newCodeStore(final Context context) {
final Class<CodeStore> baseClass = CodeStore.class;
try {
// security check first
@@ -102,9 +102,14 @@ public abstract class CodeStore implements Loggable {
} catch (final AccessControlException e) {
context.getLogger(CodeStore.class).warning("failed to load code store provider ", e);
}
- final CodeStore store = new DirectoryCodeStore();
- store.initLogger(context);
- return store;
+ try {
+ final CodeStore store = new DirectoryCodeStore(context);
+ store.initLogger(context);
+ return store;
+ } catch (final IOException e) {
+ context.getLogger(CodeStore.class).warning("failed to create cache directory ", e);
+ return null;
+ }
}
@@ -210,32 +215,34 @@ public abstract class CodeStore implements Loggable {
/**
* Constructor
*
+ * @param context the current context
* @throws IOException if there are read/write problems with the cache and cache directory
*/
- public DirectoryCodeStore() throws IOException {
- this(Options.getStringProperty("nashorn.persistent.code.cache", "nashorn_code_cache"), false, DEFAULT_MIN_SIZE);
+ public DirectoryCodeStore(final Context context) throws IOException {
+ this(context, Options.getStringProperty("nashorn.persistent.code.cache", "nashorn_code_cache"), false, DEFAULT_MIN_SIZE);
}
/**
* Constructor
*
+ * @param context the current context
* @param path directory to store code in
* @param readOnly is this a read only code store
* @param minSize minimum file size for caching scripts
* @throws IOException if there are read/write problems with the cache and cache directory
*/
- public DirectoryCodeStore(final String path, final boolean readOnly, final int minSize) throws IOException {
- this.dir = checkDirectory(path, readOnly);
+ public DirectoryCodeStore(final Context context, final String path, final boolean readOnly, final int minSize) throws IOException {
+ this.dir = checkDirectory(path, context.getEnv(), readOnly);
this.readOnly = readOnly;
this.minSize = minSize;
}
- private static File checkDirectory(final String path, final boolean readOnly) throws IOException {
+ private static File checkDirectory(final String path, final ScriptEnvironment env, final boolean readOnly) throws IOException {
try {
return AccessController.doPrivileged(new PrivilegedExceptionAction<File>() {
@Override
public File run() throws IOException {
- final File dir = new File(path).getAbsoluteFile();
+ final File dir = new File(path, getVersionDir(env)).getAbsoluteFile();
if (readOnly) {
if (!dir.exists() || !dir.isDirectory()) {
throw new IOException("Not a directory: " + dir.getPath());
@@ -257,6 +264,15 @@ public abstract class CodeStore implements Loggable {
}
}
+ private static String getVersionDir(final ScriptEnvironment env) throws IOException {
+ try {
+ final String versionDir = OptimisticTypesPersistence.getVersionDirName();
+ return env._optimistic_types ? versionDir + "_opt" : versionDir;
+ } catch (final Exception e) {
+ throw new IOException(e);
+ }
+ }
+
@Override
public StoredScript load(final Source source, final String functionKey) {
if (source.getLength() < minSize) {
diff --git a/src/jdk/nashorn/internal/runtime/CompiledFunction.java b/src/jdk/nashorn/internal/runtime/CompiledFunction.java
index b37f94fa..ce20ed31 100644
--- a/src/jdk/nashorn/internal/runtime/CompiledFunction.java
+++ b/src/jdk/nashorn/internal/runtime/CompiledFunction.java
@@ -27,16 +27,17 @@ package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
-
import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.MutableCallSite;
import java.lang.invoke.SwitchPoint;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.function.Supplier;
@@ -727,34 +728,58 @@ final class CompiledFunction {
* @param ipp
* @return string describing the ipp map
*/
- private static String toStringInvalidations(final Map<Integer, Type> ipp) {
+ private static List<String> toStringInvalidations(final Map<Integer, Type> ipp) {
if (ipp == null) {
- return "";
+ return Collections.emptyList();
}
- final StringBuilder sb = new StringBuilder();
+ final List<String> list = new ArrayList<>();
for (final Iterator<Map.Entry<Integer, Type>> iter = ipp.entrySet().iterator(); iter.hasNext(); ) {
final Map.Entry<Integer, Type> entry = iter.next();
final char bct = entry.getValue().getBytecodeStackType();
+ final String type;
+
+ switch (entry.getValue().getBytecodeStackType()) {
+ case 'A':
+ type = "object";
+ break;
+ case 'I':
+ type = "int";
+ break;
+ case 'J':
+ type = "long";
+ break;
+ case 'D':
+ type = "double";
+ break;
+ default:
+ type = String.valueOf(bct);
+ break;
+ }
+ final StringBuilder sb = new StringBuilder();
sb.append('[').
+ append("program point: ").
append(entry.getKey()).
- append("->").
- append(bct == 'A' ? 'O' : bct).
+ append(" -> ").
+ append(type).
append(']');
- if (iter.hasNext()) {
- sb.append(' ');
- }
+ list.add(sb.toString());
}
- return sb.toString();
+ return list;
}
private void logRecompile(final String reason, final FunctionNode fn, final MethodType type, final Map<Integer, Type> ipp) {
if (log.isEnabled()) {
- log.info(reason, DebugLogger.quote(fn.getName()), " signature: ", type, " ", toStringInvalidations(ipp));
+ log.info(reason, DebugLogger.quote(fn.getName()), " signature: ", type);
+ log.indent();
+ for (final String str : toStringInvalidations(ipp)) {
+ log.fine(str);
+ }
+ log.unindent();
}
}
@@ -770,14 +795,21 @@ final class CompiledFunction {
*/
private synchronized MethodHandle handleRewriteException(final OptimismInfo oldOptInfo, final RewriteException re) {
if (log.isEnabled()) {
- log.info(new RecompilationEvent(Level.INFO, re, re.getReturnValueNonDestructive()), "RewriteException ", re.getMessageShort());
+ log.info(
+ new RecompilationEvent(
+ Level.INFO,
+ re,
+ re.getReturnValueNonDestructive()),
+ "caught RewriteException ",
+ re.getMessageShort());
+ log.indent();
}
final MethodType type = type();
// Compiler needs a call site type as its input, which always has a callee parameter, so we must add it if
// this function doesn't have a callee parameter.
- final MethodType callSiteType = type.parameterType(0) == ScriptFunction.class ?
+ final MethodType ct = type.parameterType(0) == ScriptFunction.class ?
type :
type.insertParameterTypes(0, ScriptFunction.class);
final OptimismInfo currentOptInfo = optimismInfo;
@@ -788,43 +820,43 @@ final class CompiledFunction {
final OptimismInfo effectiveOptInfo = currentOptInfo != null ? currentOptInfo : oldOptInfo;
FunctionNode fn = effectiveOptInfo.reparse();
final boolean serialized = effectiveOptInfo.isSerialized();
- final Compiler compiler = effectiveOptInfo.getCompiler(fn, callSiteType, re); //set to non rest-of
+ final Compiler compiler = effectiveOptInfo.getCompiler(fn, ct, re); //set to non rest-of
if (!shouldRecompile) {
// It didn't necessarily recompile, e.g. for an outer invocation of a recursive function if we already
// recompiled a deoptimized version for an inner invocation.
// We still need to do the rest of from the beginning
- logRecompile("Rest-of compilation [STANDALONE] ", fn, callSiteType, effectiveOptInfo.invalidatedProgramPoints);
+ logRecompile("Rest-of compilation [STANDALONE] ", fn, ct, effectiveOptInfo.invalidatedProgramPoints);
return restOfHandle(effectiveOptInfo, compiler.compile(fn, serialized ? CompilationPhases.COMPILE_SERIALIZED_RESTOF : CompilationPhases.COMPILE_ALL_RESTOF), currentOptInfo != null);
}
- logRecompile("Deoptimizing recompilation (up to bytecode) ", fn, callSiteType, effectiveOptInfo.invalidatedProgramPoints);
+ logRecompile("Deoptimizing recompilation (up to bytecode) ", fn, ct, effectiveOptInfo.invalidatedProgramPoints);
fn = compiler.compile(fn, serialized ? CompilationPhases.RECOMPILE_SERIALIZED_UPTO_BYTECODE : CompilationPhases.COMPILE_UPTO_BYTECODE);
- log.info("Reusable IR generated");
+ log.fine("Reusable IR generated");
// compile the rest of the function, and install it
log.info("Generating and installing bytecode from reusable IR...");
- logRecompile("Rest-of compilation [CODE PIPELINE REUSE] ", fn, callSiteType, effectiveOptInfo.invalidatedProgramPoints);
+ logRecompile("Rest-of compilation [CODE PIPELINE REUSE] ", fn, ct, effectiveOptInfo.invalidatedProgramPoints);
final FunctionNode normalFn = compiler.compile(fn, CompilationPhases.GENERATE_BYTECODE_AND_INSTALL);
if (effectiveOptInfo.data.usePersistentCodeCache()) {
final RecompilableScriptFunctionData data = effectiveOptInfo.data;
final int functionNodeId = data.getFunctionNodeId();
- final TypeMap typeMap = data.typeMap(callSiteType);
+ final TypeMap typeMap = data.typeMap(ct);
final Type[] paramTypes = typeMap == null ? null : typeMap.getParameterTypes(functionNodeId);
final String cacheKey = CodeStore.getCacheKey(functionNodeId, paramTypes);
compiler.persistClassInfo(cacheKey, normalFn);
}
- log.info("Done.");
-
final boolean canBeDeoptimized = normalFn.canBeDeoptimized();
if (log.isEnabled()) {
- log.info("Recompiled '", fn.getName(), "' (", Debug.id(this), ") ", canBeDeoptimized ? " can still be deoptimized." : " is completely deoptimized.");
- }
+ log.unindent();
+ log.info("Done.");
- log.info("Looking up invoker...");
+ log.info("Recompiled '", fn.getName(), "' (", Debug.id(this), ") ", canBeDeoptimized ? "can still be deoptimized." : " is completely deoptimized.");
+ log.finest("Looking up invoker...");
+ }
final MethodHandle newInvoker = effectiveOptInfo.data.lookup(fn);
invoker = newInvoker.asType(type.changeReturnType(newInvoker.type().returnType()));
diff --git a/src/jdk/nashorn/internal/runtime/Context.java b/src/jdk/nashorn/internal/runtime/Context.java
index 23f0e3ab..9e851998 100644
--- a/src/jdk/nashorn/internal/runtime/Context.java
+++ b/src/jdk/nashorn/internal/runtime/Context.java
@@ -33,6 +33,7 @@ import static jdk.nashorn.internal.runtime.CodeStore.newCodeStore;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
import static jdk.nashorn.internal.runtime.Source.sourceFor;
+
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
@@ -60,6 +61,7 @@ import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.logging.Level;
@@ -262,6 +264,10 @@ public final class Context {
// persistent code store
private CodeStore codeStore;
+ // A factory for linking global properties as constant method handles. It is created when the first Global
+ // is created, and invalidated forever once the second global is created.
+ private final AtomicReference<GlobalConstants> globalConstantsRef = new AtomicReference<>();
+
/**
* Get the current global scope
* @return the current global scope
@@ -293,7 +299,10 @@ public final class Context {
assert getGlobal() != global;
//same code can be cached between globals, then we need to invalidate method handle constants
if (global != null) {
- Global.getConstants().invalidateAll();
+ final GlobalConstants globalConstants = getContext(global).getGlobalConstants();
+ if (globalConstants != null) {
+ globalConstants.invalidateAll();
+ }
}
currentGlobal.set(global);
}
@@ -500,11 +509,7 @@ public final class Context {
}
if (env._persistent_cache) {
- try {
- codeStore = newCodeStore(this);
- } catch (final IOException e) {
- throw new RuntimeException("Error initializing code cache", e);
- }
+ codeStore = newCodeStore(this);
}
// print version info if asked.
@@ -529,6 +534,15 @@ public final class Context {
}
/**
+ * Returns the factory for constant method handles for global properties. The returned factory can be
+ * invalidated if this Context has more than one Global.
+ * @return the factory for constant method handles for global properties.
+ */
+ GlobalConstants getGlobalConstants() {
+ return globalConstantsRef.get();
+ }
+
+ /**
* Get the error manager for this context
* @return error manger
*/
@@ -1016,9 +1030,32 @@ public final class Context {
* @return the global script object
*/
public Global newGlobal() {
+ createOrInvalidateGlobalConstants();
return new Global(this);
}
+ private void createOrInvalidateGlobalConstants() {
+ for (;;) {
+ final GlobalConstants currentGlobalConstants = getGlobalConstants();
+ if (currentGlobalConstants != null) {
+ // Subsequent invocation; we're creating our second or later Global. GlobalConstants is not safe to use
+ // with more than one Global, as the constant method handle linkages it creates create a coupling
+ // between the Global and the call sites in the compiled code.
+ currentGlobalConstants.invalidateForever();
+ return;
+ }
+ final GlobalConstants newGlobalConstants = new GlobalConstants(getLogger(GlobalConstants.class));
+ if (globalConstantsRef.compareAndSet(null, newGlobalConstants)) {
+ // First invocation; we're creating the first Global in this Context. Create the GlobalConstants object
+ // for this Context.
+ return;
+ }
+
+ // If we reach here, then we started out as the first invocation, but another concurrent invocation won the
+ // CAS race. We'll just let the loop repeat and invalidate the CAS race winner.
+ }
+ }
+
/**
* Initialize given global scope object.
*
@@ -1057,12 +1094,19 @@ public final class Context {
* @return current global's context
*/
static Context getContextTrusted() {
- return ((ScriptObject)Context.getGlobal()).getContext();
+ return getContext(getGlobal());
}
static Context getContextTrustedOrNull() {
final Global global = Context.getGlobal();
- return global == null ? null : ((ScriptObject)global).getContext();
+ return global == null ? null : getContext(global);
+ }
+
+ private static Context getContext(final Global global) {
+ // We can't invoke Global.getContext() directly, as it's a protected override, and Global isn't in our package.
+ // In order to access the method, we must cast it to ScriptObject first (which is in our package) and then let
+ // virtual invocation do its thing.
+ return ((ScriptObject)global).getContext();
}
/**
@@ -1150,10 +1194,9 @@ public final class Context {
StoredScript storedScript = null;
FunctionNode functionNode = null;
- // We only use the code store here if optimistic types are disabled. With optimistic types,
- // code is stored per function in RecompilableScriptFunctionData.
- // TODO: This should really be triggered by lazy compilation, not optimistic types.
- final boolean useCodeStore = env._persistent_cache && !env._parse_only && !env._optimistic_types;
+ // We only use the code store here if optimistic types are disabled. With optimistic types, initial compilation
+ // just creates a thin wrapper, and actual code is stored per function in RecompilableScriptFunctionData.
+ final boolean useCodeStore = codeStore != null && !env._parse_only && !env._optimistic_types;
final String cacheKey = useCodeStore ? CodeStore.getCacheKey(0, null) : null;
if (useCodeStore) {
diff --git a/src/jdk/nashorn/internal/runtime/ECMAException.java b/src/jdk/nashorn/internal/runtime/ECMAException.java
index 954a1707..f906e18f 100644
--- a/src/jdk/nashorn/internal/runtime/ECMAException.java
+++ b/src/jdk/nashorn/internal/runtime/ECMAException.java
@@ -96,15 +96,17 @@ public final class ECMAException extends NashornException {
// If thrown object is an Error or sub-object like TypeError, then
// an ECMAException object has been already initialized at constructor.
if (thrown instanceof ScriptObject) {
- final ScriptObject sobj = (ScriptObject)thrown;
- final Object exception = getException(sobj);
+ final Object exception = getException((ScriptObject)thrown);
if (exception instanceof ECMAException) {
- // copy over file name, line number and column number.
final ECMAException ee = (ECMAException)exception;
- ee.setFileName(fileName);
- ee.setLineNumber(line);
- ee.setColumnNumber(column);
- return ee;
+ // Make sure exception has correct thrown reference because that's what will end up getting caught.
+ if (ee.getThrown() == thrown) {
+ // copy over file name, line number and column number.
+ ee.setFileName(fileName);
+ ee.setLineNumber(line);
+ ee.setColumnNumber(column);
+ return ee;
+ }
}
}
@@ -154,7 +156,11 @@ public final class ECMAException extends NashornException {
* @return a {@link ECMAException}
*/
public static Object getException(final ScriptObject errObj) {
- return errObj.get(ECMAException.EXCEPTION_PROPERTY);
+ // Exclude inherited properties that may belong to errors in the prototype chain.
+ if (errObj.hasOwnProperty(ECMAException.EXCEPTION_PROPERTY)) {
+ return errObj.get(ECMAException.EXCEPTION_PROPERTY);
+ }
+ return null;
}
/**
diff --git a/src/jdk/nashorn/internal/runtime/FindProperty.java b/src/jdk/nashorn/internal/runtime/FindProperty.java
index b4e00124..3b153c58 100644
--- a/src/jdk/nashorn/internal/runtime/FindProperty.java
+++ b/src/jdk/nashorn/internal/runtime/FindProperty.java
@@ -84,13 +84,18 @@ public final class FindProperty {
* @return method handle for the getter
*/
public MethodHandle getGetter(final Class<?> type, final int programPoint, final LinkRequest request) {
- final MethodHandle getter;
+ MethodHandle getter;
if (isValid(programPoint)) {
getter = property.getOptimisticGetter(type, programPoint);
} else {
getter = property.getGetter(type);
}
if (property instanceof UserAccessorProperty) {
+ getter = MH.insertArguments(getter, 1, UserAccessorProperty.getINVOKE_UA_GETTER(type, programPoint));
+ if (isValid(programPoint) && type.isPrimitive()) {
+ getter = MH.insertArguments(getter, 1, programPoint);
+ }
+ property.setType(type);
return insertAccessorsGetter((UserAccessorProperty) property, request, getter);
}
return getter;
@@ -111,7 +116,8 @@ public final class FindProperty {
public MethodHandle getSetter(final Class<?> type, final boolean strict, final LinkRequest request) {
MethodHandle setter = property.getSetter(type, getOwner().getMap());
if (property instanceof UserAccessorProperty) {
- setter = MH.insertArguments(setter, 1, strict ? property.getKey() : null);
+ setter = MH.insertArguments(setter, 1, UserAccessorProperty.getINVOKE_UA_SETTER(type), strict ? property.getKey() : null);
+ property.setType(type);
return insertAccessorsGetter((UserAccessorProperty) property, request, setter);
}
diff --git a/src/jdk/nashorn/internal/runtime/GlobalConstants.java b/src/jdk/nashorn/internal/runtime/GlobalConstants.java
index c6f9b964..cd1061c2 100644
--- a/src/jdk/nashorn/internal/runtime/GlobalConstants.java
+++ b/src/jdk/nashorn/internal/runtime/GlobalConstants.java
@@ -31,12 +31,14 @@ import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.getProgramPoint;
import static jdk.nashorn.internal.runtime.logging.DebugLogger.quote;
+
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.SwitchPoint;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.DynamicLinker;
@@ -50,7 +52,7 @@ import jdk.nashorn.internal.runtime.logging.Loggable;
import jdk.nashorn.internal.runtime.logging.Logger;
/**
- * Each global owns one of these. This is basically table of accessors
+ * Each context owns one of these. This is basically table of accessors
* for global properties. A global constant is evaluated to a MethodHandle.constant
* for faster access and to avoid walking to proto chain looking for it.
*
@@ -67,12 +69,19 @@ import jdk.nashorn.internal.runtime.logging.Logger;
* reregister the switchpoint. Set twice or more - don't try again forever, or we'd
* just end up relinking our way into megamorphisism.
*
+ * Also it has to be noted that this kind of linking creates a coupling between a Global
+ * and the call sites in compiled code belonging to the Context. For this reason, the
+ * linkage becomes incorrect as soon as the Context has more than one Global. The
+ * {@link #invalidateForever()} is invoked by the Context to invalidate all linkages and
+ * turn off the functionality of this object as soon as the Context's {@link Context#newGlobal()} is invoked
+ * for second time.
+ *
* We can extend this to ScriptObjects in general (GLOBAL_ONLY=false), which requires
* a receiver guard on the constant getter, but it currently leaks memory and its benefits
* have not yet been investigated property.
*
- * As long as all Globals share the same constant instance, we need synchronization
- * whenever we access the instance.
+ * As long as all Globals in a Context share the same GlobalConstants instance, we need synchronization
+ * whenever we access it.
*/
@Logger(name="const")
public final class GlobalConstants implements Loggable {
@@ -82,7 +91,7 @@ public final class GlobalConstants implements Loggable {
* Script objects require a receiver guard, which is memory intensive, so this is currently
* disabled. We might implement a weak reference based approach to this later.
*/
- private static final boolean GLOBAL_ONLY = true;
+ public static final boolean GLOBAL_ONLY = true;
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
@@ -98,6 +107,8 @@ public final class GlobalConstants implements Loggable {
*/
private final Map<String, Access> map = new HashMap<>();
+ private final AtomicBoolean invalidatedForever = new AtomicBoolean(false);
+
/**
* Constructor - used only by global
* @param log logger, or null if none
@@ -216,10 +227,34 @@ public final class GlobalConstants implements Loggable {
* the same class for a new global, but the builtins and global scoped variables
* will have changed.
*/
- public synchronized void invalidateAll() {
- log.info("New global created - invalidating all constant callsites without increasing invocation count.");
- for (final Access acc : map.values()) {
- acc.invalidateUncounted();
+ public void invalidateAll() {
+ if (!invalidatedForever.get()) {
+ log.info("New global created - invalidating all constant callsites without increasing invocation count.");
+ synchronized (this) {
+ for (final Access acc : map.values()) {
+ acc.invalidateUncounted();
+ }
+ }
+ }
+ }
+
+ /**
+ * To avoid an expensive global guard "is this the same global", similar to the
+ * receiver guard on the ScriptObject level, we invalidate all getters when the
+ * second Global is created by the Context owning this instance. After this
+ * method is invoked, this GlobalConstants instance will both invalidate all the
+ * switch points it produced, and it will stop handing out new method handles
+ * altogether.
+ */
+ public void invalidateForever() {
+ if (invalidatedForever.compareAndSet(false, true)) {
+ log.info("New global created - invalidating all constant callsites.");
+ synchronized (this) {
+ for (final Access acc : map.values()) {
+ acc.invalidateForever();
+ }
+ map.clear();
+ }
}
}
@@ -251,7 +286,7 @@ public final class GlobalConstants implements Loggable {
return obj;
}
- private synchronized Access getOrCreateSwitchPoint(final String name) {
+ private Access getOrCreateSwitchPoint(final String name) {
Access acc = map.get(name);
if (acc != null) {
return acc;
@@ -267,9 +302,13 @@ public final class GlobalConstants implements Loggable {
* @param name name of property
*/
void delete(final String name) {
- final Access acc = map.get(name);
- if (acc != null) {
- acc.invalidateForever();
+ if (!invalidatedForever.get()) {
+ synchronized (this) {
+ final Access acc = map.get(name);
+ if (acc != null) {
+ acc.invalidateForever();
+ }
+ }
}
}
@@ -313,45 +352,45 @@ public final class GlobalConstants implements Loggable {
*
* @return null if failed to set up constant linkage
*/
- synchronized GuardedInvocation findSetMethod(final FindProperty find, final ScriptObject receiver, final GuardedInvocation inv, final CallSiteDescriptor desc, final LinkRequest request) {
- if (GLOBAL_ONLY && !isGlobalSetter(receiver, find)) {
+ GuardedInvocation findSetMethod(final FindProperty find, final ScriptObject receiver, final GuardedInvocation inv, final CallSiteDescriptor desc, final LinkRequest request) {
+ if (invalidatedForever.get() || (GLOBAL_ONLY && !isGlobalSetter(receiver, find))) {
return null;
}
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
- final Access acc = getOrCreateSwitchPoint(name);
-
- if (log.isEnabled()) {
- log.fine("Trying to link constant SETTER ", acc);
- }
+ synchronized (this) {
+ final Access acc = getOrCreateSwitchPoint(name);
- if (!acc.mayRetry()) {
if (log.isEnabled()) {
- log.fine("*** SET: Giving up on " + quote(name) + " - retry count has exceeded " + DynamicLinker.getLinkedCallSiteLocation());
+ log.fine("Trying to link constant SETTER ", acc);
}
- return null;
- }
- assert acc.mayRetry();
+ if (!acc.mayRetry() || invalidatedForever.get()) {
+ if (log.isEnabled()) {
+ log.fine("*** SET: Giving up on " + quote(name) + " - retry count has exceeded " + DynamicLinker.getLinkedCallSiteLocation());
+ }
+ return null;
+ }
- if (acc.hasBeenInvalidated()) {
- log.info("New chance for " + acc);
- acc.newSwitchPoint();
- }
+ if (acc.hasBeenInvalidated()) {
+ log.info("New chance for " + acc);
+ acc.newSwitchPoint();
+ }
- assert !acc.hasBeenInvalidated();
+ assert !acc.hasBeenInvalidated();
- // if we haven't given up on this symbol, add a switchpoint invalidation filter to the receiver parameter
- final MethodHandle target = inv.getInvocation();
- final Class<?> receiverType = target.type().parameterType(0);
- final MethodHandle boundInvalidator = MH.bindTo(INVALIDATE_SP, this);
- final MethodHandle invalidator = MH.asType(boundInvalidator, boundInvalidator.type().changeParameterType(0, receiverType).changeReturnType(receiverType));
- final MethodHandle mh = MH.filterArguments(inv.getInvocation(), 0, MH.insertArguments(invalidator, 1, acc));
+ // if we haven't given up on this symbol, add a switchpoint invalidation filter to the receiver parameter
+ final MethodHandle target = inv.getInvocation();
+ final Class<?> receiverType = target.type().parameterType(0);
+ final MethodHandle boundInvalidator = MH.bindTo(INVALIDATE_SP, this);
+ final MethodHandle invalidator = MH.asType(boundInvalidator, boundInvalidator.type().changeParameterType(0, receiverType).changeReturnType(receiverType));
+ final MethodHandle mh = MH.filterArguments(inv.getInvocation(), 0, MH.insertArguments(invalidator, 1, acc));
- assert inv.getSwitchPoints() == null : Arrays.asList(inv.getSwitchPoints());
- log.info("Linked setter " + quote(name) + " " + acc.getSwitchPoint());
- return new GuardedInvocation(mh, inv.getGuard(), acc.getSwitchPoint(), inv.getException());
+ assert inv.getSwitchPoints() == null : Arrays.asList(inv.getSwitchPoints());
+ log.info("Linked setter " + quote(name) + " " + acc.getSwitchPoint());
+ return new GuardedInvocation(mh, inv.getGuard(), acc.getSwitchPoint(), inv.getException());
+ }
}
/**
@@ -380,11 +419,11 @@ public final class GlobalConstants implements Loggable {
*
* @return resulting getter, or null if failed to create constant
*/
- synchronized GuardedInvocation findGetMethod(final FindProperty find, final ScriptObject receiver, final CallSiteDescriptor desc) {
+ GuardedInvocation findGetMethod(final FindProperty find, final ScriptObject receiver, final CallSiteDescriptor desc) {
// Only use constant getter for fast scope access, because the receiver may change between invocations
// for slow-scope and non-scope callsites.
// Also return null for user accessor properties as they may have side effects.
- if (!NashornCallSiteDescriptor.isFastScope(desc)
+ if (invalidatedForever.get() || !NashornCallSiteDescriptor.isFastScope(desc)
|| (GLOBAL_ONLY && !find.getOwner().isGlobal())
|| find.getProperty() instanceof UserAccessorProperty) {
return null;
@@ -395,51 +434,53 @@ public final class GlobalConstants implements Loggable {
final Class<?> retType = desc.getMethodType().returnType();
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
- final Access acc = getOrCreateSwitchPoint(name);
-
- log.fine("Starting to look up object value " + name);
- final Object c = find.getObjectValue();
+ synchronized (this) {
+ final Access acc = getOrCreateSwitchPoint(name);
- if (log.isEnabled()) {
- log.fine("Trying to link constant GETTER " + acc + " value = " + c);
- }
+ log.fine("Starting to look up object value " + name);
+ final Object c = find.getObjectValue();
- if (acc.hasBeenInvalidated() || acc.guardFailed()) {
if (log.isEnabled()) {
- log.info("*** GET: Giving up on " + quote(name) + " - retry count has exceeded " + DynamicLinker.getLinkedCallSiteLocation());
+ log.fine("Trying to link constant GETTER " + acc + " value = " + c);
}
- return null;
- }
- final MethodHandle cmh = constantGetter(c);
+ if (acc.hasBeenInvalidated() || acc.guardFailed() || invalidatedForever.get()) {
+ if (log.isEnabled()) {
+ log.info("*** GET: Giving up on " + quote(name) + " - retry count has exceeded " + DynamicLinker.getLinkedCallSiteLocation());
+ }
+ return null;
+ }
- MethodHandle mh;
- MethodHandle guard;
+ final MethodHandle cmh = constantGetter(c);
- if (isOptimistic) {
- if (JSType.getAccessorTypeIndex(cmh.type().returnType()) <= JSType.getAccessorTypeIndex(retType)) {
- //widen return type - this is pessimistic, so it will always work
- mh = MH.asType(cmh, cmh.type().changeReturnType(retType));
+ MethodHandle mh;
+ MethodHandle guard;
+
+ if (isOptimistic) {
+ if (JSType.getAccessorTypeIndex(cmh.type().returnType()) <= JSType.getAccessorTypeIndex(retType)) {
+ //widen return type - this is pessimistic, so it will always work
+ mh = MH.asType(cmh, cmh.type().changeReturnType(retType));
+ } else {
+ //immediately invalidate - we asked for a too wide constant as a narrower one
+ mh = MH.dropArguments(MH.insertArguments(JSType.THROW_UNWARRANTED.methodHandle(), 0, c, programPoint), 0, Object.class);
+ }
} else {
- //immediately invalidate - we asked for a too wide constant as a narrower one
- mh = MH.dropArguments(MH.insertArguments(JSType.THROW_UNWARRANTED.methodHandle(), 0, c, programPoint), 0, Object.class);
+ //pessimistic return type filter
+ mh = Lookup.filterReturnType(cmh, retType);
}
- } else {
- //pessimistic return type filter
- mh = Lookup.filterReturnType(cmh, retType);
- }
- if (find.getOwner().isGlobal()) {
- guard = null;
- } else {
- guard = MH.insertArguments(RECEIVER_GUARD, 0, acc, receiver);
- }
+ if (find.getOwner().isGlobal()) {
+ guard = null;
+ } else {
+ guard = MH.insertArguments(RECEIVER_GUARD, 0, acc, receiver);
+ }
- if (log.isEnabled()) {
- log.info("Linked getter " + quote(name) + " as MethodHandle.constant() -> " + c + " " + acc.getSwitchPoint());
- mh = MethodHandleFactory.addDebugPrintout(log, Level.FINE, mh, "get const " + acc);
- }
+ if (log.isEnabled()) {
+ log.info("Linked getter " + quote(name) + " as MethodHandle.constant() -> " + c + " " + acc.getSwitchPoint());
+ mh = MethodHandleFactory.addDebugPrintout(log, Level.FINE, mh, "get const " + acc);
+ }
- return new GuardedInvocation(mh, guard, acc.getSwitchPoint(), null);
+ return new GuardedInvocation(mh, guard, acc.getSwitchPoint(), null);
+ }
}
}
diff --git a/src/jdk/nashorn/internal/runtime/JSType.java b/src/jdk/nashorn/internal/runtime/JSType.java
index b040b588..7e54b1e2 100644
--- a/src/jdk/nashorn/internal/runtime/JSType.java
+++ b/src/jdk/nashorn/internal/runtime/JSType.java
@@ -29,7 +29,6 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY;
import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
-
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Array;
@@ -115,6 +114,9 @@ public enum JSType {
/** JavaScript compliant conversion function from double to int32 */
public static final Call TO_INT32_D = staticCall(JSTYPE_LOOKUP, JSType.class, "toInt32", int.class, double.class);
+ /** JavaScript compliant conversion function from int to uint32 */
+ public static final Call TO_UINT32_I = staticCall(JSTYPE_LOOKUP, JSType.class, "toUint32", long.class, int.class);
+
/** JavaScript compliant conversion function from Object to uint32 */
public static final Call TO_UINT32 = staticCall(JSTYPE_LOOKUP, JSType.class, "toUint32", long.class, Object.class);
@@ -148,6 +150,12 @@ public enum JSType {
/** Div exact wrapper for potentially integer division that turns into float point */
public static final Call DIV_EXACT = staticCall(JSTYPE_LOOKUP, JSType.class, "divExact", int.class, int.class, int.class, int.class);
+ /** Div zero wrapper for integer division that handles (0/0)|0 == 0 */
+ public static final Call DIV_ZERO = staticCall(JSTYPE_LOOKUP, JSType.class, "divZero", int.class, int.class, int.class);
+
+ /** Mod zero wrapper for integer division that handles (0%0)|0 == 0 */
+ public static final Call REM_ZERO = staticCall(JSTYPE_LOOKUP, JSType.class, "remZero", int.class, int.class, int.class);
+
/** Mod exact wrapper for potentially integer remainders that turns into float point */
public static final Call REM_EXACT = staticCall(JSTYPE_LOOKUP, JSType.class, "remExact", int.class, int.class, int.class, int.class);
@@ -172,6 +180,12 @@ public enum JSType {
/** Div exact wrapper for potentially integer division that turns into float point */
public static final Call DIV_EXACT_LONG = staticCall(JSTYPE_LOOKUP, JSType.class, "divExact", long.class, long.class, long.class, int.class);
+ /** Div zero wrapper for long division that handles (0/0) >>> 0 == 0 */
+ public static final Call DIV_ZERO_LONG = staticCall(JSTYPE_LOOKUP, JSType.class, "divZero", long.class, long.class, long.class);
+
+ /** Mod zero wrapper for long division that handles (0%0) >>> 0 == 0 */
+ public static final Call REM_ZERO_LONG = staticCall(JSTYPE_LOOKUP, JSType.class, "remZero", long.class, long.class, long.class);
+
/** Mod exact wrapper for potentially integer remainders that turns into float point */
public static final Call REM_EXACT_LONG = staticCall(JSTYPE_LOOKUP, JSType.class, "remExact", long.class, long.class, long.class, int.class);
@@ -700,6 +714,9 @@ public enum JSType {
* @return a number
*/
public static double toNumber(final Object obj) {
+ if (obj instanceof Double) {
+ return (Double)obj;
+ }
if (obj instanceof Number) {
return ((Number)obj).doubleValue();
}
@@ -1002,6 +1019,16 @@ public enum JSType {
}
/**
+ * JavaScript compliant int to uint32 conversion
+ *
+ * @param num an int
+ * @return a uint32
+ */
+ public static long toUint32(final int num) {
+ return num & MAX_UINT;
+ }
+
+ /**
* JavaScript compliant Object to uint16 conversion
* ECMA 9.7 ToUint16: (Unsigned 16 Bit Integer)
*
@@ -1474,6 +1501,28 @@ public enum JSType {
}
/**
+ * Implements int division but allows {@code x / 0} to be represented as 0. Basically equivalent to
+ * {@code (x / y)|0} JavaScript expression (division of two ints coerced to int).
+ * @param x the dividend
+ * @param y the divisor
+ * @return the result
+ */
+ public static int divZero(final int x, final int y) {
+ return y == 0 ? 0 : x / y;
+ }
+
+ /**
+ * Implements int remainder but allows {@code x % 0} to be represented as 0. Basically equivalent to
+ * {@code (x % y)|0} JavaScript expression (remainder of two ints coerced to int).
+ * @param x the dividend
+ * @param y the divisor
+ * @return the remainder
+ */
+ public static int remZero(final int x, final int y) {
+ return y == 0 ? 0 : x % y;
+ }
+
+ /**
* Wrapper for modExact. Throws UnwarrantedOptimismException if the modulo can't be represented as int.
*
* @param x first term
@@ -1517,6 +1566,28 @@ public enum JSType {
}
/**
+ * Implements long division but allows {@code x / 0} to be represented as 0. Useful when division of two longs
+ * is coerced to long.
+ * @param x the dividend
+ * @param y the divisor
+ * @return the result
+ */
+ public static long divZero(final long x, final long y) {
+ return y == 0L ? 0L : x / y;
+ }
+
+ /**
+ * Implements long remainder but allows {@code x % 0} to be represented as 0. Useful when remainder of two longs
+ * is coerced to long.
+ * @param x the dividend
+ * @param y the divisor
+ * @return the remainder
+ */
+ public static long remZero(final long x, final long y) {
+ return y == 0L ? 0L : x % y;
+ }
+
+ /**
* Wrapper for modExact. Throws UnwarrantedOptimismException if the modulo can't be represented as int.
*
* @param x first term
@@ -1776,6 +1847,23 @@ public enum JSType {
}
/**
+ * Returns the boxed version of a primitive class
+ * @param clazz the class
+ * @return the boxed type of clazz, or unchanged if not primitive
+ */
+ public static Class<?> getBoxedClass(final Class<?> clazz) {
+ if (clazz == int.class) {
+ return Integer.class;
+ } else if (clazz == long.class) {
+ return Long.class;
+ } else if (clazz == double.class) {
+ return Double.class;
+ }
+ assert !clazz.isPrimitive();
+ return clazz;
+ }
+
+ /**
* Create a method handle constant of the correct primitive type
* for a constant object
* @param o object
diff --git a/src/jdk/nashorn/internal/runtime/Property.java b/src/jdk/nashorn/internal/runtime/Property.java
index f57246ca..4225c251 100644
--- a/src/jdk/nashorn/internal/runtime/Property.java
+++ b/src/jdk/nashorn/internal/runtime/Property.java
@@ -84,14 +84,17 @@ public abstract class Property implements Serializable {
public static final int IS_NASGEN_PRIMITIVE = 1 << 6;
/** Is this a builtin property, e.g. Function.prototype.apply */
- public static final int IS_BUILTIN = 1 << 7;
+ public static final int IS_BUILTIN = 1 << 7;
/** Is this property bound to a receiver? This means get/set operations will be delegated to
* a statically defined object instead of the object passed as callsite parameter. */
- public static final int IS_BOUND = 1 << 7;
+ public static final int IS_BOUND = 1 << 8;
/** Is this a lexically scoped LET or CONST variable that is dead until it is declared. */
- public static final int NEEDS_DECLARATION = 1 << 8;
+ public static final int NEEDS_DECLARATION = 1 << 9;
+
+ /** Is this property an ES6 lexical binding? */
+ public static final int IS_LEXICAL_BINDING = 1 << 10;
/** Property key. */
private final String key;
@@ -102,6 +105,13 @@ public abstract class Property implements Serializable {
/** Property field number or spill slot. */
private final int slot;
+ /**
+ * Current type of this object, in object only mode, this is an Object.class. In dual-fields mode
+ * null means undefined, and primitive types are allowed. The reason a special type is used for
+ * undefined, is that are no bits left to represent it in primitive types
+ */
+ private Class<?> type;
+
/** SwitchPoint that is invalidated when property is changed, optional */
protected transient SwitchPoint builtinSwitchPoint;
@@ -536,7 +546,7 @@ public abstract class Property implements Serializable {
* <p>
* see {@link ObjectClassGenerator#createSetter(Class, Class, MethodHandle, MethodHandle)}
* if you are interested in the internal details of this. Note that if you
- * are running in default mode, with {@code -Dnashorn.fields.dual=true}, disabled, the setters
+ * are running with {@code -Dnashorn.fields.objects=true}, the setters
* will currently never change, as all properties are represented as Object field,
* the Object fields are Initialized to {@code ScriptRuntime.UNDEFINED} and primitives are
* boxed/unboxed upon every access, which is not necessarily optimal
@@ -569,7 +579,7 @@ public abstract class Property implements Serializable {
@Override
public int hashCode() {
- final Class<?> type = getCurrentType();
+ final Class<?> type = getLocalType();
return Objects.hashCode(this.key) ^ flags ^ getSlot() ^ (type == null ? 0 : type.hashCode());
}
@@ -586,7 +596,7 @@ public abstract class Property implements Serializable {
final Property otherProperty = (Property)other;
return equalsWithoutType(otherProperty) &&
- getCurrentType() == otherProperty.getCurrentType();
+ getLocalType() == otherProperty.getLocalType();
}
boolean equalsWithoutType(final Property otherProperty) {
@@ -615,7 +625,7 @@ public abstract class Property implements Serializable {
*/
public final String toStringShort() {
final StringBuilder sb = new StringBuilder();
- final Class<?> type = getCurrentType();
+ final Class<?> type = getLocalType();
sb.append(getKey()).append(" (").append(type(type)).append(')');
return sb.toString();
}
@@ -632,7 +642,7 @@ public abstract class Property implements Serializable {
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
- final Class<?> type = getCurrentType();
+ final Class<?> type = getLocalType();
sb.append(indent(getKey(), 20)).
append(" id=").
@@ -656,20 +666,40 @@ public abstract class Property implements Serializable {
}
/**
- * Get the current type of this field. If you are not running with dual fields enabled,
+ * Get the current type of this property. If you are running with object fields enabled,
* this will always be Object.class. See the value representation explanation in
* {@link Property#getSetter(Class, PropertyMap)} and {@link ObjectClassGenerator}
* for more information.
*
+ * <p>Note that for user accessor properties, this returns the type of the last observed
+ * value passed to or returned by a user accessor. Use {@link #getLocalType()} to always get
+ * the type of the actual value stored in the property slot.</p>
+ *
* @return current type of property, null means undefined
*/
- public abstract Class<?> getCurrentType();
+ public final Class<?> getType() {
+ return type;
+ }
+
+ /**
+ * Set the type of this property.
+ * @param type new type
+ */
+ public final void setType(final Class<?> type) {
+ assert type != boolean.class : "no boolean storage support yet - fix this";
+ this.type = type == null ? null : type.isPrimitive() ? type : Object.class;
+ }
/**
- * Reset the current type of this property
- * @param currentType new current type
+ * Get the type of the value in the local property slot. This returns the same as
+ * {@link #getType()} for normal properties, but always returns {@code Object.class}
+ * for {@link UserAccessorProperty}s as their local type is a pair of accessor references.
+ *
+ * @return the local property type
*/
- public abstract void setCurrentType(final Class<?> currentType);
+ protected Class<?> getLocalType() {
+ return getType();
+ }
/**
* Check whether this Property can ever change its type. The default is false, and if
@@ -687,4 +717,12 @@ public abstract class Property implements Serializable {
public boolean isFunctionDeclaration() {
return (flags & IS_FUNCTION_DECLARATION) == IS_FUNCTION_DECLARATION;
}
+
+ /**
+ * Is this a property defined by ES6 let or const?
+ * @return true if this property represents a lexical binding.
+ */
+ public boolean isLexicalBinding() {
+ return (flags & IS_LEXICAL_BINDING) == IS_LEXICAL_BINDING;
+ }
}
diff --git a/src/jdk/nashorn/internal/runtime/PropertyMap.java b/src/jdk/nashorn/internal/runtime/PropertyMap.java
index 61912332..30a15132 100644
--- a/src/jdk/nashorn/internal/runtime/PropertyMap.java
+++ b/src/jdk/nashorn/internal/runtime/PropertyMap.java
@@ -84,7 +84,7 @@ public final class PropertyMap implements Iterable<Object>, Serializable {
private transient WeakHashMap<Property, SoftReference<PropertyMap>> history;
/** History of prototypes, used to limit map duplication. */
- private transient WeakHashMap<PropertyMap, SoftReference<PropertyMap>> protoHistory;
+ private transient WeakHashMap<ScriptObject, SoftReference<PropertyMap>> protoHistory;
/** property listeners */
private transient PropertyListeners listeners;
@@ -512,7 +512,7 @@ public final class PropertyMap implements Iterable<Object>, Serializable {
assert sameType ||
oldProperty instanceof AccessorProperty &&
newProperty instanceof UserAccessorProperty :
- "arbitrary replaceProperty attempted " + sameType + " oldProperty=" + oldProperty.getClass() + " newProperty=" + newProperty.getClass() + " [" + oldProperty.getCurrentType() + " => " + newProperty.getCurrentType() + "]";
+ "arbitrary replaceProperty attempted " + sameType + " oldProperty=" + oldProperty.getClass() + " newProperty=" + newProperty.getClass() + " [" + oldProperty.getLocalType() + " => " + newProperty.getLocalType() + "]";
newMap.flags = flags;
@@ -677,14 +677,14 @@ public final class PropertyMap implements Iterable<Object>, Serializable {
/**
* Check prototype history for an existing property map with specified prototype.
*
- * @param parentMap New prototype object.
+ * @param proto New prototype object.
*
* @return Existing {@link PropertyMap} or {@code null} if not found.
*/
- private PropertyMap checkProtoHistory(final PropertyMap parentMap) {
+ private PropertyMap checkProtoHistory(final ScriptObject proto) {
final PropertyMap cachedMap;
if (protoHistory != null) {
- final SoftReference<PropertyMap> weakMap = protoHistory.get(parentMap);
+ final SoftReference<PropertyMap> weakMap = protoHistory.get(proto);
cachedMap = (weakMap != null ? weakMap.get() : null);
} else {
cachedMap = null;
@@ -700,15 +700,15 @@ public final class PropertyMap implements Iterable<Object>, Serializable {
/**
* Add a map to the prototype history.
*
- * @param parentMap Prototype to add (key.)
+ * @param newProto Prototype to add (key.)
* @param newMap {@link PropertyMap} associated with prototype.
*/
- private void addToProtoHistory(final PropertyMap parentMap, final PropertyMap newMap) {
+ private void addToProtoHistory(final ScriptObject newProto, final PropertyMap newMap) {
if (protoHistory == null) {
protoHistory = new WeakHashMap<>();
}
- protoHistory.put(parentMap, new SoftReference<>(newMap));
+ protoHistory.put(newProto, new SoftReference<>(newMap));
}
/**
@@ -883,8 +883,7 @@ public final class PropertyMap implements Iterable<Object>, Serializable {
*/
public PropertyMap changeProto(final ScriptObject newProto) {
- final PropertyMap parentMap = newProto == null ? null : newProto.getMap();
- final PropertyMap nextMap = checkProtoHistory(parentMap);
+ final PropertyMap nextMap = checkProtoHistory(newProto);
if (nextMap != null) {
return nextMap;
}
@@ -894,7 +893,7 @@ public final class PropertyMap implements Iterable<Object>, Serializable {
}
final PropertyMap newMap = new PropertyMap(this);
- addToProtoHistory(parentMap, newMap);
+ addToProtoHistory(newProto, newMap);
return newMap;
}
diff --git a/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java b/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java
index 6b2c7620..06414edb 100644
--- a/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java
+++ b/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java
@@ -26,7 +26,6 @@
package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.lookup.Lookup.MH;
-
import java.io.IOException;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
@@ -475,6 +474,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
* @return either the existing map, or a loaded map from the persistent type info cache, or a new empty map if
* neither an existing map or a persistent cached type info is available.
*/
+ @SuppressWarnings("unused")
private static Map<Integer, Type> getEffectiveInvalidatedProgramPoints(
final Map<Integer, Type> invalidatedProgramPoints, final Object typeInformationFile) {
if(invalidatedProgramPoints != null) {
@@ -619,20 +619,25 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
return f;
}
- MethodHandle lookup(final FunctionInitializer fnInit) {
+ private void logLookup(final boolean shouldLog, final MethodType targetType) {
+ if (shouldLog && log.isEnabled()) {
+ log.info("Looking up ", DebugLogger.quote(functionName), " type=", targetType);
+ }
+ }
+
+ private MethodHandle lookup(final FunctionInitializer fnInit, final boolean shouldLog) {
final MethodType type = fnInit.getMethodType();
+ logLookup(shouldLog, type);
return lookupCodeMethod(fnInit.getCode(), type);
}
MethodHandle lookup(final FunctionNode fn) {
final MethodType type = new FunctionSignature(fn).getMethodType();
+ logLookup(true, type);
return lookupCodeMethod(fn.getCompileUnit().getCode(), type);
}
MethodHandle lookupCodeMethod(final Class<?> codeClass, final MethodType targetType) {
- if (log.isEnabled()) {
- log.info("Looking up ", DebugLogger.quote(functionName), " type=", targetType);
- }
return MH.findStatic(LOOKUP, codeClass, functionName, targetType);
}
@@ -648,7 +653,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
if(!code.isEmpty()) {
throw new IllegalStateException(name);
}
- addCode(lookup(initializer), null, null, initializer.getFlags());
+ addCode(lookup(initializer, true), null, null, initializer.getFlags());
}
private CompiledFunction addCode(final MethodHandle target, final Map<Integer, Type> invalidatedProgramPoints,
@@ -670,10 +675,10 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
*/
private CompiledFunction addCode(final FunctionInitializer fnInit, final MethodType callSiteType) {
if (isVariableArity()) {
- return addCode(lookup(fnInit), fnInit.getInvalidatedProgramPoints(), callSiteType, fnInit.getFlags());
+ return addCode(lookup(fnInit, true), fnInit.getInvalidatedProgramPoints(), callSiteType, fnInit.getFlags());
}
- final MethodHandle handle = lookup(fnInit);
+ final MethodHandle handle = lookup(fnInit, true);
final MethodType fromType = handle.type();
MethodType toType = needsCallee(fromType) ? callSiteType.changeParameterType(0, ScriptFunction.class) : callSiteType.dropParameterTypes(0, 1);
toType = toType.changeReturnType(fromType.returnType());
@@ -698,7 +703,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
toType = toType.dropParameterTypes(fromCount, toCount);
}
- return addCode(lookup(fnInit).asType(toType), fnInit.getInvalidatedProgramPoints(), callSiteType, fnInit.getFlags());
+ return addCode(lookup(fnInit, false).asType(toType), fnInit.getInvalidatedProgramPoints(), callSiteType, fnInit.getFlags());
}
/**
@@ -727,7 +732,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
assert existingBest != null;
//we are calling a vararg method with real args
- boolean applyToCall = existingBest.isVarArg() && !CompiledFunction.isVarArgsType(callSiteType);
+ boolean varArgWithRealArgs = existingBest.isVarArg() && !CompiledFunction.isVarArgsType(callSiteType);
//if the best one is an apply to call, it has to match the callsite exactly
//or we need to regenerate
@@ -736,14 +741,16 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
if (best != null) {
return best;
}
- applyToCall = true;
+ varArgWithRealArgs = true;
}
- if (applyToCall) {
+ if (varArgWithRealArgs) {
+ // special case: we had an apply to call, but we failed to make it fit.
+ // Try to generate a specialized one for this callsite. It may
+ // be another apply to call specialization, or it may not, but whatever
+ // it is, it is a specialization that is guaranteed to fit
final FunctionInitializer fnInit = compileTypeSpecialization(callSiteType, runtimeScope, false);
- if ((fnInit.getFlags() & FunctionNode.HAS_APPLY_TO_CALL_SPECIALIZATION) != 0) { //did the specialization work
- existingBest = addCode(fnInit, callSiteType);
- }
+ existingBest = addCode(fnInit, callSiteType);
}
return existingBest;
diff --git a/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java b/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java
index cd528a01..c7fd0190 100644
--- a/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java
+++ b/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java
@@ -212,6 +212,7 @@ public final class ScriptEnvironment {
* @param out output print writer
* @param err error print writer
*/
+ @SuppressWarnings("unused")
public ScriptEnvironment(final Options options, final PrintWriter out, final PrintWriter err) {
this.out = out;
this.err = err;
diff --git a/src/jdk/nashorn/internal/runtime/ScriptFunction.java b/src/jdk/nashorn/internal/runtime/ScriptFunction.java
index 0ba06b3a..d999c118 100644
--- a/src/jdk/nashorn/internal/runtime/ScriptFunction.java
+++ b/src/jdk/nashorn/internal/runtime/ScriptFunction.java
@@ -603,16 +603,6 @@ public abstract class ScriptFunction extends ScriptObject {
log.info("Linking optimistic builtin function: '", name, "' args=", Arrays.toString(request.getArguments()), " desc=", desc);
}
- final SwitchPoint[] msps = linkLogic.getModificationSwitchPoints();
- if (msps != null) {
- for (final SwitchPoint sp : msps) {
- if (sp != null) {
- assert !sp.hasBeenInvalidated();
- sps.add(sp);
- }
- }
- }
-
exceptionGuard = linkLogic.getRelinkException();
break;
diff --git a/src/jdk/nashorn/internal/runtime/ScriptObject.java b/src/jdk/nashorn/internal/runtime/ScriptObject.java
index 87d28976..c3eca18f 100644
--- a/src/jdk/nashorn/internal/runtime/ScriptObject.java
+++ b/src/jdk/nashorn/internal/runtime/ScriptObject.java
@@ -46,7 +46,10 @@ import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.getArrayIndex;
import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex;
+import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.isScopeFlag;
+import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.isStrictFlag;
import static jdk.nashorn.internal.runtime.linker.NashornGuards.explicitInstanceOfCheck;
+
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
@@ -97,7 +100,7 @@ import jdk.nashorn.internal.runtime.linker.NashornGuards;
* </ul>
*/
-public abstract class ScriptObject implements PropertyAccess {
+public abstract class ScriptObject implements PropertyAccess, Cloneable {
/** __proto__ special property name inside object literals. ES6 draft. */
public static final String PROTO_PROPERTY_NAME = "__proto__";
@@ -303,29 +306,44 @@ public abstract class ScriptObject implements PropertyAccess {
PropertyMap newMap = this.getMap();
for (final Property property : properties) {
- final String key = property.getKey();
- final Property oldProp = newMap.findProperty(key);
- if (oldProp == null) {
- if (property instanceof UserAccessorProperty) {
- // Note: we copy accessor functions to this object which is semantically different from binding.
- final UserAccessorProperty prop = this.newUserAccessors(key, property.getFlags(), property.getGetterFunction(source), property.getSetterFunction(source));
- newMap = newMap.addPropertyNoHistory(prop);
- } else {
- newMap = newMap.addPropertyBind((AccessorProperty)property, source);
- }
+ newMap = addBoundProperty(newMap, source, property);
+ }
+
+ this.setMap(newMap);
+ }
+
+ /**
+ * Add a bound property from {@code source}, using the interim property map {@code propMap}, and return the
+ * new interim property map.
+ *
+ * @param propMap the property map
+ * @param source the source object
+ * @param property the property to be added
+ * @return the new property map
+ */
+ protected PropertyMap addBoundProperty(final PropertyMap propMap, final ScriptObject source, final Property property) {
+ PropertyMap newMap = propMap;
+ final String key = property.getKey();
+ final Property oldProp = newMap.findProperty(key);
+ if (oldProp == null) {
+ if (property instanceof UserAccessorProperty) {
+ // Note: we copy accessor functions to this object which is semantically different from binding.
+ final UserAccessorProperty prop = this.newUserAccessors(key, property.getFlags(), property.getGetterFunction(source), property.getSetterFunction(source));
+ newMap = newMap.addPropertyNoHistory(prop);
} else {
- // See ECMA section 10.5 Declaration Binding Instantiation
- // step 5 processing each function declaration.
- if (property.isFunctionDeclaration() && !oldProp.isConfigurable()) {
- if (oldProp instanceof UserAccessorProperty ||
- !(oldProp.isWritable() && oldProp.isEnumerable())) {
- throw typeError("cant.redefine.property", key, ScriptRuntime.safeToString(this));
- }
+ newMap = newMap.addPropertyBind((AccessorProperty)property, source);
+ }
+ } else {
+ // See ECMA section 10.5 Declaration Binding Instantiation
+ // step 5 processing each function declaration.
+ if (property.isFunctionDeclaration() && !oldProp.isConfigurable()) {
+ if (oldProp instanceof UserAccessorProperty ||
+ !(oldProp.isWritable() && oldProp.isEnumerable())) {
+ throw typeError("cant.redefine.property", key, ScriptRuntime.safeToString(this));
}
}
}
-
- this.setMap(newMap);
+ return newMap;
}
/**
@@ -510,6 +528,17 @@ public abstract class ScriptObject implements PropertyAccess {
}
/**
+ * Invalidate any existing global constant method handles that may exist for {@code key}.
+ * @param key the property name
+ */
+ protected void invalidateGlobalConstant(final String key) {
+ final GlobalConstants globalConstants = getGlobalConstants();
+ if (globalConstants != null) {
+ globalConstants.delete(key);
+ }
+ }
+
+ /**
* ECMA 8.12.9 [[DefineOwnProperty]] (P, Desc, Throw)
*
* @param key the property key
@@ -524,6 +553,8 @@ public abstract class ScriptObject implements PropertyAccess {
final Object current = getOwnPropertyDescriptor(key);
final String name = JSType.toString(key);
+ invalidateGlobalConstant(key);
+
if (current == UNDEFINED) {
if (isExtensible()) {
// add a new own property
@@ -692,8 +723,7 @@ public abstract class ScriptObject implements PropertyAccess {
assert isValidArrayIndex(index) : "invalid array index";
final long longIndex = ArrayIndex.toLongIndex(index);
doesNotHaveEnsureDelete(longIndex, getArray().length(), false);
- setArray(getArray().ensure(longIndex));
- setArray(getArray().set(index, value, false));
+ setArray(getArray().ensure(longIndex).set(index,value, false));
}
private void checkIntegerKey(final String key) {
@@ -923,7 +953,8 @@ public abstract class ScriptObject implements PropertyAccess {
if (property instanceof UserAccessorProperty) {
((UserAccessorProperty)property).setAccessors(this, getMap(), null);
}
- Global.getConstants().delete(property.getKey());
+
+ invalidateGlobalConstant(property.getKey());
return true;
}
}
@@ -970,7 +1001,7 @@ public abstract class ScriptObject implements PropertyAccess {
final UserAccessorProperty uc = (UserAccessorProperty)oldProperty;
final int slot = uc.getSlot();
- assert uc.getCurrentType() == Object.class;
+ assert uc.getLocalType() == Object.class;
if (slot >= spillLength) {
uc.setAccessors(this, getMap(), new UserAccessorProperty.Accessors(getter, setter));
} else {
@@ -1349,12 +1380,9 @@ public abstract class ScriptObject implements PropertyAccess {
final PropertyMap selfMap = this.getMap();
final ArrayData array = getArray();
- final long length = array.length();
- for (long i = 0; i < length; i = array.nextIndex(i)) {
- if (array.has((int)i)) {
- keys.add(JSType.toString(i));
- }
+ for (final Iterator<Long> iter = array.indexIterator(); iter.hasNext(); ) {
+ keys.add(JSType.toString(iter.next().longValue()));
}
for (final Property property : selfMap.getProperties()) {
@@ -1462,9 +1490,8 @@ public abstract class ScriptObject implements PropertyAccess {
//invalidate any fast array setters
final ArrayData array = getArray();
- if (array != null) {
- array.invalidateSetters();
- }
+ assert array != null;
+ setArray(ArrayData.preventExtension(array));
return this;
}
@@ -1514,12 +1541,12 @@ public abstract class ScriptObject implements PropertyAccess {
*
* @return {@code true} if 'length' property is non-writable
*/
- public final boolean isLengthNotWritable() {
+ public boolean isLengthNotWritable() {
return (flags & IS_LENGTH_NOT_WRITABLE) != 0;
}
/**
- * Flag this object as having non-writable length property
+ * Flag this object as having non-writable length property.
*/
public void setIsLengthNotWritable() {
flags |= IS_LENGTH_NOT_WRITABLE;
@@ -1974,20 +2001,22 @@ public abstract class ScriptObject implements PropertyAccess {
if (find == null) {
switch (operator) {
+ case "getElem": // getElem only gets here if element name is constant, so treat it like a property access
case "getProp":
return noSuchProperty(desc, request);
case "getMethod":
return noSuchMethod(desc, request);
- case "getElem":
- return createEmptyGetter(desc, explicitInstanceOfCheck, name);
default:
throw new AssertionError(operator); // never invoked with any other operation
}
}
- final GuardedInvocation cinv = Global.getConstants().findGetMethod(find, this, desc);
- if (cinv != null) {
- return cinv;
+ final GlobalConstants globalConstants = getGlobalConstants();
+ if (globalConstants != null) {
+ final GuardedInvocation cinv = globalConstants.findGetMethod(find, this, desc);
+ if (cinv != null) {
+ return cinv;
+ }
}
final Class<?> returnType = desc.getMethodType().returnType();
@@ -2174,6 +2203,9 @@ public abstract class ScriptObject implements PropertyAccess {
if (find != null) {
if (!find.getProperty().isWritable() && !NashornCallSiteDescriptor.isDeclaration(desc)) {
+ if (NashornCallSiteDescriptor.isScope(desc) && find.getProperty().isLexicalBinding()) {
+ throw typeError("assign.constant", name); // Overwriting ES6 const should throw also in non-strict mode.
+ }
// Existing, non-writable property
return createEmptySetMethod(desc, explicitInstanceOfCheck, "property.not.writable", true);
}
@@ -2185,14 +2217,22 @@ public abstract class ScriptObject implements PropertyAccess {
final GuardedInvocation inv = new SetMethodCreator(this, find, desc, request).createGuardedInvocation(findBuiltinSwitchPoint(name));
- final GuardedInvocation cinv = Global.getConstants().findSetMethod(find, this, inv, desc, request);
- if (cinv != null) {
- return cinv;
+ final GlobalConstants globalConstants = getGlobalConstants();
+ if (globalConstants != null) {
+ final GuardedInvocation cinv = globalConstants.findSetMethod(find, this, inv, desc, request);
+ if (cinv != null) {
+ return cinv;
+ }
}
return inv;
}
+ private GlobalConstants getGlobalConstants() {
+ // Avoid hitting getContext() which might be costly for a non-Global unless needed.
+ return GlobalConstants.GLOBAL_ONLY && !isGlobal() ? null : getContext().getGlobalConstants();
+ }
+
private GuardedInvocation createEmptySetMethod(final CallSiteDescriptor desc, final boolean explicitInstanceOfCheck, final String strictErrorMessage, final boolean canBeFastScope) {
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
if (NashornCallSiteDescriptor.isStrict(desc)) {
@@ -2292,8 +2332,9 @@ public abstract class ScriptObject implements PropertyAccess {
}
final ScriptFunction func = (ScriptFunction)value;
- final Object thiz = scopeCall && func.isStrict() ? ScriptRuntime.UNDEFINED : this;
+ final Object thiz = scopeCall && func.isStrict() ? UNDEFINED : this;
// TODO: It'd be awesome if we could bind "name" without binding "this".
+ // Since we're binding this we must use an identity guard here.
return new GuardedInvocation(
MH.dropArguments(
MH.constant(
@@ -2301,9 +2342,9 @@ public abstract class ScriptObject implements PropertyAccess {
func.makeBoundFunction(thiz, new Object[] { name })),
0,
Object.class),
- NashornGuards.getMapGuard(getMap(), explicitInstanceOfCheck),
- (SwitchPoint)null,
- explicitInstanceOfCheck ? null : ClassCastException.class);
+ NashornGuards.combineGuards(
+ NashornGuards.getIdentityGuard(this),
+ NashornGuards.getMapGuard(getMap(), true)));
}
/**
@@ -2645,20 +2686,22 @@ public abstract class ScriptObject implements PropertyAccess {
* @param newLength new length to set
*/
public final void setLength(final long newLength) {
- final long arrayLength = getArray().length();
- if (newLength == arrayLength) {
- return;
- }
+ ArrayData data = getArray();
+ final long arrayLength = data.length();
+ if (newLength == arrayLength) {
+ return;
+ }
- if (newLength > arrayLength) {
- setArray(getArray().ensure(newLength - 1));
- if (getArray().canDelete(arrayLength, newLength - 1, false)) {
- setArray(getArray().delete(arrayLength, newLength - 1));
- }
- return;
- }
+ if (newLength > arrayLength) {
+ data = data.ensure(newLength - 1);
+ if (data.canDelete(arrayLength, newLength - 1, false)) {
+ data = data.delete(arrayLength, newLength - 1);
+ }
+ setArray(data);
+ return;
+ }
- if (newLength < arrayLength) {
+ if (newLength < arrayLength) {
long actualLength = newLength;
// Check for numeric keys in property map and delete them or adjust length, depending on whether
@@ -2680,8 +2723,8 @@ public abstract class ScriptObject implements PropertyAccess {
}
}
- setArray(getArray().shrink(actualLength));
- getArray().setLength(actualLength);
+ setArray(data.shrink(actualLength));
+ data.setLength(actualLength);
}
}
@@ -3065,7 +3108,7 @@ public abstract class ScriptObject implements PropertyAccess {
private boolean doesNotHaveEnsureLength(final long longIndex, final long oldLength, final int callSiteFlags) {
if (longIndex >= oldLength) {
if (!isExtensible()) {
- if (NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)) {
+ if (isStrictFlag(callSiteFlags)) {
throw typeError("object.non.extensible", JSType.toString(longIndex), ScriptRuntime.safeToString(this));
}
return true;
@@ -3089,7 +3132,7 @@ public abstract class ScriptObject implements PropertyAccess {
final long oldLength = getArray().length();
final long longIndex = ArrayIndex.toLongIndex(index);
if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) {
- final boolean strict = NashornCallSiteDescriptor.isStrictFlag(callSiteFlags);
+ final boolean strict = isStrictFlag(callSiteFlags);
setArray(getArray().set(index, value, strict));
doesNotHaveEnsureDelete(longIndex, oldLength, strict);
}
@@ -3099,7 +3142,7 @@ public abstract class ScriptObject implements PropertyAccess {
final long oldLength = getArray().length();
final long longIndex = ArrayIndex.toLongIndex(index);
if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) {
- final boolean strict = NashornCallSiteDescriptor.isStrictFlag(callSiteFlags);
+ final boolean strict = isStrictFlag(callSiteFlags);
setArray(getArray().set(index, value, strict));
doesNotHaveEnsureDelete(longIndex, oldLength, strict);
}
@@ -3109,7 +3152,7 @@ public abstract class ScriptObject implements PropertyAccess {
final long oldLength = getArray().length();
final long longIndex = ArrayIndex.toLongIndex(index);
if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) {
- final boolean strict = NashornCallSiteDescriptor.isStrictFlag(callSiteFlags);
+ final boolean strict = isStrictFlag(callSiteFlags);
setArray(getArray().set(index, value, strict));
doesNotHaveEnsureDelete(longIndex, oldLength, strict);
}
@@ -3119,7 +3162,7 @@ public abstract class ScriptObject implements PropertyAccess {
final long oldLength = getArray().length();
final long longIndex = ArrayIndex.toLongIndex(index);
if (!doesNotHaveCheckArrayKeys(longIndex, value, callSiteFlags) && !doesNotHaveEnsureLength(longIndex, oldLength, callSiteFlags)) {
- final boolean strict = NashornCallSiteDescriptor.isStrictFlag(callSiteFlags);
+ final boolean strict = isStrictFlag(callSiteFlags);
setArray(getArray().set(index, value, strict));
doesNotHaveEnsureDelete(longIndex, oldLength, strict);
}
@@ -3137,8 +3180,10 @@ public abstract class ScriptObject implements PropertyAccess {
public final void setObject(final FindProperty find, final int callSiteFlags, final String key, final Object value) {
FindProperty f = find;
+ invalidateGlobalConstant(key);
+
if (f != null && f.isInherited() && !(f.getProperty() instanceof UserAccessorProperty)) {
- final boolean isScope = NashornCallSiteDescriptor.isScopeFlag(callSiteFlags);
+ final boolean isScope = isScopeFlag(callSiteFlags);
// If the start object of the find is not this object it means the property was found inside a
// 'with' statement expression (see WithObject.findProperty()). In this case we forward the 'set'
// to the 'with' object.
@@ -3159,17 +3204,19 @@ public abstract class ScriptObject implements PropertyAccess {
if (f != null) {
if (!f.getProperty().isWritable()) {
- if (NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)) {
+ if (isScopeFlag(callSiteFlags) && f.getProperty().isLexicalBinding()) {
+ throw typeError("assign.constant", key); // Overwriting ES6 const should throw also in non-strict mode.
+ }
+ if (isStrictFlag(callSiteFlags)) {
throw typeError("property.not.writable", key, ScriptRuntime.safeToString(this));
}
-
return;
}
- f.setValue(value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags));
+ f.setValue(value, isStrictFlag(callSiteFlags));
} else if (!isExtensible()) {
- if (NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)) {
+ if (isStrictFlag(callSiteFlags)) {
throw typeError("object.non.extensible", key, ScriptRuntime.safeToString(this));
}
} else {
@@ -3194,8 +3241,9 @@ public abstract class ScriptObject implements PropertyAccess {
final int index = getArrayIndex(primitiveKey);
if (isValidArrayIndex(index)) {
- if (getArray().has(index)) {
- setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
+ final ArrayData data = getArray();
+ if (data.has(index)) {
+ setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
} else {
doesNotHave(index, value, callSiteFlags);
}
@@ -3213,8 +3261,9 @@ public abstract class ScriptObject implements PropertyAccess {
final int index = getArrayIndex(primitiveKey);
if (isValidArrayIndex(index)) {
- if (getArray().has(index)) {
- setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
+ final ArrayData data = getArray();
+ if (data.has(index)) {
+ setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
} else {
doesNotHave(index, value, callSiteFlags);
}
@@ -3232,8 +3281,9 @@ public abstract class ScriptObject implements PropertyAccess {
final int index = getArrayIndex(primitiveKey);
if (isValidArrayIndex(index)) {
- if (getArray().has(index)) {
- setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
+ final ArrayData data = getArray();
+ if (data.has(index)) {
+ setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
} else {
doesNotHave(index, value, callSiteFlags);
}
@@ -3251,8 +3301,9 @@ public abstract class ScriptObject implements PropertyAccess {
final int index = getArrayIndex(primitiveKey);
if (isValidArrayIndex(index)) {
- if (getArray().has(index)) {
- setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
+ final ArrayData data = getArray();
+ if (data.has(index)) {
+ setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
} else {
doesNotHave(index, value, callSiteFlags);
}
@@ -3269,8 +3320,9 @@ public abstract class ScriptObject implements PropertyAccess {
final int index = getArrayIndex(key);
if (isValidArrayIndex(index)) {
- if (getArray().has(index)) {
- setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
+ final ArrayData data = getArray();
+ if (data.has(index)) {
+ setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
} else {
doesNotHave(index, value, callSiteFlags);
}
@@ -3287,8 +3339,9 @@ public abstract class ScriptObject implements PropertyAccess {
final int index = getArrayIndex(key);
if (isValidArrayIndex(index)) {
- if (getArray().has(index)) {
- setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
+ final ArrayData data = getArray();
+ if (data.has(index)) {
+ setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
} else {
doesNotHave(index, value, callSiteFlags);
}
@@ -3305,8 +3358,9 @@ public abstract class ScriptObject implements PropertyAccess {
final int index = getArrayIndex(key);
if (isValidArrayIndex(index)) {
- if (getArray().has(index)) {
- setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
+ final ArrayData data = getArray();
+ if (data.has(index)) {
+ setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
} else {
doesNotHave(index, value, callSiteFlags);
}
@@ -3323,8 +3377,9 @@ public abstract class ScriptObject implements PropertyAccess {
final int index = getArrayIndex(key);
if (isValidArrayIndex(index)) {
- if (getArray().has(index)) {
- setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
+ final ArrayData data = getArray();
+ if (data.has(index)) {
+ setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
} else {
doesNotHave(index, value, callSiteFlags);
}
@@ -3341,8 +3396,9 @@ public abstract class ScriptObject implements PropertyAccess {
final int index = getArrayIndex(key);
if (isValidArrayIndex(index)) {
- if (getArray().has(index)) {
- setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
+ final ArrayData data = getArray();
+ if (data.has(index)) {
+ setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
} else {
doesNotHave(index, value, callSiteFlags);
}
@@ -3359,8 +3415,9 @@ public abstract class ScriptObject implements PropertyAccess {
final int index = getArrayIndex(key);
if (isValidArrayIndex(index)) {
- if (getArray().has(index)) {
- setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
+ final ArrayData data = getArray();
+ if (data.has(index)) {
+ setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
} else {
doesNotHave(index, value, callSiteFlags);
}
@@ -3377,8 +3434,9 @@ public abstract class ScriptObject implements PropertyAccess {
final int index = getArrayIndex(key);
if (isValidArrayIndex(index)) {
- if (getArray().has(index)) {
- setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
+ final ArrayData data = getArray();
+ if (data.has(index)) {
+ setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
} else {
doesNotHave(index, value, callSiteFlags);
}
@@ -3395,8 +3453,9 @@ public abstract class ScriptObject implements PropertyAccess {
final int index = getArrayIndex(key);
if (isValidArrayIndex(index)) {
- if (getArray().has(index)) {
- setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
+ final ArrayData data = getArray();
+ if (data.has(index)) {
+ setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
} else {
doesNotHave(index, value, callSiteFlags);
}
@@ -3413,7 +3472,8 @@ public abstract class ScriptObject implements PropertyAccess {
final int index = getArrayIndex(key);
if (isValidArrayIndex(index)) {
if (getArray().has(index)) {
- setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
+ final ArrayData data = getArray();
+ setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
} else {
doesNotHave(index, value, callSiteFlags);
}
@@ -3429,8 +3489,9 @@ public abstract class ScriptObject implements PropertyAccess {
final int index = getArrayIndex(key);
if (isValidArrayIndex(index)) {
- if (getArray().has(index)) {
- setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
+ final ArrayData data = getArray();
+ if (data.has(index)) {
+ setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
} else {
doesNotHave(index, value, callSiteFlags);
}
@@ -3447,8 +3508,9 @@ public abstract class ScriptObject implements PropertyAccess {
final int index = getArrayIndex(key);
if (isValidArrayIndex(index)) {
- if (getArray().has(index)) {
- setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
+ final ArrayData data = getArray();
+ if (data.has(index)) {
+ setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
} else {
doesNotHave(index, value, callSiteFlags);
}
@@ -3465,8 +3527,9 @@ public abstract class ScriptObject implements PropertyAccess {
final int index = getArrayIndex(key);
if (isValidArrayIndex(index)) {
- if (getArray().has(index)) {
- setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
+ final ArrayData data = getArray();
+ if (data.has(index)) {
+ setArray(data.set(index, value, isStrictFlag(callSiteFlags)));
} else {
doesNotHave(index, value, callSiteFlags);
}
@@ -3557,7 +3620,6 @@ public abstract class ScriptObject implements PropertyAccess {
}
return false;
}
-
return deleteObject(JSType.toObject(key), strict);
}
@@ -3632,6 +3694,31 @@ public abstract class ScriptObject implements PropertyAccess {
}
/**
+ * Return a shallow copy of this ScriptObject.
+ * @return a shallow copy.
+ */
+ public final ScriptObject copy() {
+ try {
+ return clone();
+ } catch (final CloneNotSupportedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ protected ScriptObject clone() throws CloneNotSupportedException {
+ final ScriptObject clone = (ScriptObject) super.clone();
+ if (objectSpill != null) {
+ clone.objectSpill = objectSpill.clone();
+ if (primitiveSpill != null) {
+ clone.primitiveSpill = primitiveSpill.clone();
+ }
+ }
+ clone.arrayData = arrayData.copy();
+ return clone;
+ }
+
+ /**
* Make a new UserAccessorProperty property. getter and setter functions are stored in
* this ScriptObject and slot values are used in property object.
*
diff --git a/src/jdk/nashorn/internal/runtime/SpillProperty.java b/src/jdk/nashorn/internal/runtime/SpillProperty.java
index 8ff1b8e5..7b42b2bf 100644
--- a/src/jdk/nashorn/internal/runtime/SpillProperty.java
+++ b/src/jdk/nashorn/internal/runtime/SpillProperty.java
@@ -161,12 +161,12 @@ public class SpillProperty extends AccessorProperty {
*/
public SpillProperty(final String key, final int flags, final int slot) {
super(key, flags, slot, primitiveGetter(slot), primitiveSetter(slot), objectGetter(slot), objectSetter(slot));
- assert !OBJECT_FIELDS_ONLY || getCurrentType() == Object.class;
+ assert !OBJECT_FIELDS_ONLY || getLocalType() == Object.class;
}
SpillProperty(final String key, final int flags, final int slot, final Class<?> initialType) {
this(key, flags, slot);
- setCurrentType(OBJECT_FIELDS_ONLY ? Object.class : initialType);
+ setType(OBJECT_FIELDS_ONLY ? Object.class : initialType);
}
SpillProperty(final String key, final int flags, final int slot, final ScriptObject owner, final Object initialValue) {
diff --git a/src/jdk/nashorn/internal/runtime/UserAccessorProperty.java b/src/jdk/nashorn/internal/runtime/UserAccessorProperty.java
index 5fdec009..d14dd8e7 100644
--- a/src/jdk/nashorn/internal/runtime/UserAccessorProperty.java
+++ b/src/jdk/nashorn/internal/runtime/UserAccessorProperty.java
@@ -27,16 +27,16 @@ package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
-import static jdk.nashorn.internal.runtime.JSType.CONVERT_OBJECT_OPTIMISTIC;
-import static jdk.nashorn.internal.runtime.JSType.getAccessorTypeIndex;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
+import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
+import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_PROGRAM_POINT_SHIFT;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
-import java.util.concurrent.Callable;
import jdk.nashorn.internal.lookup.Lookup;
import jdk.nashorn.internal.runtime.linker.Bootstrap;
+import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
/**
* Property with user defined getters/setters. Actual getter and setter
@@ -69,38 +69,29 @@ public final class UserAccessorProperty extends SpillProperty {
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
/** Getter method handle */
- private final static MethodHandle INVOKE_GETTER_ACCESSOR = findOwnMH_S("invokeGetterAccessor", Object.class, Accessors.class, Object.class);
+ private final static MethodHandle INVOKE_OBJECT_GETTER = findOwnMH_S("invokeObjectGetter", Object.class, Accessors.class, MethodHandle.class, Object.class);
+ private final static MethodHandle INVOKE_INT_GETTER = findOwnMH_S("invokeIntGetter", int.class, Accessors.class, MethodHandle.class, int.class, Object.class);
+ private final static MethodHandle INVOKE_LONG_GETTER = findOwnMH_S("invokeLongGetter", long.class, Accessors.class, MethodHandle.class, int.class, Object.class);
+ private final static MethodHandle INVOKE_NUMBER_GETTER = findOwnMH_S("invokeNumberGetter", double.class, Accessors.class, MethodHandle.class, int.class, Object.class);
/** Setter method handle */
- private final static MethodHandle INVOKE_SETTER_ACCESSOR = findOwnMH_S("invokeSetterAccessor", void.class, Accessors.class, String.class, Object.class, Object.class);
-
- /** Dynamic invoker for getter */
- private static final Object GETTER_INVOKER_KEY = new Object();
-
- private static MethodHandle getINVOKE_UA_GETTER() {
-
- return Context.getGlobal().getDynamicInvoker(GETTER_INVOKER_KEY,
- new Callable<MethodHandle>() {
- @Override
- public MethodHandle call() {
- return Bootstrap.createDynamicInvoker("dyn:call", Object.class,
- Object.class, Object.class);
- }
- });
+ private final static MethodHandle INVOKE_OBJECT_SETTER = findOwnMH_S("invokeObjectSetter", void.class, Accessors.class, MethodHandle.class, String.class, Object.class, Object.class);
+ private final static MethodHandle INVOKE_INT_SETTER = findOwnMH_S("invokeIntSetter", void.class, Accessors.class, MethodHandle.class, String.class, Object.class, int.class);
+ private final static MethodHandle INVOKE_LONG_SETTER = findOwnMH_S("invokeLongSetter", void.class, Accessors.class, MethodHandle.class, String.class, Object.class, long.class);
+ private final static MethodHandle INVOKE_NUMBER_SETTER = findOwnMH_S("invokeNumberSetter", void.class, Accessors.class, MethodHandle.class, String.class, Object.class, double.class);
+
+
+ static MethodHandle getINVOKE_UA_GETTER(final Class<?> returnType, final int programPoint) {
+ if (UnwarrantedOptimismException.isValid(programPoint)) {
+ final int flags = NashornCallSiteDescriptor.CALLSITE_OPTIMISTIC | programPoint << CALLSITE_PROGRAM_POINT_SHIFT;
+ return Bootstrap.createDynamicInvoker("dyn:call", flags, returnType, Object.class, Object.class);
+ } else {
+ return Bootstrap.createDynamicInvoker("dyn:call", Object.class, Object.class, Object.class);
+ }
}
- /** Dynamic invoker for setter */
- private static Object SETTER_INVOKER_KEY = new Object();
-
- private static MethodHandle getINVOKE_UA_SETTER() {
- return Context.getGlobal().getDynamicInvoker(SETTER_INVOKER_KEY,
- new Callable<MethodHandle>() {
- @Override
- public MethodHandle call() {
- return Bootstrap.createDynamicInvoker("dyn:call", void.class,
- Object.class, Object.class, Object.class);
- }
- });
+ static MethodHandle getINVOKE_UA_SETTER(final Class<?> valueType) {
+ return Bootstrap.createDynamicInvoker("dyn:call", void.class, Object.class, Object.class, valueType);
}
/**
@@ -158,7 +149,7 @@ public final class UserAccessorProperty extends SpillProperty {
}
@Override
- public Class<?> getCurrentType() {
+ protected Class<?> getLocalType() {
return Object.class;
}
@@ -189,7 +180,13 @@ public final class UserAccessorProperty extends SpillProperty {
@Override
public Object getObjectValue(final ScriptObject self, final ScriptObject owner) {
- return invokeGetterAccessor(getAccessors((owner != null) ? owner : self), self);
+ try {
+ return invokeObjectGetter(getAccessors((owner != null) ? owner : self), getINVOKE_UA_GETTER(Object.class, INVALID_PROGRAM_POINT), self);
+ } catch (final Error | RuntimeException t) {
+ throw t;
+ } catch (final Throwable t) {
+ throw new RuntimeException(t);
+ }
}
@Override
@@ -209,41 +206,33 @@ public final class UserAccessorProperty extends SpillProperty {
@Override
public void setValue(final ScriptObject self, final ScriptObject owner, final Object value, final boolean strict) {
- invokeSetterAccessor(getAccessors((owner != null) ? owner : self), strict ? getKey() : null, self, value);
+ try {
+ invokeObjectSetter(getAccessors((owner != null) ? owner : self), getINVOKE_UA_SETTER(Object.class), strict ? getKey() : null, self, value);
+ } catch (final Error | RuntimeException t) {
+ throw t;
+ } catch (final Throwable t) {
+ throw new RuntimeException(t);
+ }
}
@Override
public MethodHandle getGetter(final Class<?> type) {
//this returns a getter on the format (Accessors, Object receiver)
- return Lookup.filterReturnType(INVOKE_GETTER_ACCESSOR, type);
+ return Lookup.filterReturnType(INVOKE_OBJECT_GETTER, type);
}
@Override
public MethodHandle getOptimisticGetter(final Class<?> type, final int programPoint) {
- //fortype is always object, but in the optimistic world we have to throw
- //unwarranted optimism exception for narrower types. We can improve this
- //by checking for boxed types and unboxing them, but it is doubtful that
- //this gives us any performance, as UserAccessorProperties are typically not
- //primitives. Are there? TODO: investigate later. For now we just throw an
- //exception for narrower types than object
-
- if (type.isPrimitive()) {
- final MethodHandle getter = getGetter(Object.class);
- final MethodHandle mh =
- MH.asType(
- MH.filterReturnValue(
- getter,
- MH.insertArguments(
- CONVERT_OBJECT_OPTIMISTIC.get(getAccessorTypeIndex(type)),
- 1,
- programPoint)),
- getter.type().changeReturnType(type));
-
- return mh;
+ if (type == int.class) {
+ return INVOKE_INT_GETTER;
+ } else if (type == long.class) {
+ return INVOKE_LONG_GETTER;
+ } else if (type == double.class) {
+ return INVOKE_NUMBER_GETTER;
+ } else {
+ assert type == Object.class;
+ return INVOKE_OBJECT_GETTER;
}
-
- assert type == Object.class;
- return getGetter(type);
}
@Override
@@ -259,7 +248,16 @@ public final class UserAccessorProperty extends SpillProperty {
@Override
public MethodHandle getSetter(final Class<?> type, final PropertyMap currentMap) {
- return INVOKE_SETTER_ACCESSOR;
+ if (type == int.class) {
+ return INVOKE_INT_SETTER;
+ } else if (type == long.class) {
+ return INVOKE_LONG_SETTER;
+ } else if (type == double.class) {
+ return INVOKE_NUMBER_SETTER;
+ } else {
+ assert type == Object.class;
+ return INVOKE_OBJECT_SETTER;
+ }
}
@Override
@@ -282,31 +280,81 @@ public final class UserAccessorProperty extends SpillProperty {
// getter/setter may be inherited. If so, proto is bound during lookup. In either
// inherited or self case, slot is also bound during lookup. Actual ScriptFunction
// to be called is retrieved everytime and applied.
- private static Object invokeGetterAccessor(final Accessors gs, final Object self) {
+ @SuppressWarnings("unused")
+ private static Object invokeObjectGetter(final Accessors gs, final MethodHandle invoker, final Object self) throws Throwable {
final Object func = gs.getter;
if (func instanceof ScriptFunction) {
- try {
- return getINVOKE_UA_GETTER().invokeExact(func, self);
- } catch (final Error | RuntimeException t) {
- throw t;
- } catch (final Throwable t) {
- throw new RuntimeException(t);
- }
+ return invoker.invokeExact(func, self);
}
return UNDEFINED;
}
- private static void invokeSetterAccessor(final Accessors gs, final String name, final Object self, final Object value) {
+ @SuppressWarnings("unused")
+ private static int invokeIntGetter(final Accessors gs, final MethodHandle invoker, final int programPoint, final Object self) throws Throwable {
+ final Object func = gs.getter;
+ if (func instanceof ScriptFunction) {
+ return (int) invoker.invokeExact(func, self);
+ }
+
+ throw new UnwarrantedOptimismException(UNDEFINED, programPoint);
+ }
+
+ @SuppressWarnings("unused")
+ private static long invokeLongGetter(final Accessors gs, final MethodHandle invoker, final int programPoint, final Object self) throws Throwable {
+ final Object func = gs.getter;
+ if (func instanceof ScriptFunction) {
+ return (long) invoker.invokeExact(func, self);
+ }
+
+ throw new UnwarrantedOptimismException(UNDEFINED, programPoint);
+ }
+
+ @SuppressWarnings("unused")
+ private static double invokeNumberGetter(final Accessors gs, final MethodHandle invoker, final int programPoint, final Object self) throws Throwable {
+ final Object func = gs.getter;
+ if (func instanceof ScriptFunction) {
+ return (double) invoker.invokeExact(func, self);
+ }
+
+ throw new UnwarrantedOptimismException(UNDEFINED, programPoint);
+ }
+
+ @SuppressWarnings("unused")
+ private static void invokeObjectSetter(final Accessors gs, final MethodHandle invoker, final String name, final Object self, final Object value) throws Throwable {
+ final Object func = gs.setter;
+ if (func instanceof ScriptFunction) {
+ invoker.invokeExact(func, self, value);
+ } else if (name != null) {
+ throw typeError("property.has.no.setter", name, ScriptRuntime.safeToString(self));
+ }
+ }
+
+ @SuppressWarnings("unused")
+ private static void invokeIntSetter(final Accessors gs, final MethodHandle invoker, final String name, final Object self, final int value) throws Throwable {
+ final Object func = gs.setter;
+ if (func instanceof ScriptFunction) {
+ invoker.invokeExact(func, self, value);
+ } else if (name != null) {
+ throw typeError("property.has.no.setter", name, ScriptRuntime.safeToString(self));
+ }
+ }
+
+ @SuppressWarnings("unused")
+ private static void invokeLongSetter(final Accessors gs, final MethodHandle invoker, final String name, final Object self, final long value) throws Throwable {
+ final Object func = gs.setter;
+ if (func instanceof ScriptFunction) {
+ invoker.invokeExact(func, self, value);
+ } else if (name != null) {
+ throw typeError("property.has.no.setter", name, ScriptRuntime.safeToString(self));
+ }
+ }
+
+ @SuppressWarnings("unused")
+ private static void invokeNumberSetter(final Accessors gs, final MethodHandle invoker, final String name, final Object self, final double value) throws Throwable {
final Object func = gs.setter;
if (func instanceof ScriptFunction) {
- try {
- getINVOKE_UA_SETTER().invokeExact(func, self, value);
- } catch (final Error | RuntimeException t) {
- throw t;
- } catch (final Throwable t) {
- throw new RuntimeException(t);
- }
+ invoker.invokeExact(func, self, value);
} else if (name != null) {
throw typeError("property.has.no.setter", name, ScriptRuntime.safeToString(self));
}
diff --git a/src/jdk/nashorn/internal/runtime/arrays/AnyElements.java b/src/jdk/nashorn/internal/runtime/arrays/AnyElements.java
new file mode 100644
index 00000000..d9e01923
--- /dev/null
+++ b/src/jdk/nashorn/internal/runtime/arrays/AnyElements.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.nashorn.internal.runtime.arrays;
+
+/**
+ * Marker interface for any ContinuousArray with any elements
+ * Used for type checks that throw ClassCastExceptions and force relinks
+ * for fast NativeArray specializations of builtin methods
+ */
+public interface AnyElements {
+ /**
+ * Return a numeric weight of the element type - wider is higher
+ * @return element type weight
+ */
+ public int getElementWeight();
+} \ No newline at end of file
diff --git a/src/jdk/nashorn/internal/runtime/arrays/ArrayData.java b/src/jdk/nashorn/internal/runtime/arrays/ArrayData.java
index 9e606ee6..f0a8c7a2 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/ArrayData.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/ArrayData.java
@@ -28,7 +28,11 @@ package jdk.nashorn.internal.runtime.arrays;
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
+import java.lang.reflect.Array;
import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
@@ -37,6 +41,7 @@ import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.objects.Global;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.PropertyDescriptor;
+import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.UnwarrantedOptimismException;
/**
@@ -49,15 +54,18 @@ public abstract class ArrayData {
/** Mask for getting a chunk */
protected static final int CHUNK_MASK = CHUNK_SIZE - 1;
- /**
- * Immutable empty array to get ScriptObjects started.
- */
- public static final ArrayData EMPTY_ARRAY = new NoTypeArrayData();
+ /** Untouched data - still link callsites as IntArrayData, but expands to
+ * a proper ArrayData when we try to write to it */
+ public static final ArrayData EMPTY_ARRAY = new UntouchedArrayData();
/**
* Length of the array data. Not necessarily length of the wrapped array.
+ * This is private to ensure that no one in a subclass is able to touch the length
+ * without going through {@link #setLength}. This is used to implement
+ * {@link LengthNotWritableFilter}s, ensuring that there are no ways past
+ * a {@link #setLength} function replaced by a nop
*/
- protected long length;
+ private long length;
/**
* Method handle to throw an {@link UnwarrantedOptimismException} when getting an element
@@ -66,6 +74,178 @@ public abstract class ArrayData {
protected static final CompilerConstants.Call THROW_UNWARRANTED = staticCall(MethodHandles.lookup(), ArrayData.class, "throwUnwarranted", void.class, ArrayData.class, int.class, int.class);
/**
+ * Immutable empty array to get ScriptObjects started.
+ * Use the same array and convert it to mutable as soon as it is modified
+ */
+ private static class UntouchedArrayData extends ContinuousArrayData {
+ private UntouchedArrayData() {
+ super(0);
+ }
+
+ private ArrayData toRealArrayData() {
+ return toRealArrayData(0);
+ }
+
+ private ArrayData toRealArrayData(final int index) {
+ final IntArrayData newData = new IntArrayData(index + 1);
+ if (index == 0) {
+ return newData;
+ }
+ return new DeletedRangeArrayFilter(newData, 0, index);
+ }
+
+ @Override
+ public ContinuousArrayData copy() {
+ assert length() == 0;
+ return this;
+ }
+
+ @Override
+ public Object asArrayOfType(final Class<?> componentType) {
+ return Array.newInstance(componentType, 0);
+ }
+
+ @Override
+ public Object[] asObjectArray() {
+ return ScriptRuntime.EMPTY_ARRAY;
+ }
+
+ @Override
+ public ArrayData ensure(final long safeIndex) {
+ if (safeIndex > 0L) {
+ if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH) {
+ return new SparseArrayData(this, safeIndex + 1);
+ }
+ //known to fit in int
+ return toRealArrayData((int)safeIndex).ensure(safeIndex);
+ }
+ return this;
+ }
+
+ @Override
+ public ArrayData convert(final Class<?> type) {
+ return toRealArrayData(0).convert(type);
+ }
+
+ @Override
+ public ArrayData delete(final int index) {
+ return new DeletedRangeArrayFilter(this, index, index);
+ }
+
+ @Override
+ public ArrayData delete(final long fromIndex, final long toIndex) {
+ return new DeletedRangeArrayFilter(this, fromIndex, toIndex);
+ }
+
+ @Override
+ public void shiftLeft(final int by) {
+ //nop, always empty or we wouldn't be of this class
+ }
+
+ @Override
+ public ArrayData shiftRight(final int by) {
+ return this; //always empty or we wouldn't be of this class
+ }
+
+ @Override
+ public ArrayData shrink(final long newLength) {
+ return this;
+ }
+
+ @Override
+ public ArrayData set(final int index, final Object value, final boolean strict) {
+ return toRealArrayData(index).set(index, value, strict);
+ }
+
+ @Override
+ public ArrayData set(final int index, final int value, final boolean strict) {
+ return toRealArrayData(index).set(index, value, strict);
+ }
+
+ @Override
+ public ArrayData set(final int index, final long value, final boolean strict) {
+ return toRealArrayData(index).set(index, value, strict);
+ }
+
+ @Override
+ public ArrayData set(final int index, final double value, final boolean strict) {
+ return toRealArrayData(index).set(index, value, strict);
+ }
+
+ @Override
+ public int getInt(final int index) {
+ throw new ArrayIndexOutOfBoundsException(index); //empty
+ }
+
+ @Override
+ public long getLong(final int index) {
+ throw new ArrayIndexOutOfBoundsException(index); //empty
+ }
+
+ @Override
+ public double getDouble(final int index) {
+ throw new ArrayIndexOutOfBoundsException(index); //empty
+ }
+
+ @Override
+ public Object getObject(final int index) {
+ throw new ArrayIndexOutOfBoundsException(index); //empty
+ }
+
+ @Override
+ public boolean has(final int index) {
+ return false; //empty
+ }
+
+ @Override
+ public Object pop() {
+ return ScriptRuntime.UNDEFINED;
+ }
+
+ @Override
+ public ArrayData push(final boolean strict, final Object item) {
+ return toRealArrayData().push(strict, item);
+ }
+
+ @Override
+ public ArrayData slice(final long from, final long to) {
+ return this; //empty
+ }
+
+ @Override
+ public ContinuousArrayData fastConcat(final ContinuousArrayData otherData) {
+ return otherData.copy();
+ }
+
+ //no need to override fastPopInt, as the default behavior is to throw classcast exception so we
+ //can relink and return an undefined, this is the IntArrayData default behavior
+ @Override
+ public String toString() {
+ return getClass().getSimpleName();
+ }
+
+ @Override
+ public MethodHandle getElementGetter(final Class<?> returnType, final int programPoint) {
+ return null;
+ }
+
+ @Override
+ public MethodHandle getElementSetter(final Class<?> elementType) {
+ return null;
+ }
+
+ @Override
+ public Class<?> getElementType() {
+ return int.class;
+ }
+
+ @Override
+ public Class<?> getBoxedElementType() {
+ return Integer.class;
+ }
+ }
+
+ /**
* Constructor
* @param length Virtual length of the array.
*/
@@ -77,7 +257,7 @@ public abstract class ArrayData {
* Factory method for unspecified array - start as int
* @return ArrayData
*/
- public static ArrayData initialArray() {
+ public final static ArrayData initialArray() {
return new IntArrayData();
}
@@ -92,24 +272,13 @@ public abstract class ArrayData {
throw new UnwarrantedOptimismException(data.getObject(index), programPoint);
}
- private static int alignUp(final int size) {
- return size + CHUNK_SIZE - 1 & ~(CHUNK_SIZE - 1);
- }
-
- /**
- * Generic invalidation hook for script object to have call sites to this array indexing
- * relinked, e.g. when a native array is marked as non extensible
- */
- public void invalidateGetters() {
- //subclass responsibility
- }
-
/**
- * Generic invalidation hook for script object to have call sites to this array indexing
- * relinked, e.g. when a native array is marked as non extensible
+ * Align an array size up to the nearest array chunk size
+ * @param size size required
+ * @return size given, always >= size
*/
- public void invalidateSetters() {
- //subclass responsibility
+ protected final static int alignUp(final int size) {
+ return size + CHUNK_SIZE - 1 & ~(CHUNK_SIZE - 1);
}
/**
@@ -118,7 +287,7 @@ public abstract class ArrayData {
* @param length the initial length
* @return ArrayData
*/
- public static ArrayData allocate(final int length) {
+ public static final ArrayData allocate(final int length) {
if (length == 0) {
return new IntArrayData();
} else if (length >= SparseArrayData.MAX_DENSE_LENGTH) {
@@ -134,7 +303,7 @@ public abstract class ArrayData {
* @param array the array
* @return ArrayData wrapping this array
*/
- public static ArrayData allocate(final Object array) {
+ public static final ArrayData allocate(final Object array) {
final Class<?> clazz = array.getClass();
if (clazz == int[].class) {
@@ -154,7 +323,7 @@ public abstract class ArrayData {
* @param array the array to use for initial elements
* @return the ArrayData
*/
- public static ArrayData allocate(final int[] array) {
+ public static final ArrayData allocate(final int[] array) {
return new IntArrayData(array, array.length);
}
@@ -164,7 +333,7 @@ public abstract class ArrayData {
* @param array the array to use for initial elements
* @return the ArrayData
*/
- public static ArrayData allocate(final long[] array) {
+ public static final ArrayData allocate(final long[] array) {
return new LongArrayData(array, array.length);
}
@@ -174,7 +343,7 @@ public abstract class ArrayData {
* @param array the array to use for initial elements
* @return the ArrayData
*/
- public static ArrayData allocate(final double[] array) {
+ public static final ArrayData allocate(final double[] array) {
return new NumberArrayData(array, array.length);
}
@@ -184,7 +353,7 @@ public abstract class ArrayData {
* @param array the array to use for initial elements
* @return the ArrayData
*/
- public static ArrayData allocate(final Object[] array) {
+ public static final ArrayData allocate(final Object[] array) {
return new ObjectArrayData(array, array.length);
}
@@ -194,7 +363,7 @@ public abstract class ArrayData {
* @param buf the nio ByteBuffer to wrap
* @return the ArrayData
*/
- public static ArrayData allocate(final ByteBuffer buf) {
+ public static final ArrayData allocate(final ByteBuffer buf) {
return new ByteBufferArrayData(buf);
}
@@ -204,7 +373,7 @@ public abstract class ArrayData {
* @param underlying the underlying ArrayData to wrap in the freeze filter
* @return the frozen ArrayData
*/
- public static ArrayData freeze(final ArrayData underlying) {
+ public static final ArrayData freeze(final ArrayData underlying) {
return new FrozenArrayFilter(underlying);
}
@@ -214,11 +383,31 @@ public abstract class ArrayData {
* @param underlying the underlying ArrayData to wrap in the seal filter
* @return the sealed ArrayData
*/
- public static ArrayData seal(final ArrayData underlying) {
+ public static final ArrayData seal(final ArrayData underlying) {
return new SealedArrayFilter(underlying);
}
/**
+ * Prevent this array from being extended
+ *
+ * @param underlying the underlying ArrayData to wrap in the non extensible filter
+ * @return new array data, filtered
+ */
+ public static final ArrayData preventExtension(final ArrayData underlying) {
+ return new NonExtensibleArrayFilter(underlying);
+ }
+
+ /**
+ * Prevent this array from having its length reset
+ *
+ * @param underlying the underlying ArrayDAta to wrap in the non extensible filter
+ * @return new array data, filtered
+ */
+ public static final ArrayData setIsLengthNotWritable(final ArrayData underlying) {
+ return new LengthNotWritableFilter(underlying);
+ }
+
+ /**
* Return the length of the array data. This may differ from the actual
* length of the array this wraps as length may be set or gotten as any
* other JavaScript Property
@@ -271,6 +460,22 @@ public abstract class ArrayData {
}
/**
+ * Increase length by 1
+ * @return the new length, not the old one (i.e. pre-increment)
+ */
+ protected final long increaseLength() {
+ return ++this.length;
+ }
+
+ /**
+ * Decrease length by 1.
+ * @return the new length, not the old one (i.e. pre-decrement)
+ */
+ protected final long decreaseLength() {
+ return --this.length;
+ }
+
+ /**
* Shift the array data left
*
* TODO: explore start at an index and not at zero, to make these operations
@@ -279,7 +484,7 @@ public abstract class ArrayData {
*
* @param by offset to shift
*/
- public abstract void shiftLeft(int by);
+ public abstract void shiftLeft(final int by);
/**
* Shift the array right
@@ -288,7 +493,7 @@ public abstract class ArrayData {
* @return New arraydata (or same)
*/
- public abstract ArrayData shiftRight(int by);
+ public abstract ArrayData shiftRight(final int by);
/**
* Ensure that the given index exists and won't fail subsequent
@@ -296,7 +501,7 @@ public abstract class ArrayData {
* @param safeIndex the index to ensure wont go out of bounds
* @return new array data (or same)
*/
- public abstract ArrayData ensure(long safeIndex);
+ public abstract ArrayData ensure(final long safeIndex);
/**
* Shrink the array to a new length, may or may not retain the
@@ -306,7 +511,7 @@ public abstract class ArrayData {
*
* @return new array data (or same)
*/
- public abstract ArrayData shrink(long newLength);
+ public abstract ArrayData shrink(final long newLength);
/**
* Set an object value at a given index
@@ -316,7 +521,7 @@ public abstract class ArrayData {
* @param strict are we in strict mode
* @return new array data (or same)
*/
- public abstract ArrayData set(int index, Object value, boolean strict);
+ public abstract ArrayData set(final int index, final Object value, final boolean strict);
/**
* Set an int value at a given index
@@ -326,7 +531,7 @@ public abstract class ArrayData {
* @param strict are we in strict mode
* @return new array data (or same)
*/
- public abstract ArrayData set(int index, int value, boolean strict);
+ public abstract ArrayData set(final int index, final int value, final boolean strict);
/**
* Set a long value at a given index
@@ -336,7 +541,7 @@ public abstract class ArrayData {
* @param strict are we in strict mode
* @return new array data (or same)
*/
- public abstract ArrayData set(int index, long value, boolean strict);
+ public abstract ArrayData set(final int index, final long value, final boolean strict);
/**
* Set an double value at a given index
@@ -346,7 +551,7 @@ public abstract class ArrayData {
* @param strict are we in strict mode
* @return new array data (or same)
*/
- public abstract ArrayData set(int index, double value, boolean strict);
+ public abstract ArrayData set(final int index, final double value, final boolean strict);
/**
* Set an empty value at a given index. Should only affect Object array.
@@ -377,7 +582,7 @@ public abstract class ArrayData {
* @param index the index
* @return the value
*/
- public abstract int getInt(int index);
+ public abstract int getInt(final int index);
/**
* Returns the optimistic type of this array data. Basically, when an array data object needs to throw an
@@ -406,7 +611,7 @@ public abstract class ArrayData {
* @param index the index
* @return the value
*/
- public abstract long getLong(int index);
+ public abstract long getLong(final int index);
/**
* Get optimistic long - default is that it's impossible. Overridden
@@ -426,7 +631,7 @@ public abstract class ArrayData {
* @param index the index
* @return the value
*/
- public abstract double getDouble(int index);
+ public abstract double getDouble(final int index);
/**
* Get optimistic double - default is that it's impossible. Overridden
@@ -446,14 +651,14 @@ public abstract class ArrayData {
* @param index the index
* @return the value
*/
- public abstract Object getObject(int index);
+ public abstract Object getObject(final int index);
/**
* Tests to see if an entry exists (avoids boxing.)
* @param index the index
* @return true if entry exists
*/
- public abstract boolean has(int index);
+ public abstract boolean has(final int index);
/**
* Returns if element at specific index can be deleted or not.
@@ -499,7 +704,7 @@ public abstract class ArrayData {
* @param index the index
* @return new array data (or same)
*/
- public abstract ArrayData delete(int index);
+ public abstract ArrayData delete(final int index);
/**
* Delete a given range from this array;
@@ -509,7 +714,7 @@ public abstract class ArrayData {
*
* @return new ArrayData after deletion
*/
- public abstract ArrayData delete(long fromIndex, long toIndex);
+ public abstract ArrayData delete(final long fromIndex, final long toIndex);
/**
* Convert the ArrayData to one with a different element type
@@ -519,7 +724,7 @@ public abstract class ArrayData {
* @param type new element type
* @return new array data
*/
- public abstract ArrayData convert(Class<?> type);
+ public abstract ArrayData convert(final Class<?> type);
/**
* Push an array of items to the end of the array
@@ -603,7 +808,7 @@ public abstract class ArrayData {
* @param to end index + 1
* @return new array data
*/
- public abstract ArrayData slice(long from, long to);
+ public abstract ArrayData slice(final long from, final long to);
/**
* Fast splice operation. This just modifies the array according to the number of
@@ -648,6 +853,34 @@ public abstract class ArrayData {
}
/**
+ * Return a list of keys in the array for the iterators
+ * @return iterator key list
+ */
+ protected List<Long> computeIteratorKeys() {
+ final List<Long> keys = new ArrayList<>();
+
+ final long len = length();
+ for (long i = 0L; i < len; i = nextIndex(i)) {
+ if (has((int)i)) {
+ keys.add(i);
+ }
+ }
+
+ return keys;
+ }
+
+ /**
+ * Return an iterator that goes through all indexes of elements
+ * in this array. This includes those after array.length if
+ * they exist
+ *
+ * @return iterator
+ */
+ public Iterator<Long> indexIterator() {
+ return computeIteratorKeys().iterator();
+ }
+
+ /**
* Exponential growth function for array size when in
* need of resizing.
*
@@ -666,7 +899,7 @@ public abstract class ArrayData {
*
* @return the next index
*/
- public long nextIndex(final long index) {
+ long nextIndex(final long index) {
return index + 1;
}
@@ -728,5 +961,4 @@ public abstract class ArrayData {
public GuardedInvocation findFastSetIndexMethod(final Class<? extends ArrayData> clazz, final CallSiteDescriptor desc, final LinkRequest request) { // array, index, value
return null;
}
-
}
diff --git a/src/jdk/nashorn/internal/runtime/arrays/ArrayFilter.java b/src/jdk/nashorn/internal/runtime/arrays/ArrayFilter.java
index 8d71cc04..9c24a9bf 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/ArrayFilter.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/ArrayFilter.java
@@ -39,7 +39,7 @@ abstract class ArrayFilter extends ArrayData {
protected ArrayData underlying;
ArrayFilter(final ArrayData underlying) {
- super(underlying.length);
+ super(underlying.length());
this.underlying = underlying;
}
@@ -70,62 +70,55 @@ abstract class ArrayFilter extends ArrayData {
@Override
public void shiftLeft(final int by) {
underlying.shiftLeft(by);
- setLength(underlying.length);
+ setLength(underlying.length());
}
@Override
public ArrayData shiftRight(final int by) {
underlying = underlying.shiftRight(by);
- setLength(underlying.length);
-
+ setLength(underlying.length());
return this;
}
@Override
public ArrayData ensure(final long safeIndex) {
underlying = underlying.ensure(safeIndex);
- setLength(underlying.length);
-
+ setLength(underlying.length());
return this;
}
@Override
public ArrayData shrink(final long newLength) {
underlying = underlying.shrink(newLength);
- setLength(underlying.length);
-
+ setLength(underlying.length());
return this;
}
@Override
public ArrayData set(final int index, final Object value, final boolean strict) {
underlying = underlying.set(index, value, strict);
- setLength(underlying.length);
-
+ setLength(underlying.length());
return this;
}
@Override
public ArrayData set(final int index, final int value, final boolean strict) {
underlying = underlying.set(index, value, strict);
- setLength(underlying.length);
-
+ setLength(underlying.length());
return this;
}
@Override
public ArrayData set(final int index, final long value, final boolean strict) {
underlying = underlying.set(index, value, strict);
- setLength(underlying.length);
-
+ setLength(underlying.length());
return this;
}
@Override
public ArrayData set(final int index, final double value, final boolean strict) {
underlying = underlying.set(index, value, strict);
- setLength(underlying.length);
-
+ setLength(underlying.length());
return this;
}
@@ -189,29 +182,28 @@ abstract class ArrayFilter extends ArrayData {
@Override
public ArrayData delete(final int index) {
underlying = underlying.delete(index);
- setLength(underlying.length);
+ setLength(underlying.length());
return this;
}
@Override
public ArrayData delete(final long from, final long to) {
underlying = underlying.delete(from, to);
- setLength(underlying.length);
+ setLength(underlying.length());
return this;
}
@Override
public ArrayData convert(final Class<?> type) {
underlying = underlying.convert(type);
- setLength(underlying.length);
+ setLength(underlying.length());
return this;
}
@Override
public Object pop() {
final Object value = underlying.pop();
- setLength(underlying.length);
-
+ setLength(underlying.length());
return value;
}
diff --git a/src/jdk/nashorn/internal/runtime/arrays/ArrayIndex.java b/src/jdk/nashorn/internal/runtime/arrays/ArrayIndex.java
index 5c857e11..f979aaa9 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/ArrayIndex.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/ArrayIndex.java
@@ -182,15 +182,15 @@ public final class ArrayIndex {
}
/**
- * Convert an index to a long value. This basically amounts to ANDing it
- * with {@link JSType#MAX_UINT}, as the maximum array index in JavaScript
+ * Convert an index to a long value. This basically amounts to converting it into a
+ * {@link JSType#toUint32(int)} uint32} as the maximum array index in JavaScript
* is 0xfffffffe
*
* @param index index to convert to long form
* @return index as uint32 in a long
*/
public static long toLongIndex(final int index) {
- return index & JSType.MAX_UINT;
+ return JSType.toUint32(index);
}
/**
@@ -201,7 +201,7 @@ public final class ArrayIndex {
* @return index as string
*/
public static String toKey(final int index) {
- return Long.toString(index & JSType.MAX_UINT);
+ return Long.toString(JSType.toUint32(index));
}
}
diff --git a/src/jdk/nashorn/internal/runtime/arrays/ContinuousArrayData.java b/src/jdk/nashorn/internal/runtime/arrays/ContinuousArrayData.java
index 8724d64a..2fdd184e 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/ContinuousArrayData.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/ContinuousArrayData.java
@@ -1,5 +1,4 @@
/*
- * 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
@@ -30,7 +29,6 @@ import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.JSType.getAccessorTypeIndex;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
-
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
@@ -50,9 +48,6 @@ import jdk.nashorn.internal.runtime.logging.Logger;
*/
@Logger(name="arrays")
public abstract class ContinuousArrayData extends ArrayData {
-
- private SwitchPoint sp;
-
/**
* Constructor
* @param length length (elementLength)
@@ -61,18 +56,6 @@ public abstract class ContinuousArrayData extends ArrayData {
super(length);
}
- private SwitchPoint ensureSwitchPointExists() {
- if (sp == null){
- sp = new SwitchPoint();
- }
- return sp;
- }
-
- @Override
- public void invalidateSetters() {
- SwitchPoint.invalidateAll(new SwitchPoint[] { ensureSwitchPointExists() });
- }
-
/**
* Check if we can put one more element at the end of this continous
* array without reallocating, or if we are overwriting an already
@@ -82,7 +65,15 @@ public abstract class ContinuousArrayData extends ArrayData {
* @return true if we don't need to do any array reallocation to fit an element at index
*/
public final boolean hasRoomFor(final int index) {
- return has(index) || (index == length && ensure(index) == this);
+ return has(index) || (index == length() && ensure(index) == this);
+ }
+
+ /**
+ * Check if an arraydata is empty
+ * @return true if empty
+ */
+ public boolean isEmpty() {
+ return length() == 0L;
}
/**
@@ -109,13 +100,16 @@ public abstract class ContinuousArrayData extends ArrayData {
* @param index index to check - currently only int indexes
* @return index
*/
- protected int throwHas(final int index) {
+ protected final int throwHas(final int index) {
if (!has(index)) {
throw new ClassCastException();
}
return index;
}
+ @Override
+ public abstract ContinuousArrayData copy();
+
/**
* Returns the type used to store an element in this array
* @return element type
@@ -128,6 +122,25 @@ public abstract class ContinuousArrayData extends ArrayData {
}
/**
+ * Returns the boxed type of the type used to store an element in this array
+ * @return element type
+ */
+ public abstract Class<?> getBoxedElementType();
+
+ /**
+ * Get the widest element type of two arrays. This can be done faster in subclasses, but
+ * this works for all ContinuousArrayDatas and for where more optimal checks haven't been
+ * implemented.
+ *
+ * @param otherData another ContinuousArrayData
+ * @return the widest boxed element type
+ */
+ public ContinuousArrayData widest(final ContinuousArrayData otherData) {
+ final Class<?> elementType = getElementType();
+ return Type.widest(elementType, otherData.getElementType()) == elementType ? this : otherData;
+ }
+
+ /**
* Look up a continuous array element getter
* @param get getter, sometimes combined with a has check that throws CCE on failure for relink
* @param returnType return type
@@ -256,12 +269,7 @@ public abstract class ContinuousArrayData extends ArrayData {
final Object[] args = request.getArguments();
final int index = (int)args[args.length - 2];
- //sp may be invalidated by e.g. preventExtensions before the first setter is linked
- //then it is already created. otherwise, create it here to guard against future
- //invalidations
- ensureSwitchPointExists();
-
- if (!sp.hasBeenInvalidated() && hasRoomFor(index)) {
+ if (hasRoomFor(index)) {
MethodHandle setElement = getElementSetter(elementType); //Z(continuousarraydata, int, int), return true if successful
if (setElement != null) {
//else we are dealing with a wider type than supported by this callsite
@@ -269,7 +277,7 @@ public abstract class ContinuousArrayData extends ArrayData {
getArray = MH.asType(getArray, getArray.type().changeReturnType(getClass()));
setElement = MH.filterArguments(setElement, 0, getArray);
final MethodHandle guard = MH.insertArguments(FAST_ACCESS_GUARD, 0, clazz);
- return new GuardedInvocation(setElement, guard, sp, ClassCastException.class); //CCE if not a scriptObject anymore
+ return new GuardedInvocation(setElement, guard, (SwitchPoint)null, ClassCastException.class); //CCE if not a scriptObject anymore
}
}
}
@@ -344,4 +352,13 @@ public abstract class ContinuousArrayData extends ArrayData {
public Object fastPopObject() {
throw new ClassCastException(String.valueOf(getClass())); //type is wrong, relink
}
+
+ /**
+ * Specialization - fast concat implementation
+ * @param otherData data to concat
+ * @return new arraydata
+ */
+ public ContinuousArrayData fastConcat(final ContinuousArrayData otherData) {
+ throw new ClassCastException(String.valueOf(getClass()) + " != " + String.valueOf(otherData.getClass()));
+ }
}
diff --git a/src/jdk/nashorn/internal/runtime/arrays/DeletedArrayFilter.java b/src/jdk/nashorn/internal/runtime/arrays/DeletedArrayFilter.java
index 4f54b639..4fa89f7d 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/DeletedArrayFilter.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/DeletedArrayFilter.java
@@ -38,8 +38,7 @@ final class DeletedArrayFilter extends ArrayFilter {
DeletedArrayFilter(final ArrayData underlying) {
super(underlying);
-
- this.deleted = new BitVector(underlying.length);
+ this.deleted = new BitVector(underlying.length());
}
@Override
@@ -79,25 +78,24 @@ final class DeletedArrayFilter extends ArrayFilter {
@Override
public void shiftLeft(final int by) {
super.shiftLeft(by);
- deleted.shiftLeft(by, length);
+ deleted.shiftLeft(by, length());
}
@Override
public ArrayData shiftRight(final int by) {
super.shiftRight(by);
- deleted.shiftRight(by, length);
-
+ deleted.shiftRight(by, length());
return this;
}
@Override
public ArrayData ensure(final long safeIndex) {
- if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH && safeIndex >= length) {
+ if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH && safeIndex >= length()) {
return new SparseArrayData(this, safeIndex + 1);
}
super.ensure(safeIndex);
- deleted.resize(length);
+ deleted.resize(length());
return this;
}
@@ -105,36 +103,31 @@ final class DeletedArrayFilter extends ArrayFilter {
@Override
public ArrayData shrink(final long newLength) {
super.shrink(newLength);
- deleted.resize(length);
-
+ deleted.resize(length());
return this;
}
@Override
public ArrayData set(final int index, final Object value, final boolean strict) {
deleted.clear(ArrayIndex.toLongIndex(index));
-
return super.set(index, value, strict);
}
@Override
public ArrayData set(final int index, final int value, final boolean strict) {
deleted.clear(ArrayIndex.toLongIndex(index));
-
return super.set(index, value, strict);
}
@Override
public ArrayData set(final int index, final long value, final boolean strict) {
deleted.clear(ArrayIndex.toLongIndex(index));
-
return super.set(index, value, strict);
}
@Override
public ArrayData set(final int index, final double value, final boolean strict) {
deleted.clear(ArrayIndex.toLongIndex(index));
-
return super.set(index, value, strict);
}
@@ -146,7 +139,7 @@ final class DeletedArrayFilter extends ArrayFilter {
@Override
public ArrayData delete(final int index) {
final long longIndex = ArrayIndex.toLongIndex(index);
- assert longIndex >= 0 && longIndex < length;
+ assert longIndex >= 0 && longIndex < length();
deleted.set(longIndex);
underlying.setEmpty(index);
return this;
@@ -154,7 +147,7 @@ final class DeletedArrayFilter extends ArrayFilter {
@Override
public ArrayData delete(final long fromIndex, final long toIndex) {
- assert fromIndex >= 0 && fromIndex <= toIndex && toIndex < length;
+ assert fromIndex >= 0 && fromIndex <= toIndex && toIndex < length();
deleted.setRange(fromIndex, toIndex + 1);
underlying.setEmpty(fromIndex, toIndex);
return this;
@@ -162,7 +155,7 @@ final class DeletedArrayFilter extends ArrayFilter {
@Override
public Object pop() {
- final long index = length - 1;
+ final long index = length() - 1;
if (super.has((int)index)) {
final boolean isDeleted = deleted.isSet(index);
@@ -179,7 +172,7 @@ final class DeletedArrayFilter extends ArrayFilter {
final ArrayData newArray = underlying.slice(from, to);
final DeletedArrayFilter newFilter = new DeletedArrayFilter(newArray);
newFilter.getDeleted().copy(deleted);
- newFilter.getDeleted().shiftLeft(from, newFilter.length);
+ newFilter.getDeleted().shiftLeft(from, newFilter.length());
return newFilter;
}
diff --git a/src/jdk/nashorn/internal/runtime/arrays/DeletedRangeArrayFilter.java b/src/jdk/nashorn/internal/runtime/arrays/DeletedRangeArrayFilter.java
index cd5cadb9..953b9213 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/DeletedRangeArrayFilter.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/DeletedRangeArrayFilter.java
@@ -42,10 +42,10 @@ final class DeletedRangeArrayFilter extends ArrayFilter {
}
private static ArrayData maybeSparse(final ArrayData underlying, final long hi) {
- if(hi < SparseArrayData.MAX_DENSE_LENGTH || underlying instanceof SparseArrayData) {
+ if (hi < SparseArrayData.MAX_DENSE_LENGTH || underlying instanceof SparseArrayData) {
return underlying;
}
- return new SparseArrayData(underlying, underlying.length);
+ return new SparseArrayData(underlying, underlying.length());
}
private boolean isEmpty() {
@@ -93,7 +93,7 @@ final class DeletedRangeArrayFilter extends ArrayFilter {
@Override
public ArrayData ensure(final long safeIndex) {
- if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH && safeIndex >= length) {
+ if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH && safeIndex >= length()) {
return new SparseArrayData(this, safeIndex + 1);
}
@@ -110,7 +110,7 @@ final class DeletedRangeArrayFilter extends ArrayFilter {
@Override
public ArrayData shiftRight(final int by) {
super.shiftRight(by);
- final long len = length;
+ final long len = length();
lo = Math.min(len, lo + by);
hi = Math.min(len - 1, hi + by);
@@ -238,7 +238,7 @@ final class DeletedRangeArrayFilter extends ArrayFilter {
@Override
public Object pop() {
- final int index = (int)length - 1;
+ final int index = (int)length() - 1;
if (super.has(index)) {
final boolean isDeleted = isDeleted(index);
final Object value = super.pop();
diff --git a/src/jdk/nashorn/internal/runtime/arrays/FrozenArrayFilter.java b/src/jdk/nashorn/internal/runtime/arrays/FrozenArrayFilter.java
index ccf1d882..1e71b44d 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/FrozenArrayFilter.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/FrozenArrayFilter.java
@@ -26,9 +26,9 @@
package jdk.nashorn.internal.runtime.arrays;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
-
import jdk.nashorn.internal.objects.Global;
import jdk.nashorn.internal.runtime.PropertyDescriptor;
+import jdk.nashorn.internal.runtime.ScriptRuntime;
/**
* ArrayData after the array has been frozen by Object.freeze call.
@@ -79,4 +79,15 @@ final class FrozenArrayFilter extends SealedArrayFilter {
}
return this;
}
+
+ @Override
+ public ArrayData push(final boolean strict, final Object... items) {
+ return this; //nop
+ }
+
+ @Override
+ public Object pop() {
+ final int len = (int)underlying.length();
+ return len == 0 ? ScriptRuntime.UNDEFINED : underlying.getObject(len - 1);
+ }
}
diff --git a/src/jdk/nashorn/internal/runtime/arrays/IntArrayData.java b/src/jdk/nashorn/internal/runtime/arrays/IntArrayData.java
index 0792b6b6..381390ce 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/IntArrayData.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/IntArrayData.java
@@ -26,7 +26,6 @@
package jdk.nashorn.internal.runtime.arrays;
import static jdk.nashorn.internal.codegen.CompilerConstants.specialCall;
-
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.util.Arrays;
@@ -57,17 +56,32 @@ final class IntArrayData extends ContinuousArrayData implements IntElements {
* @param array an int array
* @param length a length, not necessarily array.length
*/
- IntArrayData(final int array[], final int length) {
+ IntArrayData(final int[] array, final int length) {
super(length);
- assert array.length >= length;
+ assert array == null || array.length >= length;
this.array = array;
}
@Override
- public Class<?> getElementType() {
+ public final Class<?> getElementType() {
return int.class;
}
+ @Override
+ public final Class<?> getBoxedElementType() {
+ return Integer.class;
+ }
+
+ @Override
+ public final int getElementWeight() {
+ return 1;
+ }
+
+ @Override
+ public final ContinuousArrayData widest(final ContinuousArrayData otherData) {
+ return otherData;
+ }
+
private static final MethodHandle HAS_GET_ELEM = specialCall(MethodHandles.lookup(), IntArrayData.class, "getElem", int.class, int.class).methodHandle();
private static final MethodHandle SET_ELEM = specialCall(MethodHandles.lookup(), IntArrayData.class, "setElem", void.class, int.class, int.class).methodHandle();
@@ -104,23 +118,25 @@ final class IntArrayData extends ContinuousArrayData implements IntElements {
}
@Override
- public ArrayData copy() {
- return new IntArrayData(array.clone(), (int)length);
+ public IntArrayData copy() {
+ return new IntArrayData(array.clone(), (int)length());
}
@Override
public Object asArrayOfType(final Class<?> componentType) {
if (componentType == int.class) {
- return array.length == length ? array.clone() : Arrays.copyOf(array, (int)length);
+ final int len = (int)length();
+ return array.length == len ? array.clone() : Arrays.copyOf(array, len);
}
return super.asArrayOfType(componentType);
}
private Object[] toObjectArray(final boolean trim) {
- assert length <= array.length : "length exceeds internal array size";
- final Object[] oarray = new Object[trim ? (int)length : array.length];
+ assert length() <= array.length : "length exceeds internal array size";
+ final int len = (int)length();
+ final Object[] oarray = new Object[trim ? len : array.length];
- for (int index = 0; index < length; index++) {
+ for (int index = 0; index < len; index++) {
oarray[index] = Integer.valueOf(array[index]);
}
@@ -128,10 +144,11 @@ final class IntArrayData extends ContinuousArrayData implements IntElements {
}
private double[] toDoubleArray() {
- assert length <= array.length : "length exceeds internal array size";
+ assert length() <= array.length : "length exceeds internal array size";
+ final int len = (int)length();
final double[] darray = new double[array.length];
- for (int index = 0; index < length; index++) {
+ for (int index = 0; index < len; index++) {
darray[index] = array[index];
}
@@ -139,10 +156,11 @@ final class IntArrayData extends ContinuousArrayData implements IntElements {
}
private long[] toLongArray() {
- assert length <= array.length : "length exceeds internal array size";
+ assert length() <= array.length : "length exceeds internal array size";
+ final int len = (int)length();
final long[] larray = new long[array.length];
- for (int index = 0; index < length; index++) {
+ for (int index = 0; index < len; index++) {
larray[index] = array[index];
}
@@ -150,23 +168,22 @@ final class IntArrayData extends ContinuousArrayData implements IntElements {
}
private LongArrayData convertToLong() {
- return new LongArrayData(toLongArray(), (int)length);
+ return new LongArrayData(toLongArray(), (int)length());
}
private NumberArrayData convertToDouble() {
- return new NumberArrayData(toDoubleArray(), (int)length);
+ return new NumberArrayData(toDoubleArray(), (int)length());
}
private ObjectArrayData convertToObject() {
- return new ObjectArrayData(toObjectArray(false), (int)length);
+ return new ObjectArrayData(toObjectArray(false), (int)length());
}
@Override
public ArrayData convert(final Class<?> type) {
if (type == Integer.class) {
return this;
- }
- if (type == Long.class) {
+ } else if (type == Long.class) {
return convertToLong();
} else if (type == Double.class) {
return convertToDouble();
@@ -183,7 +200,7 @@ final class IntArrayData extends ContinuousArrayData implements IntElements {
@Override
public ArrayData shiftRight(final int by) {
- final ArrayData newData = ensure(by + length - 1);
+ final ArrayData newData = ensure(by + length() - 1);
if (newData != this) {
newData.shiftRight(by);
return newData;
@@ -209,8 +226,7 @@ final class IntArrayData extends ContinuousArrayData implements IntElements {
@Override
public ArrayData shrink(final long newLength) {
- Arrays.fill(array, (int) newLength, array.length, 0);
-
+ Arrays.fill(array, (int)newLength, array.length, 0);
return this;
}
@@ -229,7 +245,7 @@ final class IntArrayData extends ContinuousArrayData implements IntElements {
@Override
public ArrayData set(final int index, final int value, final boolean strict) {
array[index] = value;
- setLength(Math.max(index + 1, length));
+ setLength(Math.max(index + 1, length()));
return this;
}
@@ -238,7 +254,7 @@ final class IntArrayData extends ContinuousArrayData implements IntElements {
public ArrayData set(final int index, final long value, final boolean strict) {
if (JSType.isRepresentableAsInt(value)) {
array[index] = JSType.toInt32(value);
- setLength(Math.max(index + 1, length));
+ setLength(Math.max(index + 1, length()));
return this;
}
@@ -249,7 +265,7 @@ final class IntArrayData extends ContinuousArrayData implements IntElements {
public ArrayData set(final int index, final double value, final boolean strict) {
if (JSType.isRepresentableAsInt(value)) {
array[index] = (int)(long)value;
- setLength(Math.max(index + 1, length));
+ setLength(Math.max(index + 1, length()));
return this;
}
@@ -293,7 +309,7 @@ final class IntArrayData extends ContinuousArrayData implements IntElements {
@Override
public boolean has(final int index) {
- return 0 <= index && index < length;
+ return 0 <= index && index < length();
}
@Override
@@ -308,11 +324,12 @@ final class IntArrayData extends ContinuousArrayData implements IntElements {
@Override
public Object pop() {
- if (length == 0) {
+ final int len = (int)length();
+ if (len == 0) {
return ScriptRuntime.UNDEFINED;
}
- final int newLength = (int)length - 1;
+ final int newLength = len - 1;
final int elem = array[newLength];
array[newLength] = 0;
setLength(newLength);
@@ -322,15 +339,12 @@ final class IntArrayData extends ContinuousArrayData implements IntElements {
@Override
public ArrayData slice(final long from, final long to) {
- final long start = from < 0 ? from + length : from;
- final long newLength = to - start;
-
- return new IntArrayData(Arrays.copyOfRange(array, (int)from, (int)to), (int)newLength);
+ return new IntArrayData(Arrays.copyOfRange(array, (int)from, (int)to), (int)(to - (from < 0 ? from + length() : from)));
}
@Override
public final ArrayData push(final boolean strict, final int item) {
- final long len = length;
+ final long len = length();
final ArrayData newData = ensure(len);
if (newData == this) {
array[(int)len] = item;
@@ -341,13 +355,19 @@ final class IntArrayData extends ContinuousArrayData implements IntElements {
@Override
public ArrayData fastSplice(final int start, final int removed, final int added) throws UnsupportedOperationException {
- final long oldLength = length;
+ final long oldLength = length();
final long newLength = oldLength - removed + added;
if (newLength > SparseArrayData.MAX_DENSE_LENGTH && newLength > array.length) {
throw new UnsupportedOperationException();
}
final ArrayData returnValue = removed == 0 ?
- EMPTY_ARRAY : new IntArrayData(Arrays.copyOfRange(array, start, start + removed), removed);
+ EMPTY_ARRAY :
+ new IntArrayData(
+ Arrays.copyOfRange(
+ array,
+ start,
+ start + removed),
+ removed);
if (newLength != oldLength) {
final int[] newArray;
@@ -369,21 +389,21 @@ final class IntArrayData extends ContinuousArrayData implements IntElements {
@Override
public long fastPush(final int arg) {
- final int len = (int)length;
+ final int len = (int)length();
if (len == array.length) {
array = Arrays.copyOf(array, nextSize(len));
}
array[len] = arg;
- return ++length;
+ return increaseLength();
}
//length must not be zero
@Override
public int fastPopInt() {
- if (length == 0) {
+ if (length() == 0) {
throw new ClassCastException(); //relink
}
- final int newLength = (int)--length;
+ final int newLength = (int)decreaseLength();
final int elem = array[newLength];
array[newLength] = 0;
return elem;
@@ -403,4 +423,26 @@ final class IntArrayData extends ContinuousArrayData implements IntElements {
public Object fastPopObject() {
return fastPopInt();
}
+
+ @Override
+ public ContinuousArrayData fastConcat(final ContinuousArrayData otherData) {
+ final int otherLength = (int)otherData.length();
+ final int thisLength = (int)length();
+ assert otherLength > 0 && thisLength > 0;
+
+ final int[] otherArray = ((IntArrayData)otherData).array;
+ final int newLength = otherLength + thisLength;
+ final int[] newArray = new int[ArrayData.alignUp(newLength)];
+
+ System.arraycopy(array, 0, newArray, 0, thisLength);
+ System.arraycopy(otherArray, 0, newArray, thisLength, otherLength);
+
+ return new IntArrayData(newArray, newLength);
+ }
+
+ @Override
+ public String toString() {
+ assert length() <= array.length : length() + " > " + array.length;
+ return getClass().getSimpleName() + ':' + Arrays.toString(Arrays.copyOf(array, (int)length()));
+ }
}
diff --git a/src/jdk/nashorn/internal/runtime/arrays/IteratorAction.java b/src/jdk/nashorn/internal/runtime/arrays/IteratorAction.java
index 244739b3..ff4c13e2 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/IteratorAction.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/IteratorAction.java
@@ -25,11 +25,7 @@
package jdk.nashorn.internal.runtime.arrays;
-import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
-
-import jdk.nashorn.api.scripting.JSObject;
import jdk.nashorn.internal.runtime.Context;
-import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.linker.Bootstrap;
@@ -98,17 +94,7 @@ public abstract class IteratorAction<T> {
* @return result of apply
*/
public final T apply() {
- final boolean strict;
- if (callbackfn instanceof ScriptFunction) {
- strict = ((ScriptFunction)callbackfn).isStrict();
- } else if (callbackfn instanceof JSObject &&
- ((JSObject)callbackfn).isFunction()) {
- strict = ((JSObject)callbackfn).isStrictFunction();
- } else if (Bootstrap.isDynamicMethod(callbackfn) || Bootstrap.isFunctionalInterfaceObject(callbackfn)) {
- strict = false;
- } else {
- throw typeError("not.a.function", ScriptRuntime.safeToString(callbackfn));
- }
+ final boolean strict = Bootstrap.isStrictCallable(callbackfn);
// for non-strict callback, need to translate undefined thisArg to be global object
thisArg = (thisArg == ScriptRuntime.UNDEFINED && !strict)? Context.getGlobal() : thisArg;
diff --git a/src/jdk/nashorn/internal/runtime/arrays/LengthNotWritableFilter.java b/src/jdk/nashorn/internal/runtime/arrays/LengthNotWritableFilter.java
new file mode 100644
index 00000000..945d80d2
--- /dev/null
+++ b/src/jdk/nashorn/internal/runtime/arrays/LengthNotWritableFilter.java
@@ -0,0 +1,198 @@
+package jdk.nashorn.internal.runtime.arrays;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import jdk.nashorn.internal.runtime.JSType;
+import jdk.nashorn.internal.runtime.ScriptRuntime;
+
+/**
+ * Filter to use for ArrayData where the length is not writable.
+ * The default behavior is just to ignore {@link ArrayData#setLength}
+ */
+final class LengthNotWritableFilter extends ArrayFilter {
+ private final SortedMap<Long, Object> extraElements; //elements with index >= length
+
+ /**
+ * Constructor
+ * @param underlying array
+ */
+ LengthNotWritableFilter(final ArrayData underlying) {
+ this(underlying, new TreeMap<Long, Object>());
+ }
+
+ private LengthNotWritableFilter(final ArrayData underlying, final SortedMap<Long, Object> extraElements) {
+ super(underlying);
+ this.extraElements = extraElements;
+ }
+
+ @Override
+ public ArrayData copy() {
+ return new LengthNotWritableFilter(underlying.copy(), new TreeMap<>(extraElements));
+ }
+
+ @Override
+ public boolean has(final int index) {
+ return super.has(index) || extraElements.containsKey((long)index);
+ }
+
+ /**
+ * Set the length of the data array
+ *
+ * @param length the new length for the data array
+ */
+ @Override
+ public void setLength(final long length) {
+ //empty - setting length for a LengthNotWritableFilter is always a nop
+ }
+
+ @Override
+ public ArrayData ensure(final long index) {
+ return this;
+ }
+
+ @Override
+ public ArrayData slice(final long from, final long to) {
+ //return array[from...to), or array[from...length] if undefined, in this case not as we are an ArrayData
+ return new LengthNotWritableFilter(underlying.slice(from, to), extraElements.subMap(from, to));
+ }
+
+ private boolean checkAdd(final long index, final Object value) {
+ if (index >= length()) {
+ extraElements.put(index, value);
+ return true;
+ }
+ return false;
+ }
+
+ private Object get(final long index) {
+ final Object obj = extraElements.get(index);
+ if (obj == null) {
+ return ScriptRuntime.UNDEFINED;
+ }
+ return obj;
+ }
+
+ @Override
+ public int getInt(final int index) {
+ if (index >= length()) {
+ return JSType.toInt32(get(index));
+ }
+ return underlying.getInt(index);
+ }
+
+ @Override
+ public int getIntOptimistic(final int index, final int programPoint) {
+ if (index >= length()) {
+ return JSType.toInt32Optimistic(get(index), programPoint);
+ }
+ return underlying.getIntOptimistic(index, programPoint);
+ }
+
+ @Override
+ public long getLong(final int index) {
+ if (index >= length()) {
+ return JSType.toLong(get(index));
+ }
+ return underlying.getLong(index);
+ }
+
+ @Override
+ public long getLongOptimistic(final int index, final int programPoint) {
+ if (index >= length()) {
+ return JSType.toLongOptimistic(get(index), programPoint);
+ }
+ return underlying.getLongOptimistic(index, programPoint);
+ }
+
+ @Override
+ public double getDouble(final int index) {
+ if (index >= length()) {
+ return JSType.toNumber(get(index));
+ }
+ return underlying.getDouble(index);
+ }
+
+ @Override
+ public double getDoubleOptimistic(final int index, final int programPoint) {
+ if (index >= length()) {
+ return JSType.toNumberOptimistic(get(index), programPoint);
+ }
+ return underlying.getDoubleOptimistic(index, programPoint);
+ }
+
+ @Override
+ public Object getObject(final int index) {
+ if (index >= length()) {
+ return get(index);
+ }
+ return underlying.getObject(index);
+ }
+
+ @Override
+ public ArrayData set(final int index, final Object value, final boolean strict) {
+ if (checkAdd(index, value)) {
+ return this;
+ }
+ underlying = underlying.set(index, value, strict);
+ return this;
+ }
+
+ @Override
+ public ArrayData set(final int index, final int value, final boolean strict) {
+ if (checkAdd(index, value)) {
+ return this;
+ }
+ underlying = underlying.set(index, value, strict);
+ return this;
+ }
+
+ @Override
+ public ArrayData set(final int index, final long value, final boolean strict) {
+ if (checkAdd(index, value)) {
+ return this;
+ }
+ underlying = underlying.set(index, value, strict);
+ return this;
+ }
+
+ @Override
+ public ArrayData set(final int index, final double value, final boolean strict) {
+ if (checkAdd(index, value)) {
+ return this;
+ }
+ underlying = underlying.set(index, value, strict);
+ return this;
+ }
+
+ @Override
+ public ArrayData delete(final int index) {
+ extraElements.remove(index);
+ underlying = underlying.delete(index);
+ return this;
+ }
+
+ @Override
+ public ArrayData delete(final long fromIndex, final long toIndex) {
+ for (final Iterator<Long> iter = extraElements.keySet().iterator(); iter.hasNext();) {
+ final long next = iter.next();
+ if (next >= fromIndex && next <= toIndex) {
+ iter.remove();
+ }
+ if (next > toIndex) { //ordering guaranteed because TreeSet
+ break;
+ }
+ }
+ underlying = underlying.delete(fromIndex, toIndex);
+ return this;
+ }
+
+ @Override
+ public Iterator<Long> indexIterator() {
+ final List<Long> keys = computeIteratorKeys();
+ keys.addAll(extraElements.keySet()); //even if they are outside length this is fine
+ return keys.iterator();
+ }
+
+}
diff --git a/src/jdk/nashorn/internal/runtime/arrays/LongArrayData.java b/src/jdk/nashorn/internal/runtime/arrays/LongArrayData.java
index f41ee15a..0437cdfe 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/LongArrayData.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/LongArrayData.java
@@ -27,7 +27,6 @@ package jdk.nashorn.internal.runtime.arrays;
import static jdk.nashorn.internal.codegen.CompilerConstants.specialCall;
import static jdk.nashorn.internal.lookup.Lookup.MH;
-
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.util.Arrays;
@@ -52,17 +51,32 @@ final class LongArrayData extends ContinuousArrayData implements IntOrLongElemen
LongArrayData(final long array[], final int length) {
super(length);
assert array.length >= length;
- this.array = array;
+ this.array = array;
}
@Override
- public Class<?> getElementType() {
+ public final Class<?> getElementType() {
return long.class;
}
@Override
- public ArrayData copy() {
- return new LongArrayData(array.clone(), (int)length);
+ public final Class<?> getBoxedElementType() {
+ return Long.class;
+ }
+
+ @Override
+ public final ContinuousArrayData widest(final ContinuousArrayData otherData) {
+ return otherData instanceof IntElements ? this : otherData;
+ }
+
+ @Override
+ public final int getElementWeight() {
+ return 2;
+ }
+
+ @Override
+ public LongArrayData copy() {
+ return new LongArrayData(array.clone(), (int)length());
}
@Override
@@ -71,10 +85,11 @@ final class LongArrayData extends ContinuousArrayData implements IntOrLongElemen
}
private Object[] toObjectArray(final boolean trim) {
- assert length <= array.length : "length exceeds internal array size";
- final Object[] oarray = new Object[trim ? (int)length : array.length];
+ assert length() <= array.length : "length exceeds internal array size";
+ final int len = (int)length();
+ final Object[] oarray = new Object[trim ? len : array.length];
- for (int index = 0; index < length; index++) {
+ for (int index = 0; index < len; index++) {
oarray[index] = Long.valueOf(array[index]);
}
@@ -84,16 +99,18 @@ final class LongArrayData extends ContinuousArrayData implements IntOrLongElemen
@Override
public Object asArrayOfType(final Class<?> componentType) {
if (componentType == long.class) {
- return array.length == length ? array.clone() : Arrays.copyOf(array, (int)length);
+ final int len = (int)length();
+ return array.length == len ? array.clone() : Arrays.copyOf(array, len);
}
return super.asArrayOfType(componentType);
}
private double[] toDoubleArray() {
- assert length <= array.length : "length exceeds internal array size";
+ assert length() <= array.length : "length exceeds internal array size";
+ final int len = (int)length();
final double[] darray = new double[array.length];
- for (int index = 0; index < length; index++) {
+ for (int index = 0; index < len; index++) {
darray[index] = array[index];
}
@@ -101,11 +118,11 @@ final class LongArrayData extends ContinuousArrayData implements IntOrLongElemen
}
@Override
- public ArrayData convert(final Class<?> type) {
+ public ContinuousArrayData convert(final Class<?> type) {
if (type == Integer.class || type == Long.class) {
return this;
}
- final int len = (int)length;
+ final int len = (int)length();
if (type == Double.class) {
return new NumberArrayData(toDoubleArray(), len);
}
@@ -119,7 +136,7 @@ final class LongArrayData extends ContinuousArrayData implements IntOrLongElemen
@Override
public ArrayData shiftRight(final int by) {
- final ArrayData newData = ensure(by + length - 1);
+ final ArrayData newData = ensure(by + length() - 1);
if (newData != this) {
newData.shiftRight(by);
return newData;
@@ -145,8 +162,7 @@ final class LongArrayData extends ContinuousArrayData implements IntOrLongElemen
@Override
public ArrayData shrink(final long newLength) {
- Arrays.fill(array, (int) newLength, array.length, 0);
-
+ Arrays.fill(array, (int)newLength, array.length, 0L);
return this;
}
@@ -165,14 +181,14 @@ final class LongArrayData extends ContinuousArrayData implements IntOrLongElemen
@Override
public ArrayData set(final int index, final int value, final boolean strict) {
array[index] = value;
- setLength(Math.max(index + 1, length));
+ setLength(Math.max(index + 1, length()));
return this;
}
@Override
public ArrayData set(final int index, final long value, final boolean strict) {
array[index] = value;
- setLength(Math.max(index + 1, length));
+ setLength(Math.max(index + 1, length()));
return this;
}
@@ -180,7 +196,7 @@ final class LongArrayData extends ContinuousArrayData implements IntOrLongElemen
public ArrayData set(final int index, final double value, final boolean strict) {
if (JSType.isRepresentableAsLong(value)) {
array[index] = (long)value;
- setLength(Math.max(index + 1, length));
+ setLength(Math.max(index + 1, length()));
return this;
}
return convert(Double.class).set(index, value, strict);
@@ -251,7 +267,7 @@ final class LongArrayData extends ContinuousArrayData implements IntOrLongElemen
@Override
public boolean has(final int index) {
- return 0 <= index && index < length;
+ return 0 <= index && index < length();
}
@Override
@@ -266,11 +282,12 @@ final class LongArrayData extends ContinuousArrayData implements IntOrLongElemen
@Override
public Object pop() {
- if (length == 0) {
+ final int len = (int)length();
+ if (len == 0) {
return ScriptRuntime.UNDEFINED;
}
- final int newLength = (int)length - 1;
+ final int newLength = len - 1;
final long elem = array[newLength];
array[newLength] = 0;
setLength(newLength);
@@ -280,14 +297,14 @@ final class LongArrayData extends ContinuousArrayData implements IntOrLongElemen
@Override
public ArrayData slice(final long from, final long to) {
- final long start = from < 0 ? from + length : from;
+ final long start = from < 0 ? from + length() : from;
final long newLength = to - start;
return new LongArrayData(Arrays.copyOfRange(array, (int)from, (int)to), (int)newLength);
}
@Override
public final ArrayData push(final boolean strict, final long item) {
- final long len = length;
+ final long len = length();
final ArrayData newData = ensure(len);
if (newData == this) {
array[(int)len] = item;
@@ -298,7 +315,7 @@ final class LongArrayData extends ContinuousArrayData implements IntOrLongElemen
@Override
public ArrayData fastSplice(final int start, final int removed, final int added) throws UnsupportedOperationException {
- final long oldLength = length;
+ final long oldLength = length();
final long newLength = oldLength - removed + added;
if (newLength > SparseArrayData.MAX_DENSE_LENGTH && newLength > array.length) {
throw new UnsupportedOperationException();
@@ -331,20 +348,20 @@ final class LongArrayData extends ContinuousArrayData implements IntOrLongElemen
@Override
public long fastPush(final long arg) {
- final int len = (int)length;
+ final int len = (int)length();
if (len == array.length) {
array = Arrays.copyOf(array, nextSize(len));
}
array[len] = arg;
- return ++length;
+ return increaseLength();
}
@Override
public long fastPopLong() {
- if (length == 0) {
- throw new ClassCastException();
+ if (length() == 0) {
+ throw new ClassCastException(); //undefined result
}
- final int newLength = (int)--length;
+ final int newLength = (int)decreaseLength();
final long elem = array[newLength];
array[newLength] = 0;
return elem;
@@ -359,4 +376,38 @@ final class LongArrayData extends ContinuousArrayData implements IntOrLongElemen
public Object fastPopObject() {
return fastPopLong();
}
+
+ @Override
+ public ContinuousArrayData fastConcat(final ContinuousArrayData otherData) {
+ final int otherLength = (int)otherData.length();
+ final int thisLength = (int)length();
+ assert otherLength > 0 && thisLength > 0;
+
+ final long[] otherArray = ((LongArrayData)otherData).array;
+ final int newLength = otherLength + thisLength;
+ final long[] newArray = new long[ArrayData.alignUp(newLength)];
+
+ System.arraycopy(array, 0, newArray, 0, thisLength);
+ System.arraycopy(otherArray, 0, newArray, thisLength, otherLength);
+
+ return new LongArrayData(newArray, newLength);
+ }
+
+ @Override
+ public String toString() {
+ assert length() <= array.length : length() + " > " + array.length;
+
+ final StringBuilder sb = new StringBuilder(getClass().getSimpleName()).
+ append(": [");
+ final int len = (int)length();
+ for (int i = 0; i < len; i++) {
+ sb.append(array[i]).append('L'); //make sure L suffix is on elements, to discriminate this from IntArrayData.toString()
+ if (i + 1 < len) {
+ sb.append(", ");
+ }
+ }
+ sb.append(']');
+
+ return sb.toString();
+ }
}
diff --git a/src/jdk/nashorn/internal/runtime/arrays/NoTypeArrayData.java b/src/jdk/nashorn/internal/runtime/arrays/NoTypeArrayData.java
deleted file mode 100644
index 143cd221..00000000
--- a/src/jdk/nashorn/internal/runtime/arrays/NoTypeArrayData.java
+++ /dev/null
@@ -1,186 +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.runtime.arrays;
-
-import java.lang.reflect.Array;
-import jdk.nashorn.internal.runtime.ScriptRuntime;
-
-/**
- * Place holding array data for non-array objects. Activates a true array when
- * accessed. Should only exist as a singleton defined in ArrayData.
- */
-final class NoTypeArrayData extends ArrayData {
- NoTypeArrayData() {
- super(0);
- }
-
- NoTypeArrayData(final long length) {
- super(length);
- }
-
- @Override
- public Object[] asObjectArray() {
- return ScriptRuntime.EMPTY_ARRAY;
- }
-
- @Override
- public ArrayData copy() {
- return new NoTypeArrayData();
- }
-
- @Override
- public Object asArrayOfType(final Class<?> componentType) {
- return Array.newInstance(componentType, 0);
- }
-
- @Override
- public ArrayData convert(final Class<?> type) {
- final long len = length;
- final ArrayData arrayData;
- if (type == Long.class) {
- arrayData = new LongArrayData(new long[ArrayData.nextSize((int)len)], (int)len);
- } else if (type == Double.class) {
- arrayData = new NumberArrayData(new double[ArrayData.nextSize((int)len)], (int)len);
- } else if (type == Integer.class) {
- arrayData = new IntArrayData(new int[ArrayData.nextSize((int)len)], (int)len);
- } else {
- assert !type.isPrimitive();
- arrayData = new ObjectArrayData(new Object[ArrayData.nextSize((int)len)], (int)len);
- }
- return length == 0 ? arrayData : new DeletedRangeArrayFilter(arrayData, 0, len - 1);
- }
-
- @Override
- public void shiftLeft(final int by) {
- //empty
- }
-
- @Override
- public ArrayData shiftRight(final int by) {
- return this;
- }
-
- @Override
- public ArrayData ensure(final long safeIndex) {
- if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH) {
- return new SparseArrayData(this, safeIndex + 1);
- }
-
- // Don't trample the shared EMPTY_ARRAY.
- if (length == 0) {
- return new NoTypeArrayData(Math.max(safeIndex + 1, length));
- }
-
- setLength(Math.max(safeIndex + 1, length));
- return this;
- }
-
- @Override
- public ArrayData shrink(final long newLength) {
- return this;
- }
-
- @Override
- public ArrayData set(final int index, final Object value, final boolean strict) {
- ArrayData newData;
-
- if (value instanceof Double) {
- newData = convert(Double.class);
- } else if (value instanceof Long) {
- newData = convert(Long.class);
- } else if (value instanceof Integer) {
- newData = convert(Integer.class);
- } else {
- assert !(value instanceof Number);
- newData = convert(value == null ? Object.class : value.getClass());
- }
-
- return newData.set(index, value, strict);
- }
-
- @Override
- public ArrayData set(final int index, final int value, final boolean strict) {
- final ArrayData newData = convert(Integer.class);
- return newData.set(index, value, strict);
- }
-
- @Override
- public ArrayData set(final int index, final long value, final boolean strict) {
- final ArrayData newData = convert(Long.class);
- return newData.set(index, value, strict);
- }
-
- @Override
- public ArrayData set(final int index, final double value, final boolean strict) {
- final ArrayData newData = convert(Double.class);
- return newData.set(index, value, strict);
- }
-
- @Override
- public int getInt(final int index) {
- throw new ArrayIndexOutOfBoundsException(index);
- }
-
- @Override
- public long getLong(final int index) {
- throw new ArrayIndexOutOfBoundsException(index);
- }
-
- @Override
- public double getDouble(final int index) {
- throw new ArrayIndexOutOfBoundsException(index);
- }
-
- @Override
- public Object getObject(final int index) {
- throw new ArrayIndexOutOfBoundsException(index);
- }
-
- @Override
- public boolean has(final int index) {
- return false;
- }
-
- @Override
- public ArrayData delete(final int index) {
- return new DeletedRangeArrayFilter(this, index, index);
- }
-
- @Override
- public ArrayData delete(final long fromIndex, final long toIndex) {
- return new DeletedRangeArrayFilter(this, fromIndex, toIndex);
- }
-
- @Override
- public Object pop() {
- return ScriptRuntime.UNDEFINED;
- }
-
- @Override
- public ArrayData slice(final long from, final long to) {
- return this;
- }
-}
diff --git a/src/jdk/nashorn/internal/runtime/arrays/NonExtensibleArrayFilter.java b/src/jdk/nashorn/internal/runtime/arrays/NonExtensibleArrayFilter.java
new file mode 100644
index 00000000..7e0f3c63
--- /dev/null
+++ b/src/jdk/nashorn/internal/runtime/arrays/NonExtensibleArrayFilter.java
@@ -0,0 +1,68 @@
+package jdk.nashorn.internal.runtime.arrays;
+
+import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
+import jdk.nashorn.internal.objects.Global;
+import jdk.nashorn.internal.runtime.ScriptRuntime;
+
+/**
+ * Filter class that wrap arrays that have been tagged non extensible
+ */
+final class NonExtensibleArrayFilter extends ArrayFilter {
+
+ /**
+ * Constructor
+ * @param underlying array
+ */
+ NonExtensibleArrayFilter(final ArrayData underlying) {
+ super(underlying);
+ }
+
+ @Override
+ public ArrayData copy() {
+ return new NonExtensibleArrayFilter(underlying.copy());
+ }
+
+ @Override
+ public ArrayData slice(final long from, final long to) {
+ return new NonExtensibleArrayFilter(underlying.slice(from, to));
+ }
+
+ private ArrayData extensionCheck(final boolean strict, final int index) {
+ if (!strict) {
+ return this;
+ }
+ throw typeError(Global.instance(), "object.non.extensible", String.valueOf(index), ScriptRuntime.safeToString(this));
+ }
+
+ @Override
+ public ArrayData set(final int index, final Object value, final boolean strict) {
+ if (has(index)) {
+ return underlying.set(index, value, strict);
+ }
+ return extensionCheck(strict, index);
+ }
+
+ @Override
+ public ArrayData set(final int index, final int value, final boolean strict) {
+ if (has(index)) {
+ return underlying.set(index, value, strict);
+ }
+ return extensionCheck(strict, index);
+ }
+
+ @Override
+ public ArrayData set(final int index, final long value, final boolean strict) {
+ if (has(index)) {
+ return underlying.set(index, value, strict);
+ }
+ return extensionCheck(strict, index);
+ }
+
+ @Override
+ public ArrayData set(final int index, final double value, final boolean strict) {
+ if (has(index)) {
+ return underlying.set(index, value, strict);
+ }
+ return extensionCheck(strict, index);
+ }
+}
diff --git a/src/jdk/nashorn/internal/runtime/arrays/NumberArrayData.java b/src/jdk/nashorn/internal/runtime/arrays/NumberArrayData.java
index 2c57208f..2522b979 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/NumberArrayData.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/NumberArrayData.java
@@ -28,7 +28,6 @@ package jdk.nashorn.internal.runtime.arrays;
import static jdk.nashorn.internal.codegen.CompilerConstants.specialCall;
import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
-
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.util.Arrays;
@@ -48,20 +47,35 @@ final class NumberArrayData extends ContinuousArrayData implements NumericElemen
* @param array an int array
* @param length a length, not necessarily array.length
*/
- NumberArrayData(final double array[], final int length) {
+ NumberArrayData(final double[] array, final int length) {
super(length);
assert array.length >= length;
- this.array = array;
+ this.array = array;
}
@Override
- public Class<?> getElementType() {
+ public final Class<?> getElementType() {
return double.class;
}
@Override
- public ArrayData copy() {
- return new NumberArrayData(array.clone(), (int)length);
+ public final Class<?> getBoxedElementType() {
+ return Double.class;
+ }
+
+ @Override
+ public final int getElementWeight() {
+ return 3;
+ }
+
+ @Override
+ public final ContinuousArrayData widest(final ContinuousArrayData otherData) {
+ return otherData instanceof IntOrLongElements ? this : otherData;
+ }
+
+ @Override
+ public NumberArrayData copy() {
+ return new NumberArrayData(array.clone(), (int)length());
}
@Override
@@ -70,10 +84,11 @@ final class NumberArrayData extends ContinuousArrayData implements NumericElemen
}
private Object[] toObjectArray(final boolean trim) {
- assert length <= array.length : "length exceeds internal array size";
- final Object[] oarray = new Object[trim ? (int)length : array.length];
+ assert length() <= array.length : "length exceeds internal array size";
+ final int len = (int)length();
+ final Object[] oarray = new Object[trim ? len : array.length];
- for (int index = 0; index < length; index++) {
+ for (int index = 0; index < len; index++) {
oarray[index] = Double.valueOf(array[index]);
}
return oarray;
@@ -81,16 +96,17 @@ final class NumberArrayData extends ContinuousArrayData implements NumericElemen
@Override
public Object asArrayOfType(final Class<?> componentType) {
- if(componentType == double.class) {
- return array.length == length ? array.clone() : Arrays.copyOf(array, (int)length);
+ if (componentType == double.class) {
+ final int len = (int)length();
+ return array.length == len ? array.clone() : Arrays.copyOf(array, len);
}
return super.asArrayOfType(componentType);
}
@Override
- public ArrayData convert(final Class<?> type) {
+ public ContinuousArrayData convert(final Class<?> type) {
if (type != Double.class && type != Integer.class && type != Long.class) {
- final int len = (int)length;
+ final int len = (int)length();
return new ObjectArrayData(toObjectArray(false), len);
}
return this;
@@ -103,7 +119,7 @@ final class NumberArrayData extends ContinuousArrayData implements NumericElemen
@Override
public ArrayData shiftRight(final int by) {
- final ArrayData newData = ensure(by + length - 1);
+ final ArrayData newData = ensure(by + length() - 1);
if (newData != this) {
newData.shiftRight(by);
return newData;
@@ -129,7 +145,7 @@ final class NumberArrayData extends ContinuousArrayData implements NumericElemen
@Override
public ArrayData shrink(final long newLength) {
- Arrays.fill(array, (int) newLength, array.length, 0.0);
+ Arrays.fill(array, (int)newLength, array.length, 0.0);
return this;
}
@@ -148,21 +164,21 @@ final class NumberArrayData extends ContinuousArrayData implements NumericElemen
@Override
public ArrayData set(final int index, final int value, final boolean strict) {
array[index] = value;
- setLength(Math.max(index + 1, length));
+ setLength(Math.max(index + 1, length()));
return this;
}
@Override
public ArrayData set(final int index, final long value, final boolean strict) {
array[index] = value;
- setLength(Math.max(index + 1, length));
+ setLength(Math.max(index + 1, length()));
return this;
}
@Override
public ArrayData set(final int index, final double value, final boolean strict) {
array[index] = value;
- setLength(Math.max(index + 1, length));
+ setLength(Math.max(index + 1, length()));
return this;
}
@@ -226,7 +242,7 @@ final class NumberArrayData extends ContinuousArrayData implements NumericElemen
@Override
public boolean has(final int index) {
- return 0 <= index && index < length;
+ return 0 <= index && index < length();
}
@Override
@@ -241,11 +257,12 @@ final class NumberArrayData extends ContinuousArrayData implements NumericElemen
@Override
public Object pop() {
- if (length == 0) {
+ final int len = (int)length();
+ if (len == 0) {
return UNDEFINED;
}
- final int newLength = (int)length - 1;
+ final int newLength = len - 1;
final double elem = array[newLength];
array[newLength] = 0;
setLength(newLength);
@@ -254,14 +271,14 @@ final class NumberArrayData extends ContinuousArrayData implements NumericElemen
@Override
public ArrayData slice(final long from, final long to) {
- final long start = from < 0 ? from + length : from;
+ final long start = from < 0 ? from + length() : from;
final long newLength = to - start;
return new NumberArrayData(Arrays.copyOfRange(array, (int)from, (int)to), (int)newLength);
}
@Override
public final ArrayData push(final boolean strict, final double item) {
- final long len = length;
+ final long len = length();
final ArrayData newData = ensure(len);
if (newData == this) {
array[(int)len] = item;
@@ -272,7 +289,7 @@ final class NumberArrayData extends ContinuousArrayData implements NumericElemen
@Override
public ArrayData fastSplice(final int start, final int removed, final int added) throws UnsupportedOperationException {
- final long oldLength = length;
+ final long oldLength = length();
final long newLength = oldLength - removed + added;
if (newLength > SparseArrayData.MAX_DENSE_LENGTH && newLength > array.length) {
throw new UnsupportedOperationException();
@@ -310,21 +327,21 @@ final class NumberArrayData extends ContinuousArrayData implements NumericElemen
@Override
public long fastPush(final double arg) {
- final int len = (int)length;
+ final int len = (int)length();
if (len == array.length) {
//note that fastpush never creates spares arrays, there is nothing to gain by that - it will just use even more memory
array = Arrays.copyOf(array, nextSize(len));
}
array[len] = arg;
- return ++length;
+ return increaseLength();
}
@Override
public double fastPopDouble() {
- if (length == 0) {
+ if (length() == 0) {
throw new ClassCastException();
}
- final int newLength = (int)--length;
+ final int newLength = (int)decreaseLength();
final double elem = array[newLength];
array[newLength] = 0;
return elem;
@@ -334,4 +351,26 @@ final class NumberArrayData extends ContinuousArrayData implements NumericElemen
public Object fastPopObject() {
return fastPopDouble();
}
+
+ @Override
+ public ContinuousArrayData fastConcat(final ContinuousArrayData otherData) {
+ final int otherLength = (int)otherData.length();
+ final int thisLength = (int)length();
+ assert otherLength > 0 && thisLength > 0;
+
+ final double[] otherArray = ((NumberArrayData)otherData).array;
+ final int newLength = otherLength + thisLength;
+ final double[] newArray = new double[ArrayData.alignUp(newLength)];
+
+ System.arraycopy(array, 0, newArray, 0, thisLength);
+ System.arraycopy(otherArray, 0, newArray, thisLength, otherLength);
+
+ return new NumberArrayData(newArray, newLength);
+ }
+
+ @Override
+ public String toString() {
+ assert length() <= array.length : length() + " > " + array.length;
+ return getClass().getSimpleName() + ':' + Arrays.toString(Arrays.copyOf(array, (int)length()));
+ }
}
diff --git a/src/jdk/nashorn/internal/runtime/arrays/NumericElements.java b/src/jdk/nashorn/internal/runtime/arrays/NumericElements.java
index ad940e2a..cb87ea91 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/NumericElements.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/NumericElements.java
@@ -30,6 +30,6 @@ package jdk.nashorn.internal.runtime.arrays;
* Used for type checks that throw ClassCastExceptions and force relinks
* for fast NativeArray specializations of builtin methods
*/
-public interface NumericElements {
+public interface NumericElements extends AnyElements {
//empty
}
diff --git a/src/jdk/nashorn/internal/runtime/arrays/ObjectArrayData.java b/src/jdk/nashorn/internal/runtime/arrays/ObjectArrayData.java
index 379ba6e4..ebaa3d6d 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/ObjectArrayData.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/ObjectArrayData.java
@@ -26,7 +26,6 @@
package jdk.nashorn.internal.runtime.arrays;
import static jdk.nashorn.internal.codegen.CompilerConstants.specialCall;
-
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.util.Arrays;
@@ -37,7 +36,7 @@ import jdk.nashorn.internal.runtime.ScriptRuntime;
* Implementation of {@link ArrayData} as soon as an Object has been
* written to the array
*/
-final class ObjectArrayData extends ContinuousArrayData {
+final class ObjectArrayData extends ContinuousArrayData implements AnyElements {
/**
* The wrapped array
@@ -49,29 +48,44 @@ final class ObjectArrayData extends ContinuousArrayData {
* @param array an int array
* @param length a length, not necessarily array.length
*/
- ObjectArrayData(final Object array[], final int length) {
+ ObjectArrayData(final Object[] array, final int length) {
super(length);
assert array.length >= length;
this.array = array;
}
@Override
- public Class<?> getElementType() {
+ public final Class<?> getElementType() {
return Object.class;
}
@Override
- public ArrayData copy() {
- return new ObjectArrayData(array.clone(), (int)length);
+ public final Class<?> getBoxedElementType() {
+ return getElementType();
+ }
+
+ @Override
+ public final int getElementWeight() {
+ return 4;
+ }
+
+ @Override
+ public final ContinuousArrayData widest(final ContinuousArrayData otherData) {
+ return otherData instanceof NumericElements ? this : otherData;
+ }
+
+ @Override
+ public ObjectArrayData copy() {
+ return new ObjectArrayData(array.clone(), (int)length());
}
@Override
public Object[] asObjectArray() {
- return array.length == length ? array.clone() : asObjectArrayCopy();
+ return array.length == length() ? array.clone() : asObjectArrayCopy();
}
private Object[] asObjectArrayCopy() {
- final long len = length;
+ final long len = length();
assert len <= Integer.MAX_VALUE;
final Object[] copy = new Object[(int)len];
System.arraycopy(array, 0, copy, 0, (int)len);
@@ -79,7 +93,7 @@ final class ObjectArrayData extends ContinuousArrayData {
}
@Override
- public ArrayData convert(final Class<?> type) {
+ public ObjectArrayData convert(final Class<?> type) {
return this;
}
@@ -90,7 +104,7 @@ final class ObjectArrayData extends ContinuousArrayData {
@Override
public ArrayData shiftRight(final int by) {
- final ArrayData newData = ensure(by + length - 1);
+ final ArrayData newData = ensure(by + length() - 1);
if (newData != this) {
newData.shiftRight(by);
return newData;
@@ -122,28 +136,28 @@ final class ObjectArrayData extends ContinuousArrayData {
@Override
public ArrayData set(final int index, final Object value, final boolean strict) {
array[index] = value;
- setLength(Math.max(index + 1, length));
+ setLength(Math.max(index + 1, length()));
return this;
}
@Override
public ArrayData set(final int index, final int value, final boolean strict) {
array[index] = value;
- setLength(Math.max(index + 1, length));
+ setLength(Math.max(index + 1, length()));
return this;
}
@Override
public ArrayData set(final int index, final long value, final boolean strict) {
array[index] = value;
- setLength(Math.max(index + 1, length));
+ setLength(Math.max(index + 1, length()));
return this;
}
@Override
public ArrayData set(final int index, final double value, final boolean strict) {
array[index] = value;
- setLength(Math.max(index + 1, length));
+ setLength(Math.max(index + 1, length()));
return this;
}
@@ -216,7 +230,7 @@ final class ObjectArrayData extends ContinuousArrayData {
@Override
public boolean has(final int index) {
- return 0 <= index && index < length;
+ return 0 <= index && index < length();
}
@Override
@@ -248,20 +262,20 @@ final class ObjectArrayData extends ContinuousArrayData {
@Override
public long fastPush(final Object arg) {
- final int len = (int)length;
+ final int len = (int)length();
if (len == array.length) {
array = Arrays.copyOf(array, nextSize(len));
}
array[len] = arg;
- return ++length;
+ return increaseLength();
}
@Override
public Object fastPopObject() {
- if (length == 0) {
+ if (length() == 0) {
return ScriptRuntime.UNDEFINED;
}
- final int newLength = (int)--length;
+ final int newLength = (int)decreaseLength();
final Object elem = array[newLength];
array[newLength] = ScriptRuntime.EMPTY;
return elem;
@@ -269,11 +283,11 @@ final class ObjectArrayData extends ContinuousArrayData {
@Override
public Object pop() {
- if (length == 0) {
+ if (length() == 0) {
return ScriptRuntime.UNDEFINED;
}
- final int newLength = (int)length - 1;
+ final int newLength = (int)length() - 1;
final Object elem = array[newLength];
setEmpty(newLength);
setLength(newLength);
@@ -282,14 +296,14 @@ final class ObjectArrayData extends ContinuousArrayData {
@Override
public ArrayData slice(final long from, final long to) {
- final long start = from < 0 ? from + length : from;
+ final long start = from < 0 ? from + length() : from;
final long newLength = to - start;
return new ObjectArrayData(Arrays.copyOfRange(array, (int)from, (int)to), (int)newLength);
}
@Override
public ArrayData push(final boolean strict, final Object item) {
- final long len = length;
+ final long len = length();
final ArrayData newData = ensure(len);
if (newData == this) {
array[(int)len] = item;
@@ -300,7 +314,7 @@ final class ObjectArrayData extends ContinuousArrayData {
@Override
public ArrayData fastSplice(final int start, final int removed, final int added) throws UnsupportedOperationException {
- final long oldLength = length;
+ final long oldLength = length();
final long newLength = oldLength - removed + added;
if (newLength > SparseArrayData.MAX_DENSE_LENGTH && newLength > array.length) {
throw new UnsupportedOperationException();
@@ -325,4 +339,26 @@ final class ObjectArrayData extends ContinuousArrayData {
return returnValue;
}
+
+ @Override
+ public ContinuousArrayData fastConcat(final ContinuousArrayData otherData) {
+ final int otherLength = (int)otherData.length();
+ final int thisLength = (int)length();
+ assert otherLength > 0 && thisLength > 0;
+
+ final Object[] otherArray = ((ObjectArrayData)otherData).array;
+ final int newLength = otherLength + thisLength;
+ final Object[] newArray = new Object[ArrayData.alignUp(newLength)];
+
+ System.arraycopy(array, 0, newArray, 0, thisLength);
+ System.arraycopy(otherArray, 0, newArray, thisLength, otherLength);
+
+ return new ObjectArrayData(newArray, newLength);
+ }
+
+ @Override
+ public String toString() {
+ assert length() <= array.length : length() + " > " + array.length;
+ return getClass().getSimpleName() + ':' + Arrays.toString(Arrays.copyOf(array, (int)length()));
+ }
}
diff --git a/src/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java b/src/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java
index ef473451..ff569d44 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java
@@ -36,7 +36,7 @@ import jdk.nashorn.internal.runtime.ScriptRuntime;
* Handle arrays where the index is very large.
*/
class SparseArrayData extends ArrayData {
- static final long MAX_DENSE_LENGTH = 16 * 512 * 1024;
+ static final int MAX_DENSE_LENGTH = 8 * 1024 * 1024;
/** Underlying array. */
private ArrayData underlying;
@@ -53,21 +53,21 @@ class SparseArrayData extends ArrayData {
SparseArrayData(final ArrayData underlying, final long length, final TreeMap<Long, Object> sparseMap) {
super(length);
- assert underlying.length <= length;
+ assert underlying.length() <= length;
this.underlying = underlying;
- this.maxDenseLength = Math.max(MAX_DENSE_LENGTH, underlying.length);
+ this.maxDenseLength = Math.max(MAX_DENSE_LENGTH, underlying.length());
this.sparseMap = sparseMap;
}
@Override
public ArrayData copy() {
- return new SparseArrayData(underlying.copy(), length, new TreeMap<>(sparseMap));
+ return new SparseArrayData(underlying.copy(), length(), new TreeMap<>(sparseMap));
}
@Override
public Object[] asObjectArray() {
- final int len = (int)Math.min(length, Integer.MAX_VALUE);
- final int underlyingLength = (int)Math.min(len, underlying.length);
+ final int len = (int)Math.min(length(), Integer.MAX_VALUE);
+ final int underlyingLength = (int)Math.min(len, underlying.length());
final Object[] objArray = new Object[len];
for (int i = 0; i < underlyingLength; i++) {
@@ -104,14 +104,15 @@ class SparseArrayData extends ArrayData {
}
sparseMap = newSparseMap;
- setLength(Math.max(length - by, 0));
+ setLength(Math.max(length() - by, 0));
}
@Override
public ArrayData shiftRight(final int by) {
final TreeMap<Long, Object> newSparseMap = new TreeMap<>();
- if (underlying.length + by > maxDenseLength) {
- for (long i = maxDenseLength - by; i < underlying.length; i++) {
+ final long len = underlying.length();
+ if (len + by > maxDenseLength) {
+ for (long i = maxDenseLength - by; i < len; i++) {
if (underlying.has((int) i)) {
newSparseMap.put(Long.valueOf(i + by), underlying.getObject((int) i));
}
@@ -127,23 +128,23 @@ class SparseArrayData extends ArrayData {
}
sparseMap = newSparseMap;
- setLength(length + by);
+ setLength(length() + by);
return this;
}
@Override
public ArrayData ensure(final long safeIndex) {
- if (safeIndex < maxDenseLength && underlying.length <= safeIndex) {
+ if (safeIndex < maxDenseLength && underlying.length() <= safeIndex) {
underlying = underlying.ensure(safeIndex);
}
- setLength(Math.max(safeIndex + 1, length));
+ setLength(Math.max(safeIndex + 1, length()));
return this;
}
@Override
public ArrayData shrink(final long newLength) {
- if (newLength < underlying.length) {
+ if (newLength < underlying.length()) {
underlying = underlying.shrink(newLength);
underlying.setLength(newLength);
sparseMap.clear();
@@ -160,11 +161,11 @@ class SparseArrayData extends ArrayData {
if (index >= 0 && index < maxDenseLength) {
ensure(index);
underlying = underlying.set(index, value, strict);
- setLength(Math.max(underlying.length, length));
+ setLength(Math.max(underlying.length(), length()));
} else {
final Long longIndex = indexToKey(index);
sparseMap.put(longIndex, value);
- setLength(Math.max(longIndex + 1, length));
+ setLength(Math.max(longIndex + 1, length()));
}
return this;
@@ -175,11 +176,11 @@ class SparseArrayData extends ArrayData {
if (index >= 0 && index < maxDenseLength) {
ensure(index);
underlying = underlying.set(index, value, strict);
- setLength(Math.max(underlying.length, length));
+ setLength(Math.max(underlying.length(), length()));
} else {
final Long longIndex = indexToKey(index);
sparseMap.put(longIndex, value);
- setLength(Math.max(longIndex + 1, length));
+ setLength(Math.max(longIndex + 1, length()));
}
return this;
}
@@ -189,11 +190,11 @@ class SparseArrayData extends ArrayData {
if (index >= 0 && index < maxDenseLength) {
ensure(index);
underlying = underlying.set(index, value, strict);
- setLength(Math.max(underlying.length, length));
+ setLength(Math.max(underlying.length(), length()));
} else {
final Long longIndex = indexToKey(index);
sparseMap.put(longIndex, value);
- setLength(Math.max(longIndex + 1, length));
+ setLength(Math.max(longIndex + 1, length()));
}
return this;
}
@@ -203,11 +204,11 @@ class SparseArrayData extends ArrayData {
if (index >= 0 && index < maxDenseLength) {
ensure(index);
underlying = underlying.set(index, value, strict);
- setLength(Math.max(underlying.length, length));
+ setLength(Math.max(underlying.length(), length()));
} else {
final Long longIndex = indexToKey(index);
sparseMap.put(longIndex, value);
- setLength(Math.max(longIndex + 1, length));
+ setLength(Math.max(longIndex + 1, length()));
}
return this;
}
@@ -294,7 +295,7 @@ class SparseArrayData extends ArrayData {
@Override
public boolean has(final int index) {
if (index >= 0 && index < maxDenseLength) {
- return index < underlying.length && underlying.has(index);
+ return index < underlying.length() && underlying.has(index);
}
return sparseMap.containsKey(indexToKey(index));
@@ -303,7 +304,7 @@ class SparseArrayData extends ArrayData {
@Override
public ArrayData delete(final int index) {
if (index >= 0 && index < maxDenseLength) {
- if (index < underlying.length) {
+ if (index < underlying.length()) {
underlying = underlying.delete(index);
}
} else {
@@ -315,8 +316,8 @@ class SparseArrayData extends ArrayData {
@Override
public ArrayData delete(final long fromIndex, final long toIndex) {
- if (fromIndex < maxDenseLength && fromIndex < underlying.length) {
- underlying = underlying.delete(fromIndex, Math.min(toIndex, underlying.length - 1));
+ if (fromIndex < maxDenseLength && fromIndex < underlying.length()) {
+ underlying = underlying.delete(fromIndex, Math.min(toIndex, underlying.length() - 1));
}
if (toIndex >= maxDenseLength) {
sparseMap.subMap(fromIndex, true, toIndex, true).clear();
@@ -336,30 +337,34 @@ class SparseArrayData extends ArrayData {
@Override
public Object pop() {
- if (length == 0) {
+ final long len = length();
+ final long underlyingLen = underlying.length();
+ if (len == 0) {
return ScriptRuntime.UNDEFINED;
}
- if (length == underlying.length) {
+ if (len == underlyingLen) {
final Object result = underlying.pop();
- setLength(underlying.length);
+ setLength(underlying.length());
return result;
}
- setLength(length - 1);
- final Long key = Long.valueOf(length);
+ setLength(len - 1);
+ final Long key = Long.valueOf(len - 1);
return sparseMap.containsKey(key) ? sparseMap.remove(key) : ScriptRuntime.UNDEFINED;
}
@Override
public ArrayData slice(final long from, final long to) {
- assert to <= length;
- final long start = from < 0 ? (from + length) : from;
+ assert to <= length();
+ final long start = from < 0 ? (from + length()) : from;
final long newLength = to - start;
+ final long underlyingLength = underlying.length();
+
if (start >= 0 && to <= maxDenseLength) {
- if (newLength <= underlying.length) {
+ if (newLength <= underlyingLength) {
return underlying.slice(from, to);
}
- return underlying.slice(from, to).ensure(newLength - 1).delete(underlying.length, newLength);
+ return underlying.slice(from, to).ensure(newLength - 1).delete(underlyingLength, newLength);
}
ArrayData sliced = EMPTY_ARRAY;
@@ -369,13 +374,13 @@ class SparseArrayData extends ArrayData {
sliced = sliced.set((int)(i - start), getObject((int)i), false);
}
}
- assert sliced.length == newLength;
+ assert sliced.length() == newLength;
return sliced;
}
@Override
public long nextIndex(final long index) {
- if (index < underlying.length - 1) {
+ if (index < underlying.length() - 1) {
return underlying.nextIndex(index);
}
@@ -383,6 +388,7 @@ class SparseArrayData extends ArrayData {
if (nextKey != null) {
return nextKey;
}
- return length;
+
+ return length();
}
}
diff --git a/src/jdk/nashorn/internal/runtime/arrays/TypedArrayData.java b/src/jdk/nashorn/internal/runtime/arrays/TypedArrayData.java
index 428678d0..40b3210b 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/TypedArrayData.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/TypedArrayData.java
@@ -58,7 +58,7 @@ public abstract class TypedArrayData<T extends Buffer> extends ContinuousArrayDa
* @return element length
*/
public final int getElementLength() {
- return (int)length;
+ return (int)length();
}
/**
@@ -88,7 +88,7 @@ public abstract class TypedArrayData<T extends Buffer> extends ContinuousArrayDa
}
@Override
- public ArrayData copy() {
+ public TypedArrayData<T> copy() {
throw new UnsupportedOperationException();
}
@@ -119,7 +119,7 @@ public abstract class TypedArrayData<T extends Buffer> extends ContinuousArrayDa
@Override
public final boolean has(final int index) {
- return 0 <= index && index < length;
+ return 0 <= index && index < length();
}
@Override
@@ -133,7 +133,7 @@ public abstract class TypedArrayData<T extends Buffer> extends ContinuousArrayDa
}
@Override
- public ArrayData convert(final Class<?> type) {
+ public TypedArrayData<T> convert(final Class<?> type) {
throw new UnsupportedOperationException();
}
diff --git a/src/jdk/nashorn/internal/runtime/arrays/UndefinedArrayFilter.java b/src/jdk/nashorn/internal/runtime/arrays/UndefinedArrayFilter.java
index f744aacd..9865dcbb 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/UndefinedArrayFilter.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/UndefinedArrayFilter.java
@@ -39,8 +39,7 @@ final class UndefinedArrayFilter extends ArrayFilter {
UndefinedArrayFilter(final ArrayData underlying) {
super(underlying);
-
- this.undefined = new BitVector(underlying.length);
+ this.undefined = new BitVector(underlying.length());
}
@Override
@@ -80,25 +79,24 @@ final class UndefinedArrayFilter extends ArrayFilter {
@Override
public void shiftLeft(final int by) {
super.shiftLeft(by);
- undefined.shiftLeft(by, length);
+ undefined.shiftLeft(by, length());
}
@Override
public ArrayData shiftRight(final int by) {
super.shiftRight(by);
- undefined.shiftRight(by, length);
-
+ undefined.shiftRight(by, length());
return this;
}
@Override
public ArrayData ensure(final long safeIndex) {
- if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH && safeIndex >= length) {
+ if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH && safeIndex >= length()) {
return new SparseArrayData(this, safeIndex + 1);
}
super.ensure(safeIndex);
- undefined.resize(length);
+ undefined.resize(length());
return this;
}
@@ -106,8 +104,7 @@ final class UndefinedArrayFilter extends ArrayFilter {
@Override
public ArrayData shrink(final long newLength) {
super.shrink(newLength);
- undefined.resize(length);
-
+ undefined.resize(length());
return this;
}
@@ -216,7 +213,7 @@ final class UndefinedArrayFilter extends ArrayFilter {
@Override
public Object pop() {
- final long index = length - 1;
+ final long index = length() - 1;
if (super.has((int)index)) {
final boolean isUndefined = undefined.isSet(index);
@@ -233,7 +230,7 @@ final class UndefinedArrayFilter extends ArrayFilter {
final ArrayData newArray = underlying.slice(from, to);
final UndefinedArrayFilter newFilter = new UndefinedArrayFilter(newArray);
newFilter.getUndefined().copy(undefined);
- newFilter.getUndefined().shiftLeft(from, newFilter.length);
+ newFilter.getUndefined().shiftLeft(from, newFilter.length());
return newFilter;
}
diff --git a/src/jdk/nashorn/internal/runtime/events/RecompilationEvent.java b/src/jdk/nashorn/internal/runtime/events/RecompilationEvent.java
index bdc73bfe..8b2ff471 100644
--- a/src/jdk/nashorn/internal/runtime/events/RecompilationEvent.java
+++ b/src/jdk/nashorn/internal/runtime/events/RecompilationEvent.java
@@ -47,10 +47,9 @@ public final class RecompilationEvent extends RuntimeEvent<RewriteException> {
* @param level logging level
* @param rewriteException rewriteException wrapped by this RuntimEvent
* @param returnValue rewriteException return value - as we don't want to make
- * {@link RewriteException#getReturnValueNonDestructive()} public, we pass it as
+ * {@code RewriteException.getReturnValueNonDestructive()} public, we pass it as
* an extra parameter, rather than querying the getter from another package.
*/
- @SuppressWarnings("javadoc")
public RecompilationEvent(final Level level, final RewriteException rewriteException, final Object returnValue) {
super(level, rewriteException);
assert Context.getContext().getLogger(RecompilableScriptFunctionData.class).isEnabled() :
diff --git a/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java b/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java
index 67dd88e2..cd4dd3b7 100644
--- a/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java
+++ b/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java
@@ -26,6 +26,7 @@
package jdk.nashorn.internal.runtime.linker;
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
+import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import java.lang.invoke.CallSite;
import java.lang.invoke.ConstantCallSite;
@@ -42,12 +43,16 @@ import jdk.internal.dynalink.beans.StaticClass;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
import jdk.internal.dynalink.linker.LinkerServices;
+import jdk.internal.dynalink.linker.MethodTypeConversionStrategy;
+import jdk.internal.dynalink.support.TypeUtilities;
import jdk.nashorn.api.scripting.JSObject;
import jdk.nashorn.internal.codegen.CompilerConstants.Call;
import jdk.nashorn.internal.codegen.ObjectClassGenerator;
import jdk.nashorn.internal.codegen.RuntimeCallSite;
import jdk.nashorn.internal.lookup.MethodHandleFactory;
import jdk.nashorn.internal.lookup.MethodHandleFunctionality;
+import jdk.nashorn.internal.objects.ScriptFunctionImpl;
+import jdk.nashorn.internal.runtime.ECMAException;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.OptimisticReturnFilters;
import jdk.nashorn.internal.runtime.ScriptFunction;
@@ -63,6 +68,8 @@ public final class Bootstrap {
private static final MethodHandleFunctionality MH = MethodHandleFactory.getFunctionality();
+ private static final MethodHandle VOID_TO_OBJECT = MH.constant(Object.class, ScriptRuntime.UNDEFINED);
+
/**
* The default dynalink relink threshold for megamorphisism is 8. In the case
* of object fields only, it is fine. However, with dual fields, in order to get
@@ -92,7 +99,7 @@ public final class Bootstrap {
new NashornLinker(),
new NashornPrimitiveLinker(),
new NashornStaticClassLinker(),
- new BoundDynamicMethodLinker(),
+ new BoundCallableLinker(),
new JavaSuperAdapterLinker(),
new JSObjectLinker(nashornBeansLinker),
new BrowserJSObjectLinker(nashornBeansLinker),
@@ -106,6 +113,12 @@ public final class Bootstrap {
return OptimisticReturnFilters.filterOptimisticReturnValue(inv, desc).asType(linkerServices, desc.getMethodType());
}
});
+ factory.setAutoConversionStrategy(new MethodTypeConversionStrategy() {
+ @Override
+ public MethodHandle asType(final MethodHandle target, final MethodType newType) {
+ return unboxReturnType(target, newType);
+ }
+ });
final int relinkThreshold = Options.getIntProperty("nashorn.unstable.relink.threshold", NASHORN_DEFAULT_UNSTABLE_RELINK_THRESHOLD);
if (relinkThreshold > -1) {
factory.setUnstableRelinkThreshold(relinkThreshold);
@@ -128,19 +141,47 @@ public final class Bootstrap {
}
return obj instanceof ScriptFunction ||
- ((obj instanceof JSObject) && ((JSObject)obj).isFunction()) ||
- isDynamicMethod(obj) ||
+ isJSObjectFunction(obj) ||
+ BeansLinker.isDynamicMethod(obj) ||
+ obj instanceof BoundCallable ||
isFunctionalInterfaceObject(obj) ||
obj instanceof StaticClass;
}
/**
+ * Returns true if the given object is a strict callable
+ * @param callable the callable object to be checked for strictness
+ * @return true if the obj is a strict callable, false if it is a non-strict callable.
+ * @throws ECMAException with {@code TypeError} if the object is not a callable.
+ */
+ public static boolean isStrictCallable(final Object callable) {
+ if (callable instanceof ScriptFunction) {
+ return ((ScriptFunction)callable).isStrict();
+ } else if (isJSObjectFunction(callable)) {
+ return ((JSObject)callable).isStrictFunction();
+ } else if (callable instanceof BoundCallable) {
+ return isStrictCallable(((BoundCallable)callable).getCallable());
+ } else if (BeansLinker.isDynamicMethod(callable) || callable instanceof StaticClass) {
+ return false;
+ }
+ throw notFunction(callable);
+ }
+
+ private static ECMAException notFunction(final Object obj) {
+ return typeError("not.a.function", ScriptRuntime.safeToString(obj));
+ }
+
+ private static boolean isJSObjectFunction(final Object obj) {
+ return obj instanceof JSObject && ((JSObject)obj).isFunction();
+ }
+
+ /**
* Returns if the given object is a dynalink Dynamic method
* @param obj object to be checked
* @return true if the obj is a dynamic method
*/
public static boolean isDynamicMethod(final Object obj) {
- return obj instanceof BoundDynamicMethod || BeansLinker.isDynamicMethod(obj);
+ return BeansLinker.isDynamicMethod(obj instanceof BoundCallable ? ((BoundCallable)obj).getCallable() : obj);
}
/**
@@ -150,7 +191,7 @@ public final class Bootstrap {
* @return true if the obj is an instance of @FunctionalInterface interface
*/
public static boolean isFunctionalInterfaceObject(final Object obj) {
- return !JSType.isPrimitive(obj) && (NashornBottomLinker.getFunctionalInterfaceMethod(obj.getClass()) != null);
+ return !JSType.isPrimitive(obj) && (NashornBeansLinker.getFunctionalInterfaceMethod(obj.getClass()) != null);
}
/**
@@ -337,6 +378,20 @@ public final class Bootstrap {
/**
* Returns a dynamic invoker for a specified dynamic operation using the public lookup. Similar to
+ * {@link #createDynamicInvoker(String, Class, Class...)} but with an additional parameter to
+ * set the call site flags of the dynamic invoker.
+ * @param opDesc Dynalink dynamic operation descriptor.
+ * @param flags the call site flags for the operation
+ * @param rtype the return type for the operation
+ * @param ptypes the parameter types for the operation
+ * @return MethodHandle for invoking the operation.
+ */
+ public static MethodHandle createDynamicInvoker(final String opDesc, final int flags, final Class<?> rtype, final Class<?>... ptypes) {
+ return bootstrap(MethodHandles.publicLookup(), opDesc, MethodType.methodType(rtype, ptypes), flags).dynamicInvoker();
+ }
+
+ /**
+ * Returns a dynamic invoker for a specified dynamic operation using the public lookup. Similar to
* {@link #createDynamicInvoker(String, Class, Class...)} but with return and parameter types composed into a
* method type in the signature. See the discussion of that method for details.
* @param opDesc Dynalink dynamic operation descriptor.
@@ -348,14 +403,22 @@ public final class Bootstrap {
}
/**
- * Binds a bean dynamic method (returned by invoking {@code dyn:getMethod} on an object linked with
- * {@code BeansLinker} to a receiver.
- * @param dynamicMethod the dynamic method to bind
+ * Binds any object Nashorn can use as a [[Callable]] to a receiver and optionally arguments.
+ * @param callable the callable to bind
* @param boundThis the bound "this" value.
- * @return a bound dynamic method.
+ * @param boundArgs the bound arguments. Can be either null or empty array to signify no arguments are bound.
+ * @return a bound callable.
+ * @throws ECMAException with {@code TypeError} if the object is not a callable.
*/
- public static Object bindDynamicMethod(final Object dynamicMethod, final Object boundThis) {
- return new BoundDynamicMethod(dynamicMethod, boundThis);
+ public static Object bindCallable(final Object callable, final Object boundThis, final Object[] boundArgs) {
+ if (callable instanceof ScriptFunctionImpl) {
+ return ((ScriptFunctionImpl)callable).makeBoundFunction(boundThis, boundArgs);
+ } else if (callable instanceof BoundCallable) {
+ return ((BoundCallable)callable).bind(boundArgs);
+ } else if (isCallable(callable)) {
+ return new BoundCallable(callable, boundThis, boundArgs);
+ }
+ throw notFunction(callable);
}
/**
@@ -406,4 +469,31 @@ public final class Bootstrap {
static GuardedInvocation asTypeSafeReturn(final GuardedInvocation inv, final LinkerServices linkerServices, final CallSiteDescriptor desc) {
return inv == null ? null : inv.asTypeSafeReturn(linkerServices, desc.getMethodType());
}
+
+ /**
+ * Adapts the return type of the method handle with {@code explicitCastArguments} when it is an unboxing
+ * conversion. This will ensure that nulls are unwrapped to false or 0.
+ * @param target the target method handle
+ * @param newType the desired new type. Note that this method does not adapt the method handle completely to the
+ * new type, it only adapts the return type; this is allowed as per
+ * {@link DynamicLinkerFactory#setAutoConversionStrategy(MethodTypeConversionStrategy)}, which is what this method
+ * is used for.
+ * @return the method handle with adapted return type, if it required an unboxing conversion.
+ */
+ private static MethodHandle unboxReturnType(final MethodHandle target, final MethodType newType) {
+ final MethodType targetType = target.type();
+ final Class<?> oldReturnType = targetType.returnType();
+ final Class<?> newReturnType = newType.returnType();
+ if (TypeUtilities.isWrapperType(oldReturnType)) {
+ if (newReturnType.isPrimitive()) {
+ // The contract of setAutoConversionStrategy is such that the difference between newType and targetType
+ // can only be JLS method invocation conversions.
+ assert TypeUtilities.isMethodInvocationConvertible(oldReturnType, newReturnType);
+ return MethodHandles.explicitCastArguments(target, targetType.changeReturnType(newReturnType));
+ }
+ } else if (oldReturnType == void.class && newReturnType == Object.class) {
+ return MethodHandles.filterReturnValue(target, VOID_TO_OBJECT);
+ }
+ return target;
+ }
}
diff --git a/src/jdk/nashorn/internal/runtime/linker/BoundCallable.java b/src/jdk/nashorn/internal/runtime/linker/BoundCallable.java
new file mode 100644
index 00000000..a0eee0de
--- /dev/null
+++ b/src/jdk/nashorn/internal/runtime/linker/BoundCallable.java
@@ -0,0 +1,96 @@
+/*
+ * 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 java.util.Arrays;
+import jdk.nashorn.internal.runtime.ScriptRuntime;
+
+/**
+ * Represents a Nashorn callable bound to a receiver and optionally arguments. Note that objects of this class
+ * are just the tuples of a callable and a bound this and arguments, without any behavior. All the behavior is
+ * defined in the {@code BoundCallableLinker}.
+ */
+public final class BoundCallable {
+ private final Object callable;
+ private final Object boundThis;
+ private final Object[] boundArgs;
+
+ BoundCallable(final Object callable, final Object boundThis, final Object[] boundArgs) {
+ this.callable = callable;
+ this.boundThis = boundThis;
+ this.boundArgs = isEmptyArray(boundArgs) ? ScriptRuntime.EMPTY_ARRAY : boundArgs.clone();
+ }
+
+ private BoundCallable(final BoundCallable original, final Object[] extraBoundArgs) {
+ this.callable = original.callable;
+ this.boundThis = original.boundThis;
+ this.boundArgs = original.concatenateBoundArgs(extraBoundArgs);
+ }
+
+ Object getCallable() {
+ return callable;
+ }
+
+ Object getBoundThis() {
+ return boundThis;
+ }
+
+ Object[] getBoundArgs() {
+ return boundArgs;
+ }
+
+ BoundCallable bind(final Object[] extraBoundArgs) {
+ if (isEmptyArray(extraBoundArgs)) {
+ return this;
+ }
+ return new BoundCallable(this, extraBoundArgs);
+ }
+
+ private Object[] concatenateBoundArgs(final Object[] extraBoundArgs) {
+ if (boundArgs.length == 0) {
+ return extraBoundArgs.clone();
+ }
+ final int origBoundArgsLen = boundArgs.length;
+ final int extraBoundArgsLen = extraBoundArgs.length;
+ final Object[] newBoundArgs = new Object[origBoundArgsLen + extraBoundArgsLen];
+ System.arraycopy(boundArgs, 0, newBoundArgs, 0, origBoundArgsLen);
+ System.arraycopy(extraBoundArgs, 0, newBoundArgs, origBoundArgsLen, extraBoundArgsLen);
+ return newBoundArgs;
+ }
+
+ private static boolean isEmptyArray(final Object[] a) {
+ return a == null || a.length == 0;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder b = new StringBuilder(callable.toString()).append(" on ").append(boundThis);
+ if (boundArgs.length != 0) {
+ b.append(" with ").append(Arrays.toString(boundArgs));
+ }
+ return b.toString();
+ }
+}
diff --git a/src/jdk/nashorn/internal/runtime/linker/BoundCallableLinker.java b/src/jdk/nashorn/internal/runtime/linker/BoundCallableLinker.java
new file mode 100644
index 00000000..d52063bf
--- /dev/null
+++ b/src/jdk/nashorn/internal/runtime/linker/BoundCallableLinker.java
@@ -0,0 +1,132 @@
+/*
+ * 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 java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.util.Arrays;
+import jdk.internal.dynalink.CallSiteDescriptor;
+import jdk.internal.dynalink.linker.GuardedInvocation;
+import jdk.internal.dynalink.linker.LinkRequest;
+import jdk.internal.dynalink.linker.LinkerServices;
+import jdk.internal.dynalink.linker.TypeBasedGuardingDynamicLinker;
+import jdk.internal.dynalink.support.Guards;
+
+/**
+ * Links {@link BoundCallable} objects. Passes through to linker services for linking a callable (for either
+ * "dyn:call" or "dyn:new"), and modifies the returned invocation to deal with the receiver and argument binding.
+ */
+final class BoundCallableLinker implements TypeBasedGuardingDynamicLinker {
+ @Override
+ public boolean canLinkType(final Class<?> type) {
+ return type == BoundCallable.class;
+ }
+
+ @Override
+ public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest, final LinkerServices linkerServices) throws Exception {
+ final Object objBoundCallable = linkRequest.getReceiver();
+ if(!(objBoundCallable instanceof BoundCallable)) {
+ return null;
+ }
+
+ final CallSiteDescriptor descriptor = linkRequest.getCallSiteDescriptor();
+ if (descriptor.getNameTokenCount() < 2 || !"dyn".equals(descriptor.getNameToken(CallSiteDescriptor.SCHEME))) {
+ return null;
+ }
+ final String operation = descriptor.getNameToken(CallSiteDescriptor.OPERATOR);
+ // We need to distinguish "dyn:new" from "dyn:call" because "dyn:call" sites have parameter list of the form
+ // "callee, this, args", while "dyn:call" sites have "callee, args" -- they lack the "this" parameter.
+ final boolean isCall;
+ if ("new".equals(operation)) {
+ isCall = false;
+ } else if ("call".equals(operation)) {
+ isCall = true;
+ } else {
+ // Only dyn:call and dyn:new are supported.
+ return null;
+ }
+ final BoundCallable boundCallable = (BoundCallable)objBoundCallable;
+ final Object callable = boundCallable.getCallable();
+ final Object boundThis = boundCallable.getBoundThis();
+
+ // We need to ask the linker services for a delegate invocation on the target callable.
+
+ // Replace arguments (boundCallable[, this], args) => (callable[, boundThis], boundArgs, args) when delegating
+ final Object[] args = linkRequest.getArguments();
+ final Object[] boundArgs = boundCallable.getBoundArgs();
+ final int argsLen = args.length;
+ final int boundArgsLen = boundArgs.length;
+ final Object[] newArgs = new Object[argsLen + boundArgsLen];
+ newArgs[0] = callable;
+ final int firstArgIndex;
+ if (isCall) {
+ newArgs[1] = boundThis;
+ firstArgIndex = 2;
+ } else {
+ firstArgIndex = 1;
+ }
+ System.arraycopy(boundArgs, 0, newArgs, firstArgIndex, boundArgsLen);
+ System.arraycopy(args, firstArgIndex, newArgs, firstArgIndex + boundArgsLen, argsLen - firstArgIndex);
+
+ // Use R(T0, T1, T2, ...) => R(callable.class, boundThis.class, boundArg0.class, ..., boundArgn.class, T2, ...)
+ // call site type when delegating to underlying linker (for dyn:new, there's no this).
+ final MethodType type = descriptor.getMethodType();
+ // Use R(T0, ...) => R(callable.class, ...)
+ MethodType newMethodType = descriptor.getMethodType().changeParameterType(0, callable.getClass());
+ if (isCall) {
+ // R(callable.class, T1, ...) => R(callable.class, boundThis.class, ...)
+ newMethodType = newMethodType.changeParameterType(1, boundThis.getClass());
+ }
+ // R(callable.class[, boundThis.class], T2, ...) => R(callable.class[, boundThis.class], boundArg0.class, ..., boundArgn.class, T2, ...)
+ for(int i = boundArgs.length; i-- > 0;) {
+ newMethodType = newMethodType.insertParameterTypes(firstArgIndex, boundArgs[i] == null ? Object.class : boundArgs[i].getClass());
+ }
+ final CallSiteDescriptor newDescriptor = descriptor.changeMethodType(newMethodType);
+
+ // Delegate to target's linker
+ final GuardedInvocation inv = linkerServices.getGuardedInvocation(linkRequest.replaceArguments(newDescriptor, newArgs));
+ if(inv == null) {
+ return null;
+ }
+
+ // Bind (callable[, boundThis], boundArgs) to the delegate handle
+ final MethodHandle boundHandle = MethodHandles.insertArguments(inv.getInvocation(), 0,
+ Arrays.copyOf(newArgs, firstArgIndex + boundArgs.length));
+ final Class<?> p0Type = type.parameterType(0);
+ final MethodHandle droppingHandle;
+ if (isCall) {
+ // Ignore incoming boundCallable and this
+ droppingHandle = MethodHandles.dropArguments(boundHandle, 0, p0Type, type.parameterType(1));
+ } else {
+ // Ignore incoming boundCallable
+ droppingHandle = MethodHandles.dropArguments(boundHandle, 0, p0Type);
+ }
+ // Identity guard on boundCallable object
+ final MethodHandle newGuard = Guards.getIdentityGuard(boundCallable);
+ return inv.replaceMethods(droppingHandle, newGuard.asType(newGuard.type().changeParameterType(0, p0Type)));
+ }
+}
diff --git a/src/jdk/nashorn/internal/runtime/linker/BoundDynamicMethodLinker.java b/src/jdk/nashorn/internal/runtime/linker/BoundDynamicMethodLinker.java
deleted file mode 100644
index 67e29835..00000000
--- a/src/jdk/nashorn/internal/runtime/linker/BoundDynamicMethodLinker.java
+++ /dev/null
@@ -1,91 +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.runtime.linker;
-
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.MethodType;
-import jdk.internal.dynalink.CallSiteDescriptor;
-import jdk.internal.dynalink.beans.BeansLinker;
-import jdk.internal.dynalink.linker.GuardedInvocation;
-import jdk.internal.dynalink.linker.LinkRequest;
-import jdk.internal.dynalink.linker.LinkerServices;
-import jdk.internal.dynalink.linker.TypeBasedGuardingDynamicLinker;
-import jdk.internal.dynalink.support.Guards;
-
-/**
- * Links {@code BoundDynamicMethod} objects. Passes through to Dynalink's BeansLinker for linking a dynamic method
- * (they only respond to "dyn:call"), and modifies the returned invocation to deal with the receiver binding.
- */
-final class BoundDynamicMethodLinker implements TypeBasedGuardingDynamicLinker {
- @Override
- public boolean canLinkType(final Class<?> type) {
- return type == BoundDynamicMethod.class;
- }
-
- @Override
- public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest, final LinkerServices linkerServices) throws Exception {
- final Object objBoundDynamicMethod = linkRequest.getReceiver();
- if(!(objBoundDynamicMethod instanceof BoundDynamicMethod)) {
- return null;
- }
-
- final BoundDynamicMethod boundDynamicMethod = (BoundDynamicMethod)objBoundDynamicMethod;
- final Object dynamicMethod = boundDynamicMethod.getDynamicMethod();
- final Object boundThis = boundDynamicMethod.getBoundThis();
-
- // Replace arguments (boundDynamicMethod, this, ...) => (dynamicMethod, boundThis, ...) when delegating to
- // BeansLinker
- final Object[] args = linkRequest.getArguments();
- args[0] = dynamicMethod;
- args[1] = boundThis;
-
- // Use R(T0, T1, ...) => R(dynamicMethod.class, boundThis.class, ...) call site type when delegating to
- // BeansLinker.
- final CallSiteDescriptor descriptor = linkRequest.getCallSiteDescriptor();
- final MethodType type = descriptor.getMethodType();
- final Class<?> dynamicMethodClass = dynamicMethod.getClass();
- final CallSiteDescriptor newDescriptor = descriptor.changeMethodType(
- type.changeParameterType(0, dynamicMethodClass).changeParameterType(1, boundThis.getClass()));
-
- // Delegate to BeansLinker
- final GuardedInvocation inv = NashornBeansLinker.getGuardedInvocation(BeansLinker.getLinkerForClass(dynamicMethodClass),
- linkRequest.replaceArguments(newDescriptor, args), linkerServices);
- if(inv == null) {
- return null;
- }
-
- // Bind (dynamicMethod, boundThis) to the handle
- final MethodHandle boundHandle = MethodHandles.insertArguments(inv.getInvocation(), 0, dynamicMethod, boundThis);
- final Class<?> p0Type = type.parameterType(0);
- // Ignore incoming (boundDynamicMethod, this)
- final MethodHandle droppingHandle = MethodHandles.dropArguments(boundHandle, 0, p0Type, type.parameterType(1));
- // Identity guard on boundDynamicMethod object
- final MethodHandle newGuard = Guards.getIdentityGuard(boundDynamicMethod);
-
- return inv.replaceMethods(droppingHandle, newGuard.asType(newGuard.type().changeParameterType(0, p0Type)));
- }
-}
diff --git a/src/jdk/nashorn/internal/runtime/linker/BrowserJSObjectLinker.java b/src/jdk/nashorn/internal/runtime/linker/BrowserJSObjectLinker.java
index e8cfa4ed..d2f23e82 100644
--- a/src/jdk/nashorn/internal/runtime/linker/BrowserJSObjectLinker.java
+++ b/src/jdk/nashorn/internal/runtime/linker/BrowserJSObjectLinker.java
@@ -25,8 +25,11 @@
package jdk.nashorn.internal.runtime.linker;
-import static jdk.nashorn.internal.runtime.linker.BrowserJSObjectLinker.JSObjectHandles.*;
-
+import static jdk.nashorn.internal.runtime.linker.BrowserJSObjectLinker.JSObjectHandles.JSOBJECT_GETMEMBER;
+import static jdk.nashorn.internal.runtime.linker.BrowserJSObjectLinker.JSObjectHandles.JSOBJECT_GETSLOT;
+import static jdk.nashorn.internal.runtime.linker.BrowserJSObjectLinker.JSObjectHandles.JSOBJECT_SETMEMBER;
+import static jdk.nashorn.internal.runtime.linker.BrowserJSObjectLinker.JSObjectHandles.JSOBJECT_SETSLOT;
+import static jdk.nashorn.internal.runtime.linker.BrowserJSObjectLinker.JSObjectHandles.JSOBJECT_CALL;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import jdk.internal.dynalink.CallSiteDescriptor;
@@ -114,15 +117,15 @@ final class BrowserJSObjectLinker implements TypeBasedGuardingDynamicLinker {
case "getMethod":
if (c > 2) {
return findGetMethod(desc);
- } else {
- // For indexed get, we want GuardedInvocation from beans linker and pass it.
- // BrowserJSObjectLinker.get uses this fallback getter for explicit signature method access.
- final GuardedInvocation beanInv = nashornBeansLinker.getGuardedInvocation(request, linkerServices);
- return findGetIndexMethod(beanInv);
}
+ // For indexed get, we want GuardedInvocation from beans linker and pass it.
+ // BrowserJSObjectLinker.get uses this fallback getter for explicit signature method access.
+ return findGetIndexMethod(nashornBeansLinker.getGuardedInvocation(request, linkerServices));
case "setProp":
case "setElem":
return c > 2 ? findSetMethod(desc) : findSetIndexMethod();
+ case "call":
+ return findCallMethod(desc);
default:
return null;
}
@@ -148,6 +151,11 @@ final class BrowserJSObjectLinker implements TypeBasedGuardingDynamicLinker {
return new GuardedInvocation(JSOBJECTLINKER_PUT, IS_JSOBJECT_GUARD);
}
+ private static GuardedInvocation findCallMethod(final CallSiteDescriptor desc) {
+ final MethodHandle call = MH.insertArguments(JSOBJECT_CALL, 1, "call");
+ return new GuardedInvocation(MH.asCollector(call, Object[].class, desc.getMethodType().parameterCount() - 1), IS_JSOBJECT_GUARD);
+ }
+
@SuppressWarnings("unused")
private static boolean isJSObject(final Object self) {
return jsObjectClass.isInstance(self);
@@ -166,9 +174,8 @@ final class BrowserJSObjectLinker implements TypeBasedGuardingDynamicLinker {
final String name = (String)key;
if (name.indexOf('(') != -1) {
return fallback.invokeExact(jsobj, key);
- } else {
- return JSOBJECT_GETMEMBER.invokeExact(jsobj, (String)key);
}
+ return JSOBJECT_GETMEMBER.invokeExact(jsobj, (String)key);
}
return null;
}
@@ -208,6 +215,7 @@ final class BrowserJSObjectLinker implements TypeBasedGuardingDynamicLinker {
static final MethodHandle JSOBJECT_GETSLOT = findJSObjectMH_V("getSlot", Object.class, int.class).asType(MH.type(Object.class, Object.class, int.class));
static final MethodHandle JSOBJECT_SETMEMBER = findJSObjectMH_V("setMember", Void.TYPE, String.class, Object.class).asType(MH.type(Void.TYPE, Object.class, String.class, Object.class));
static final MethodHandle JSOBJECT_SETSLOT = findJSObjectMH_V("setSlot", Void.TYPE, int.class, Object.class).asType(MH.type(Void.TYPE, Object.class, int.class, Object.class));
+ static final MethodHandle JSOBJECT_CALL = findJSObjectMH_V("call", Object.class, String.class, Object[].class).asType(MH.type(Object.class, Object.class, String.class, Object[].class));
private static MethodHandle findJSObjectMH_V(final String name, final Class<?> rtype, final Class<?>... types) {
checkJSObjectClass();
diff --git a/src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java b/src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java
index 67c8e4bb..aaf5a2c3 100644
--- a/src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java
+++ b/src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java
@@ -120,12 +120,10 @@ final class JSObjectLinker implements TypeBasedGuardingDynamicLinker, GuardingTy
case "getMethod":
if (c > 2) {
return findGetMethod(desc);
- } else {
- // For indexed get, we want get GuardedInvocation beans linker and pass it.
- // JSObjectLinker.get uses this fallback getter for explicit signature method access.
- final GuardedInvocation beanInv = nashornBeansLinker.getGuardedInvocation(request, linkerServices);
- return findGetIndexMethod(beanInv);
}
+ // For indexed get, we want get GuardedInvocation beans linker and pass it.
+ // JSObjectLinker.get uses this fallback getter for explicit signature method access.
+ return findGetIndexMethod(nashornBeansLinker.getGuardedInvocation(request, linkerServices));
case "setProp":
case "setElem":
return c > 2 ? findSetMethod(desc) : findSetIndexMethod();
@@ -192,9 +190,8 @@ final class JSObjectLinker implements TypeBasedGuardingDynamicLinker, GuardingTy
// get with method name and signature. delegate it to beans linker!
if (name.indexOf('(') != -1) {
return fallback.invokeExact(jsobj, key);
- } else {
- return ((JSObject)jsobj).getMember(name);
}
+ return ((JSObject)jsobj).getMember(name);
}
return null;
}
diff --git a/src/jdk/nashorn/internal/runtime/linker/JavaSuperAdapterLinker.java b/src/jdk/nashorn/internal/runtime/linker/JavaSuperAdapterLinker.java
index 5fc93cb4..9aeefd72 100644
--- a/src/jdk/nashorn/internal/runtime/linker/JavaSuperAdapterLinker.java
+++ b/src/jdk/nashorn/internal/runtime/linker/JavaSuperAdapterLinker.java
@@ -165,7 +165,7 @@ final class JavaSuperAdapterLinker implements TypeBasedGuardingDynamicLinker {
*/
@SuppressWarnings("unused")
private static Object bindDynamicMethod(final Object dynamicMethod, final Object boundThis) {
- return dynamicMethod == null ? ScriptRuntime.UNDEFINED : Bootstrap.bindDynamicMethod(dynamicMethod, boundThis);
+ return dynamicMethod == null ? ScriptRuntime.UNDEFINED : Bootstrap.bindCallable(dynamicMethod, boundThis, null);
}
/**
diff --git a/src/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java b/src/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java
index 25ba619b..da12af2d 100644
--- a/src/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java
+++ b/src/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java
@@ -25,20 +25,29 @@
package jdk.nashorn.internal.runtime.linker;
+import static jdk.nashorn.internal.lookup.Lookup.MH;
+import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
+
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.beans.BeansLinker;
import jdk.internal.dynalink.linker.ConversionComparator.Comparison;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.GuardingDynamicLinker;
import jdk.internal.dynalink.linker.LinkRequest;
import jdk.internal.dynalink.linker.LinkerServices;
+import jdk.internal.dynalink.support.Guards;
import jdk.internal.dynalink.support.Lookup;
import jdk.nashorn.api.scripting.ScriptUtils;
import jdk.nashorn.internal.objects.NativeArray;
import jdk.nashorn.internal.runtime.ConsString;
+import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.ScriptObject;
+import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.options.Options;
/**
@@ -53,15 +62,64 @@ public class NashornBeansLinker implements GuardingDynamicLinker {
// Object type arguments of Java method calls, field set and array set.
private static final boolean MIRROR_ALWAYS = Options.getBooleanProperty("nashorn.mirror.always", true);
- private static final MethodHandle EXPORT_ARGUMENT = new Lookup(MethodHandles.lookup()).findOwnStatic("exportArgument", Object.class, Object.class);
- private static final MethodHandle EXPORT_NATIVE_ARRAY = new Lookup(MethodHandles.lookup()).findOwnStatic("exportNativeArray", Object.class, NativeArray.class);
- private static final MethodHandle EXPORT_SCRIPT_OBJECT = new Lookup(MethodHandles.lookup()).findOwnStatic("exportScriptObject", Object.class, ScriptObject.class);
- private static final MethodHandle IMPORT_RESULT = new Lookup(MethodHandles.lookup()).findOwnStatic("importResult", Object.class, Object.class);
+ private static final MethodHandle EXPORT_ARGUMENT;
+ private static final MethodHandle EXPORT_NATIVE_ARRAY;
+ private static final MethodHandle EXPORT_SCRIPT_OBJECT;
+ private static final MethodHandle IMPORT_RESULT;
+ private static final MethodHandle FILTER_CONSSTRING;
+
+ static {
+ final Lookup lookup = new Lookup(MethodHandles.lookup());
+ EXPORT_ARGUMENT = lookup.findOwnStatic("exportArgument", Object.class, Object.class);
+ EXPORT_NATIVE_ARRAY = lookup.findOwnStatic("exportNativeArray", Object.class, NativeArray.class);
+ EXPORT_SCRIPT_OBJECT = lookup.findOwnStatic("exportScriptObject", Object.class, ScriptObject.class);
+ IMPORT_RESULT = lookup.findOwnStatic("importResult", Object.class, Object.class);
+ FILTER_CONSSTRING = lookup.findOwnStatic("consStringFilter", Object.class, Object.class);
+ }
+
+ // cache of @FunctionalInterface method of implementor classes
+ private static final ClassValue<Method> FUNCTIONAL_IFACE_METHOD = new ClassValue<Method>() {
+ @Override
+ protected Method computeValue(final Class<?> type) {
+ return findFunctionalInterfaceMethod(type);
+ }
+ };
private final BeansLinker beansLinker = new BeansLinker();
@Override
public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest, final LinkerServices linkerServices) throws Exception {
+ final Object self = linkRequest.getReceiver();
+ final CallSiteDescriptor desc = linkRequest.getCallSiteDescriptor();
+ if (self instanceof ConsString) {
+ // In order to treat ConsString like a java.lang.String we need a link request with a string receiver.
+ final Object[] arguments = linkRequest.getArguments();
+ arguments[0] = "";
+ final LinkRequest forgedLinkRequest = linkRequest.replaceArguments(desc, arguments);
+ final GuardedInvocation invocation = getGuardedInvocation(beansLinker, forgedLinkRequest, linkerServices);
+ // If an invocation is found we add a filter that makes it work for both Strings and ConsStrings.
+ return invocation == null ? null : invocation.filterArguments(0, FILTER_CONSSTRING);
+ }
+
+ if (self != null && "call".equals(desc.getNameToken(CallSiteDescriptor.OPERATOR))) {
+ // Support dyn:call on any object that supports some @FunctionalInterface
+ // annotated interface. This way Java method, constructor references or
+ // implementations of java.util.function.* interfaces can be called as though
+ // those are script functions.
+ final Method m = getFunctionalInterfaceMethod(self.getClass());
+ if (m != null) {
+ final MethodType callType = desc.getMethodType();
+ // 'callee' and 'thiz' passed from script + actual arguments
+ if (callType.parameterCount() != m.getParameterCount() + 2) {
+ throw typeError("no.method.matches.args", ScriptRuntime.safeToString(self));
+ }
+ return new GuardedInvocation(
+ // drop 'thiz' passed from the script.
+ MH.dropArguments(desc.getLookup().unreflect(m), 1, callType.parameterType(1)),
+ Guards.getInstanceOfGuard(m.getDeclaringClass())).asTypeSafeReturn(
+ new NashornBeansLinkerServices(linkerServices), callType);
+ }
+ }
return getGuardedInvocation(beansLinker, linkRequest, linkerServices);
}
@@ -113,6 +171,43 @@ public class NashornBeansLinker implements GuardingDynamicLinker {
return ScriptUtils.unwrap(arg);
}
+ @SuppressWarnings("unused")
+ private static Object consStringFilter(final Object arg) {
+ return arg instanceof ConsString ? arg.toString() : arg;
+ }
+
+ private static Method findFunctionalInterfaceMethod(final Class<?> clazz) {
+ if (clazz == null) {
+ return null;
+ }
+
+ for (final Class<?> iface : clazz.getInterfaces()) {
+ // check accessiblity up-front
+ if (! Context.isAccessibleClass(iface)) {
+ continue;
+ }
+
+ // check for @FunctionalInterface
+ if (iface.isAnnotationPresent(FunctionalInterface.class)) {
+ // return the first abstract method
+ for (final Method m : iface.getMethods()) {
+ if (Modifier.isAbstract(m.getModifiers())) {
+ return m;
+ }
+ }
+ }
+ }
+
+ // did not find here, try super class
+ return findFunctionalInterfaceMethod(clazz.getSuperclass());
+ }
+
+ // Returns @FunctionalInterface annotated interface's single abstract
+ // method. If not found, returns null.
+ static Method getFunctionalInterfaceMethod(final Class<?> clazz) {
+ return FUNCTIONAL_IFACE_METHOD.get(clazz);
+ }
+
private static class NashornBeansLinkerServices implements LinkerServices {
private final LinkerServices linkerServices;
diff --git a/src/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java b/src/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java
index 4ed6e3a2..2cbbf065 100644
--- a/src/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java
+++ b/src/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java
@@ -30,9 +30,6 @@ import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodType;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import jdk.internal.dynalink.CallSiteDescriptor;
@@ -45,7 +42,6 @@ import jdk.internal.dynalink.linker.LinkRequest;
import jdk.internal.dynalink.linker.LinkerServices;
import jdk.internal.dynalink.support.Guards;
import jdk.nashorn.internal.codegen.types.Type;
-import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.UnwarrantedOptimismException;
@@ -95,22 +91,6 @@ final class NashornBottomLinker implements GuardingDynamicLinker, GuardingTypeCo
}
throw typeError("not.a.function", ScriptRuntime.safeToString(self));
case "call":
- // Support dyn:call on any object that supports some @FunctionalInterface
- // annotated interface. This way Java method, constructor references or
- // implementations of java.util.function.* interfaces can be called as though
- // those are script functions.
- final Method m = getFunctionalInterfaceMethod(self.getClass());
- if (m != null) {
- final MethodType callType = desc.getMethodType();
- // 'callee' and 'thiz' passed from script + actual arguments
- if (callType.parameterCount() != m.getParameterCount() + 2) {
- throw typeError("no.method.matches.args", ScriptRuntime.safeToString(self));
- }
- return Bootstrap.asTypeSafeReturn(new GuardedInvocation(
- // drop 'thiz' passed from the script.
- MH.dropArguments(desc.getLookup().unreflect(m), 1, callType.parameterType(1)),
- Guards.getInstanceOfGuard(m.getDeclaringClass())), linkerServices, desc);
- }
if(BeansLinker.isDynamicConstructor(self)) {
throw typeError("constructor.requires.new", ScriptRuntime.safeToString(self));
}
@@ -218,44 +198,4 @@ final class NashornBottomLinker implements GuardingDynamicLinker, GuardingTypeCo
}
return ScriptRuntime.safeToString(linkRequest.getArguments()[1]);
}
-
- // cache of @FunctionalInterface method of implementor classes
- private static final ClassValue<Method> FUNCTIONAL_IFACE_METHOD = new ClassValue<Method>() {
- @Override
- protected Method computeValue(final Class<?> type) {
- return findFunctionalInterfaceMethod(type);
- }
-
- private Method findFunctionalInterfaceMethod(final Class<?> clazz) {
- if (clazz == null) {
- return null;
- }
-
- for (final Class<?> iface : clazz.getInterfaces()) {
- // check accessiblity up-front
- if (! Context.isAccessibleClass(iface)) {
- continue;
- }
-
- // check for @FunctionalInterface
- if (iface.isAnnotationPresent(FunctionalInterface.class)) {
- // return the first abstract method
- for (final Method m : iface.getMethods()) {
- if (Modifier.isAbstract(m.getModifiers())) {
- return m;
- }
- }
- }
- }
-
- // did not find here, try super class
- return findFunctionalInterfaceMethod(clazz.getSuperclass());
- }
- };
-
- // Returns @FunctionalInterface annotated interface's single abstract
- // method. If not found, returns null.
- static Method getFunctionalInterfaceMethod(final Class<?> clazz) {
- return FUNCTIONAL_IFACE_METHOD.get(clazz);
- }
}
diff --git a/src/jdk/nashorn/internal/runtime/regexp/joni/Analyser.java b/src/jdk/nashorn/internal/runtime/regexp/joni/Analyser.java
index 6a9ebc1a..64d90c03 100644
--- a/src/jdk/nashorn/internal/runtime/regexp/joni/Analyser.java
+++ b/src/jdk/nashorn/internal/runtime/regexp/joni/Analyser.java
@@ -27,7 +27,6 @@ import static jdk.nashorn.internal.runtime.regexp.joni.Option.isIgnoreCase;
import static jdk.nashorn.internal.runtime.regexp.joni.Option.isMultiline;
import static jdk.nashorn.internal.runtime.regexp.joni.ast.ConsAltNode.newAltNode;
import static jdk.nashorn.internal.runtime.regexp.joni.ast.QuantifierNode.isRepeatInfinite;
-
import java.util.HashSet;
import jdk.nashorn.internal.runtime.regexp.joni.ast.AnchorNode;
import jdk.nashorn.internal.runtime.regexp.joni.ast.BackRefNode;
@@ -53,6 +52,7 @@ final class Analyser extends Parser {
super(env, chars, p, end);
}
+ @SuppressWarnings("unused")
protected final void compile() {
if (Config.DEBUG) {
Config.log.println(new String(chars, getBegin(), getEnd()));
@@ -76,7 +76,9 @@ final class Analyser extends Parser {
root = setupTree(root, 0);
if (Config.DEBUG_PARSE_TREE) {
- if (Config.DEBUG_PARSE_TREE_RAW) Config.log.println("<TREE>");
+ if (Config.DEBUG_PARSE_TREE_RAW) {
+ Config.log.println("<TREE>");
+ }
root.verifyTree(new HashSet<Node>(), env.reg.warnings);
Config.log.println(root + "\n");
}
@@ -94,7 +96,9 @@ final class Analyser extends Parser {
regex.clearOptimizeInfo();
- if (!Config.DONT_OPTIMIZE) setOptimizedInfoFromTree(root);
+ if (!Config.DONT_OPTIMIZE) {
+ setOptimizedInfoFromTree(root);
+ }
env.memNodes = null;
@@ -110,7 +114,9 @@ final class Analyser extends Parser {
if (Config.DEBUG_COMPILE) {
Config.log.println("stack used: " + regex.stackNeeded);
- if (Config.USE_STRING_TEMPLATES) Config.log.print("templates: " + regex.templateNum + "\n");
+ if (Config.USE_STRING_TEMPLATES) {
+ Config.log.print("templates: " + regex.templateNum + "\n");
+ }
Config.log.println(new ByteCodePrinter(regex).byteCodeListToString());
} // DEBUG_COMPILE
@@ -136,7 +142,9 @@ final class Analyser extends Parser {
ConsAltNode can = (ConsAltNode)node;
do {
final int v = quantifiersMemoryInfo(can.car);
- if (v > info) info = v;
+ if (v > info) {
+ info = v;
+ }
} while ((can = can.cdr) != null);
break;
@@ -182,7 +190,9 @@ final class Analyser extends Parser {
switch (node.getType()) {
case NodeType.BREF:
final BackRefNode br = (BackRefNode)node;
- if (br.isRecursion()) break;
+ if (br.isRecursion()) {
+ break;
+ }
if (br.backRef > env.numMem) {
throw new ValueException(ERR_INVALID_BACKREF);
@@ -249,6 +259,9 @@ final class Analyser extends Parser {
case EncloseType.STOP_BACKTRACK:
min = getMinMatchLength(en.target);
break;
+
+ default:
+ break;
} // inner switch
break;
@@ -276,7 +289,9 @@ final class Analyser extends Parser {
ConsAltNode an = (ConsAltNode)node;
do {
final int tmax = getMaxMatchLength(an.car);
- if (max < tmax) max = tmax;
+ if (max < tmax) {
+ max = tmax;
+ }
} while ((an = an.cdr) != null);
break;
@@ -304,7 +319,9 @@ final class Analyser extends Parser {
throw new ValueException(ERR_INVALID_BACKREF);
}
final int tmax = getMaxMatchLength(env.memNodes[br.backRef]);
- if (max < tmax) max = tmax;
+ if (max < tmax) {
+ max = tmax;
+ }
break;
case NodeType.QTFR:
@@ -338,6 +355,9 @@ final class Analyser extends Parser {
case EncloseType.STOP_BACKTRACK:
max = getMaxMatchLength(en.target);
break;
+
+ default:
+ break;
} // inner switch
break;
@@ -355,8 +375,8 @@ final class Analyser extends Parser {
return getCharLengthTree(node, 0);
}
- private int getCharLengthTree(final Node node, int level) {
- level++;
+ private int getCharLengthTree(final Node node, final int levelp) {
+ final int level = levelp + 1;
int len = 0;
returnCode = 0;
@@ -366,7 +386,9 @@ final class Analyser extends Parser {
ConsAltNode ln = (ConsAltNode)node;
do {
final int tlen = getCharLengthTree(ln.car, level);
- if (returnCode == 0) len = MinMaxLen.distanceAdd(len, tlen);
+ if (returnCode == 0) {
+ len = MinMaxLen.distanceAdd(len, tlen);
+ }
} while (returnCode == 0 && (ln = ln.cdr) != null);
break;
@@ -378,7 +400,9 @@ final class Analyser extends Parser {
while (returnCode == 0 && (an = an.cdr) != null) {
final int tlen2 = getCharLengthTree(an.car, level);
if (returnCode == 0) {
- if (tlen != tlen2) varLen = true;
+ if (tlen != tlen2) {
+ varLen = true;
+ }
}
}
@@ -404,7 +428,9 @@ final class Analyser extends Parser {
final QuantifierNode qn = (QuantifierNode)node;
if (qn.lower == qn.upper) {
tlen = getCharLengthTree(qn.target, level);
- if (returnCode == 0) len = MinMaxLen.distanceMultiply(tlen, qn.lower);
+ if (returnCode == 0) {
+ len = MinMaxLen.distanceMultiply(tlen, qn.lower);
+ }
} else {
returnCode = GET_CHAR_LEN_VARLEN;
}
@@ -435,6 +461,9 @@ final class Analyser extends Parser {
case EncloseType.STOP_BACKTRACK:
len = getCharLengthTree(en.target, level);
break;
+
+ default:
+ break;
} // inner switch
break;
@@ -448,7 +477,9 @@ final class Analyser extends Parser {
}
/* x is not included y ==> 1 : 0 */
- private boolean isNotIncluded(Node x, Node y) {
+ private static boolean isNotIncluded(final Node xn, final Node yn) {
+ Node x = xn;
+ Node y = yn;
Node tmp;
// !retry:!
@@ -492,10 +523,14 @@ final class Analyser extends Parser {
boolean v = xc.bs.at(i);
if ((v && !xc.isNot()) || (!v && xc.isNot())) {
v = yc.bs.at(i);
- if ((v && !yc.isNot()) || (!v && yc.isNot())) return false;
+ if ((v && !yc.isNot()) || (!v && yc.isNot())) {
+ return false;
+ }
}
}
- if ((xc.mbuf == null && !xc.isNot()) || yc.mbuf == null && !yc.isNot()) return true;
+ if ((xc.mbuf == null && !xc.isNot()) || yc.mbuf == null && !yc.isNot()) {
+ return true;
+ }
return false;
// break; not reached
@@ -514,7 +549,9 @@ final class Analyser extends Parser {
case NodeType.STR:
final StringNode xs = (StringNode)x;
- if (xs.length() == 0) break;
+ if (xs.length() == 0) {
+ break;
+ }
switch (yType) {
@@ -526,13 +563,16 @@ final class Analyser extends Parser {
case NodeType.STR:
final StringNode ys = (StringNode)y;
int len = xs.length();
- if (len > ys.length()) len = ys.length();
+ if (len > ys.length()) {
+ len = ys.length();
+ }
if (xs.isAmbig() || ys.isAmbig()) {
/* tiny version */
return false;
- } else {
- for (int i=0, p=ys.p, q=xs.p; i<len; i++, p++, q++) {
- if (ys.chars[p] != xs.chars[q]) return true;
+ }
+ for (int i=0, pt=ys.p, q=xs.p; i<len; i++, pt++, q++) {
+ if (ys.chars[pt] != xs.chars[q]) {
+ return true;
}
}
break;
@@ -542,6 +582,8 @@ final class Analyser extends Parser {
} // inner switch
break; // case NodeType.STR
+ default:
+ break;
} // switch
@@ -561,7 +603,9 @@ final class Analyser extends Parser {
case NodeType.CTYPE:
case NodeType.CCLASS:
- if (!exact) n = node;
+ if (!exact) {
+ n = node;
+ }
break;
case NodeType.LIST:
@@ -570,7 +614,10 @@ final class Analyser extends Parser {
case NodeType.STR:
final StringNode sn = (StringNode)node;
- if (sn.end <= sn.p) break; // ???
+ if (sn.end <= sn.p)
+ {
+ break; // ???
+ }
if (exact && !sn.isRaw() && isIgnoreCase(regex.options)){
// nothing
@@ -605,12 +652,17 @@ final class Analyser extends Parser {
case EncloseType.STOP_BACKTRACK:
n = getHeadValueNode(en.target, exact);
break;
+
+ default:
+ break;
} // inner switch
break;
case NodeType.ANCHOR:
final AnchorNode an = (AnchorNode)node;
- if (an.type == AnchorType.PREC_READ) n = getHeadValueNode(an.target, exact);
+ if (an.type == AnchorType.PREC_READ) {
+ n = getHeadValueNode(an.target, exact);
+ }
break;
default:
@@ -622,7 +674,9 @@ final class Analyser extends Parser {
// true: invalid
private boolean checkTypeTree(final Node node, final int typeMask, final int encloseMask, final int anchorMask) {
- if ((node.getType2Bit() & typeMask) == 0) return true;
+ if ((node.getType2Bit() & typeMask) == 0) {
+ return true;
+ }
boolean invalid = false;
@@ -641,15 +695,21 @@ final class Analyser extends Parser {
case NodeType.ENCLOSE:
final EncloseNode en = (EncloseNode)node;
- if ((en.type & encloseMask) == 0) return true;
+ if ((en.type & encloseMask) == 0) {
+ return true;
+ }
invalid = checkTypeTree(en.target, typeMask, encloseMask, anchorMask);
break;
case NodeType.ANCHOR:
final AnchorNode an = (AnchorNode)node;
- if ((an.type & anchorMask) == 0) return true;
+ if ((an.type & anchorMask) == 0) {
+ return true;
+ }
- if (an.target != null) invalid = checkTypeTree(an.target, typeMask, encloseMask, anchorMask);
+ if (an.target != null) {
+ invalid = checkTypeTree(an.target, typeMask, encloseMask, anchorMask);
+ }
break;
default:
@@ -664,7 +724,8 @@ final class Analyser extends Parser {
(?<=A|B) ==> (?<=A)|(?<=B)
(?<!A|B) ==> (?<!A)(?<!B)
*/
- private Node divideLookBehindAlternatives(Node node) {
+ private Node divideLookBehindAlternatives(final Node nodep) {
+ Node node = nodep;
final AnchorNode an = (AnchorNode)node;
final int anchorType = an.type;
Node head = an.target;
@@ -699,7 +760,7 @@ final class Analyser extends Parser {
private Node setupLookBehind(final Node node) {
final AnchorNode an = (AnchorNode)node;
final int len = getCharLengthTree(an.target);
- switch(returnCode) {
+ switch (returnCode) {
case 0:
an.charLength = len;
break;
@@ -708,14 +769,17 @@ final class Analyser extends Parser {
case GET_CHAR_LEN_TOP_ALT_VARLEN:
if (syntax.differentLengthAltLookBehind()) {
return divideLookBehindAlternatives(node);
- } else {
- throw new SyntaxException(ERR_INVALID_LOOK_BEHIND_PATTERN);
}
+ throw new SyntaxException(ERR_INVALID_LOOK_BEHIND_PATTERN);
+ default:
+ break;
}
return node;
}
- private void nextSetup(Node node, final Node nextNode) {
+ private void nextSetup(final Node nodep, final Node nextNode) {
+ Node node = nodep;
+
// retry:
retry: while(true) {
@@ -762,7 +826,7 @@ final class Analyser extends Parser {
}
private void updateStringNodeCaseFoldMultiByte(final StringNode sn) {
- final char[] chars = sn.chars;
+ final char[] ch = sn.chars;
final int end = sn.end;
value = sn.p;
int sp = 0;
@@ -770,15 +834,15 @@ final class Analyser extends Parser {
while (value < end) {
final int ovalue = value;
- buf = EncodingHelper.toLowerCase(chars[value++]);
+ buf = EncodingHelper.toLowerCase(ch[value++]);
- if (chars[ovalue] != buf) {
+ if (ch[ovalue] != buf) {
char[] sbuf = new char[sn.length() << 1];
- System.arraycopy(chars, sn.p, sbuf, 0, ovalue - sn.p);
+ System.arraycopy(ch, sn.p, sbuf, 0, ovalue - sn.p);
value = ovalue;
while (value < end) {
- buf = EncodingHelper.toLowerCase(chars[value++]);
+ buf = EncodingHelper.toLowerCase(ch[value++]);
if (sp >= sbuf.length) {
final char[]tmp = new char[sbuf.length << 1];
System.arraycopy(sbuf, 0, tmp, 0, sbuf.length);
@@ -798,8 +862,8 @@ final class Analyser extends Parser {
updateStringNodeCaseFoldMultiByte(sn);
}
- private Node expandCaseFoldMakeRemString(final char[] chars, final int p, final int end) {
- final StringNode node = new StringNode(chars, p, end);
+ private Node expandCaseFoldMakeRemString(final char[] ch, final int pp, final int end) {
+ final StringNode node = new StringNode(ch, pp, end);
updateStringNodeCaseFold(node);
node.setAmbig();
@@ -807,7 +871,7 @@ final class Analyser extends Parser {
return node;
}
- private boolean expandCaseFoldStringAlt(final int itemNum, final char[] items,
+ private static boolean expandCaseFoldStringAlt(final int itemNum, final char[] items,
final char[] chars, final int p, final int slen, final int end, final ObjPtr<Node> node) {
ConsAltNode altNode;
@@ -833,59 +897,68 @@ final class Analyser extends Parser {
private Node expandCaseFoldString(final Node node) {
final StringNode sn = (StringNode)node;
- if (sn.isAmbig() || sn.length() <= 0) return node;
+ if (sn.isAmbig() || sn.length() <= 0) {
+ return node;
+ }
- final char[] chars = sn.chars;
- int p = sn.p;
+ final char[] chars1 = sn.chars;
+ int pt = sn.p;
final int end = sn.end;
int altNum = 1;
- ConsAltNode topRoot = null, root = null;
+ ConsAltNode topRoot = null, r = null;
+ @SuppressWarnings("unused")
final ObjPtr<Node> prevNode = new ObjPtr<Node>();
StringNode stringNode = null;
- while (p < end) {
- final char[] items = EncodingHelper.caseFoldCodesByString(regex.caseFoldFlag, chars[p]);
+ while (pt < end) {
+ final char[] items = EncodingHelper.caseFoldCodesByString(regex.caseFoldFlag, chars1[pt]);
if (items.length == 0) {
if (stringNode == null) {
- if (root == null && prevNode.p != null) {
- topRoot = root = ConsAltNode.listAdd(null, prevNode.p);
+ if (r == null && prevNode.p != null) {
+ topRoot = r = ConsAltNode.listAdd(null, prevNode.p);
}
prevNode.p = stringNode = new StringNode(); // onig_node_new_str(NULL, NULL);
- if (root != null) ConsAltNode.listAdd(root, stringNode);
+ if (r != null) {
+ ConsAltNode.listAdd(r, stringNode);
+ }
}
- stringNode.cat(chars, p, p + 1);
+ stringNode.cat(chars1, pt, pt + 1);
} else {
altNum *= (items.length + 1);
- if (altNum > THRESHOLD_CASE_FOLD_ALT_FOR_EXPANSION) break;
+ if (altNum > THRESHOLD_CASE_FOLD_ALT_FOR_EXPANSION) {
+ break;
+ }
- if (root == null && prevNode.p != null) {
- topRoot = root = ConsAltNode.listAdd(null, prevNode.p);
+ if (r == null && prevNode.p != null) {
+ topRoot = r = ConsAltNode.listAdd(null, prevNode.p);
}
- expandCaseFoldStringAlt(items.length, items, chars, p, 1, end, prevNode);
- if (root != null) ConsAltNode.listAdd(root, prevNode.p);
+ expandCaseFoldStringAlt(items.length, items, chars1, pt, 1, end, prevNode);
+ if (r != null) {
+ ConsAltNode.listAdd(r, prevNode.p);
+ }
stringNode = null;
}
- p++;
+ pt++;
}
- if (p < end) {
- final Node srem = expandCaseFoldMakeRemString(chars, p, end);
+ if (pt < end) {
+ final Node srem = expandCaseFoldMakeRemString(chars1, pt, end);
- if (prevNode.p != null && root == null) {
- topRoot = root = ConsAltNode.listAdd(null, prevNode.p);
+ if (prevNode.p != null && r == null) {
+ topRoot = r = ConsAltNode.listAdd(null, prevNode.p);
}
- if (root == null) {
+ if (r == null) {
prevNode.p = srem;
} else {
- ConsAltNode.listAdd(root, srem);
+ ConsAltNode.listAdd(r, srem);
}
}
/* ending */
@@ -909,7 +982,10 @@ final class Analyser extends Parser {
5. find invalid patterns in look-behind.
6. expand repeated string.
*/
- protected final Node setupTree(Node node, int state) {
+ protected final Node setupTree(final Node nodep, final int statep) {
+ Node node = nodep;
+ int state = statep;
+
restart: while (true) {
switch (node.getType()) {
case NodeType.LIST:
@@ -958,7 +1034,9 @@ final class Analyser extends Parser {
final QuantifierNode qn = (QuantifierNode)node;
Node target = qn.target;
- if ((state & IN_REPEAT) != 0) qn.setInRepeat();
+ if ((state & IN_REPEAT) != 0) {
+ qn.setInRepeat();
+ }
if (isRepeatInfinite(qn.upper) || qn.lower >= 1) {
final int d = getMinMatchLength(target);
@@ -966,14 +1044,18 @@ final class Analyser extends Parser {
qn.targetEmptyInfo = TargetInfo.IS_EMPTY;
if (Config.USE_MONOMANIAC_CHECK_CAPTURES_IN_ENDLESS_REPEAT) {
final int info = quantifiersMemoryInfo(target);
- if (info > 0) qn.targetEmptyInfo = info;
+ if (info > 0) {
+ qn.targetEmptyInfo = info;
+ }
} // USE_INFINITE_REPEAT_MONOMANIAC_MEM_STATUS_CHECK
// strange stuff here (turned off)
}
}
state |= IN_REPEAT;
- if (qn.lower != qn.upper) state |= IN_VAR_REPEAT;
+ if (qn.lower != qn.upper) {
+ state |= IN_VAR_REPEAT;
+ }
target = setupTree(target, state);
@@ -1035,11 +1117,16 @@ final class Analyser extends Parser {
final QuantifierNode tqn = (QuantifierNode)en.target;
if (isRepeatInfinite(tqn.upper) && tqn.lower <= 1 && tqn.greedy) {
/* (?>a*), a*+ etc... */
- if (tqn.target.isSimple()) en.setStopBtSimpleRepeat();
+ if (tqn.target.isSimple()) {
+ en.setStopBtSimpleRepeat();
+ }
}
}
break;
+ default:
+ break;
+
} // inner switch
break;
@@ -1059,7 +1146,9 @@ final class Analyser extends Parser {
throw new SyntaxException(ERR_INVALID_LOOK_BEHIND_PATTERN);
}
node = setupLookBehind(node);
- if (node.getType() != NodeType.ANCHOR) continue restart;
+ if (node.getType() != NodeType.ANCHOR) {
+ continue restart;
+ }
setupTree(((AnchorNode)node).target, state);
break;
@@ -1068,12 +1157,19 @@ final class Analyser extends Parser {
throw new SyntaxException(ERR_INVALID_LOOK_BEHIND_PATTERN);
}
node = setupLookBehind(node);
- if (node.getType() != NodeType.ANCHOR) continue restart;
+ if (node.getType() != NodeType.ANCHOR) {
+ continue restart;
+ }
setupTree(((AnchorNode)node).target, (state | IN_NOT));
break;
+ default:
+ break;
+
} // inner switch
break;
+ default:
+ break;
} // switch
return node;
} // restart: while
@@ -1191,7 +1287,9 @@ final class Analyser extends Parser {
opt.expr.copy(nopt.exm);
}
opt.expr.reachEnd = false;
- if (nopt.map.value > 0) opt.map.copy(nopt.map);
+ if (nopt.map.value > 0) {
+ opt.map.copy(nopt.map);
+ }
break;
case AnchorType.PREC_READ_NOT:
@@ -1199,6 +1297,9 @@ final class Analyser extends Parser {
case AnchorType.LOOK_BEHIND_NOT:
break;
+ default:
+ break;
+
} // inner switch
break;
}
@@ -1282,8 +1383,12 @@ final class Analyser extends Parser {
if (++en.optCount > MAX_NODE_OPT_INFO_REF_COUNT) {
int min = 0;
int max = MinMaxLen.INFINITE_DISTANCE;
- if (en.isMinFixed()) min = en.minLength;
- if (en.isMaxFixed()) max = en.maxLength;
+ if (en.isMinFixed()) {
+ min = en.minLength;
+ }
+ if (en.isMaxFixed()) {
+ max = en.maxLength;
+ }
opt.length.set(min, max);
} else { // USE_SUBEXP_CALL
optimizeNodeLeft(en.target, opt, oenv);
@@ -1298,6 +1403,9 @@ final class Analyser extends Parser {
case EncloseType.STOP_BACKTRACK:
optimizeNodeLeft(en.target, opt, oenv);
break;
+
+ default:
+ break;
} // inner switch
break;
}
@@ -1307,6 +1415,7 @@ final class Analyser extends Parser {
} // switch
}
+ @SuppressWarnings("unused")
protected final void setOptimizedInfoFromTree(final Node node) {
final NodeOptInfo opt = new NodeOptInfo();
final OptEnvironment oenv = new OptEnvironment();
@@ -1347,7 +1456,9 @@ final class Analyser extends Parser {
regex.setSubAnchor(opt.map.anchor);
} else {
regex.subAnchor |= opt.anchor.leftAnchor & AnchorType.BEGIN_LINE;
- if (opt.length.max == 0) regex.subAnchor |= opt.anchor.rightAnchor & AnchorType.END_LINE;
+ if (opt.length.max == 0) {
+ regex.subAnchor |= opt.anchor.rightAnchor & AnchorType.END_LINE;
+ }
}
if (Config.DEBUG_COMPILE || Config.DEBUG_MATCH) {
diff --git a/src/jdk/nashorn/internal/runtime/regexp/joni/ApplyCaseFold.java b/src/jdk/nashorn/internal/runtime/regexp/joni/ApplyCaseFold.java
index 0a6d4b05..5ecfec4f 100644
--- a/src/jdk/nashorn/internal/runtime/regexp/joni/ApplyCaseFold.java
+++ b/src/jdk/nashorn/internal/runtime/regexp/joni/ApplyCaseFold.java
@@ -24,7 +24,7 @@ import jdk.nashorn.internal.runtime.regexp.joni.ast.CClassNode;
final class ApplyCaseFold {
// i_apply_case_fold
- public void apply(final int from, final int to, final Object o) {
+ public static void apply(final int from, final int to, final Object o) {
final ApplyCaseFoldArg arg = (ApplyCaseFoldArg)o;
final ScanEnvironment env = arg.env;
@@ -45,7 +45,9 @@ final class ApplyCaseFold {
} else {
if (inCC) {
if (to >= BitSet.SINGLE_BYTE_SIZE) {
- if (cc.isNot()) cc.clearNotFlag();
+ if (cc.isNot()) {
+ cc.clearNotFlag();
+ }
cc.addCodeRange(env, to, to);
} else {
if (cc.isNot()) {
diff --git a/src/jdk/nashorn/internal/runtime/regexp/joni/ApplyCaseFoldArg.java b/src/jdk/nashorn/internal/runtime/regexp/joni/ApplyCaseFoldArg.java
index c199bea7..ce25af62 100644
--- a/src/jdk/nashorn/internal/runtime/regexp/joni/ApplyCaseFoldArg.java
+++ b/src/jdk/nashorn/internal/runtime/regexp/joni/ApplyCaseFoldArg.java
@@ -22,6 +22,7 @@ package jdk.nashorn.internal.runtime.regexp.joni;
import jdk.nashorn.internal.runtime.regexp.joni.ast.CClassNode;
import jdk.nashorn.internal.runtime.regexp.joni.ast.ConsAltNode;
+@SuppressWarnings("javadoc")
public final class ApplyCaseFoldArg {
final ScanEnvironment env;
final CClassNode cc;
diff --git a/src/jdk/nashorn/internal/runtime/regexp/joni/ArrayCompiler.java b/src/jdk/nashorn/internal/runtime/regexp/joni/ArrayCompiler.java
index 294113e3..0e789333 100644
--- a/src/jdk/nashorn/internal/runtime/regexp/joni/ArrayCompiler.java
+++ b/src/jdk/nashorn/internal/runtime/regexp/joni/ArrayCompiler.java
@@ -24,7 +24,6 @@ import static jdk.nashorn.internal.runtime.regexp.joni.Option.isDynamic;
import static jdk.nashorn.internal.runtime.regexp.joni.Option.isIgnoreCase;
import static jdk.nashorn.internal.runtime.regexp.joni.Option.isMultiline;
import static jdk.nashorn.internal.runtime.regexp.joni.ast.QuantifierNode.isRepeatInfinite;
-
import jdk.nashorn.internal.runtime.regexp.joni.ast.AnchorNode;
import jdk.nashorn.internal.runtime.regexp.joni.ast.BackRefNode;
import jdk.nashorn.internal.runtime.regexp.joni.ast.CClassNode;
@@ -98,15 +97,15 @@ final class ArrayCompiler extends Compiler {
} while ((aln = aln.cdr) != null);
}
- private boolean isNeedStrLenOpExact(final int op) {
+ private static boolean isNeedStrLenOpExact(final int op) {
return op == OPCode.EXACTN || op == OPCode.EXACTN_IC;
}
- private boolean opTemplated(final int op) {
+ private static boolean opTemplated(final int op) {
return isNeedStrLenOpExact(op);
}
- private int selectStrOpcode(final int strLength, final boolean ignoreCase) {
+ private static int selectStrOpcode(final int strLength, final boolean ignoreCase) {
int op;
if (ignoreCase) {
@@ -139,7 +138,7 @@ final class ArrayCompiler extends Compiler {
compileTree(node);
if (emptyInfo != 0) {
- switch(emptyInfo) {
+ switch (emptyInfo) {
case TargetInfo.IS_EMPTY:
addOpcode(OPCode.NULL_CHECK_END);
break;
@@ -149,13 +148,15 @@ final class ArrayCompiler extends Compiler {
case TargetInfo.IS_EMPTY_REC:
addOpcode(OPCode.NULL_CHECK_END_MEMST_PUSH);
break;
+ default:
+ break;
} // switch
addMemNum(savedNumNullCheck); /* NULL CHECK ID */
}
}
- private int addCompileStringlength(final char[] chars, final int p, final int strLength, final boolean ignoreCase) {
+ private static int addCompileStringlength(final char[] chars, final int p, final int strLength, final boolean ignoreCase) {
final int op = selectStrOpcode(strLength, ignoreCase);
int len = OPSize.OPCODE;
@@ -163,7 +164,9 @@ final class ArrayCompiler extends Compiler {
// string length, template index, template string pointer
len += OPSize.LENGTH + OPSize.INDEX + OPSize.INDEX;
} else {
- if (isNeedStrLenOpExact(op)) len += OPSize.LENGTH;
+ if (isNeedStrLenOpExact(op)) {
+ len += OPSize.LENGTH;
+ }
len += strLength;
}
return len;
@@ -187,9 +190,11 @@ final class ArrayCompiler extends Compiler {
}
}
- private int compileLengthStringNode(final Node node) {
+ private static int compileLengthStringNode(final Node node) {
final StringNode sn = (StringNode)node;
- if (sn.length() <= 0) return 0;
+ if (sn.length() <= 0) {
+ return 0;
+ }
final boolean ambig = sn.isAmbig();
int p, prev;
@@ -210,8 +215,10 @@ final class ArrayCompiler extends Compiler {
return rlen;
}
- private int compileLengthStringRawNode(final StringNode sn) {
- if (sn.length() <= 0) return 0;
+ private static int compileLengthStringRawNode(final StringNode sn) {
+ if (sn.length() <= 0) {
+ return 0;
+ }
return addCompileStringlength(sn.chars, sn.p, sn.length(), false);
}
@@ -220,8 +227,10 @@ final class ArrayCompiler extends Compiler {
addInts(mbuf.p, mbuf.used);
}
- private int compileLengthCClassNode(final CClassNode cc) {
- if (cc.isShare()) return OPSize.OPCODE + OPSize.POINTER;
+ private static int compileLengthCClassNode(final CClassNode cc) {
+ if (cc.isShare()) {
+ return OPSize.OPCODE + OPSize.POINTER;
+ }
int len;
if (cc.mbuf == null) {
@@ -360,9 +369,8 @@ final class ArrayCompiler extends Compiler {
if (qn.greedy && infinite) {
if (qn.nextHeadExact != null) {
return OPSize.ANYCHAR_STAR_PEEK_NEXT + tlen * qn.lower;
- } else {
- return OPSize.ANYCHAR_STAR + tlen * qn.lower;
}
+ return OPSize.ANYCHAR_STAR + tlen * qn.lower;
}
}
@@ -425,14 +433,13 @@ final class ArrayCompiler extends Compiler {
final StringNode sn = (StringNode)qn.nextHeadExact;
addChars(sn.chars, sn.p, 1);
return;
+ }
+ if (isMultiline(regex.options)) {
+ addOpcode(OPCode.ANYCHAR_ML_STAR);
} else {
- if (isMultiline(regex.options)) {
- addOpcode(OPCode.ANYCHAR_ML_STAR);
- } else {
- addOpcode(OPCode.ANYCHAR_STAR);
- }
- return;
+ addOpcode(OPCode.ANYCHAR_STAR);
}
+ return;
}
int modTLen;
@@ -510,9 +517,8 @@ final class ArrayCompiler extends Compiler {
if (isDynamic(prev ^ node.option)) {
return OPSize.SET_OPTION_PUSH + OPSize.SET_OPTION + OPSize.FAIL + tlen + OPSize.SET_OPTION;
- } else {
- return tlen;
}
+ return tlen;
}
@Override
@@ -675,13 +681,15 @@ final class ArrayCompiler extends Compiler {
break;
case AnchorType.WORD_BEGIN:
- if (Config.USE_WORD_BEGIN_END)
+ if (Config.USE_WORD_BEGIN_END) {
addOpcode(OPCode.WORD_BEGIN);
+ }
break;
case AnchorType.WORD_END:
- if (Config.USE_WORD_BEGIN_END)
+ if (Config.USE_WORD_BEGIN_END) {
addOpcode(OPCode.WORD_END);
+ }
break;
case AnchorType.PREC_READ:
@@ -701,7 +709,9 @@ final class ArrayCompiler extends Compiler {
addOpcode(OPCode.LOOK_BEHIND);
if (node.charLength < 0) {
n = analyser.getCharLengthTree(node.target);
- if (analyser.returnCode != 0) newSyntaxException(ERR_INVALID_LOOK_BEHIND_PATTERN);
+ if (analyser.returnCode != 0) {
+ newSyntaxException(ERR_INVALID_LOOK_BEHIND_PATTERN);
+ }
} else {
n = node.charLength;
}
@@ -714,7 +724,9 @@ final class ArrayCompiler extends Compiler {
addOpcodeRelAddr(OPCode.PUSH_LOOK_BEHIND_NOT, len + OPSize.FAIL_LOOK_BEHIND_NOT);
if (node.charLength < 0) {
n = analyser.getCharLengthTree(node.target);
- if (analyser.returnCode != 0) newSyntaxException(ERR_INVALID_LOOK_BEHIND_PATTERN);
+ if (analyser.returnCode != 0) {
+ newSyntaxException(ERR_INVALID_LOOK_BEHIND_PATTERN);
+ }
} else {
n = node.charLength;
}
@@ -796,7 +808,9 @@ final class ArrayCompiler extends Compiler {
private void ensure(final int size) {
if (size >= code.length) {
int length = code.length << 1;
- while (length <= size) length <<= 1;
+ while (length <= size) {
+ length <<= 1;
+ }
final int[]tmp = new int[length];
System.arraycopy(code, 0, tmp, 0, code.length);
code = tmp;
@@ -829,11 +843,14 @@ final class ArrayCompiler extends Compiler {
regex.operands[regex.operandLength++] = o;
}
- private void addChars(final char[] chars, int p ,final int length) {
+ private void addChars(final char[] chars, final int pp ,final int length) {
ensure(codeLength + length);
+ int p = pp;
final int end = p + length;
- while (p < end) code[codeLength++] = chars[p++];
+ while (p < end) {
+ code[codeLength++] = chars[p++];
+ }
}
private void addInts(final int[]ints, final int length) {
@@ -876,6 +893,9 @@ final class ArrayCompiler extends Compiler {
case OPCode.CALL:
case OPCode.RETURN: // it will appear only with CALL though
regex.stackNeeded = true;
+ break;
+ default:
+ break;
}
}
diff --git a/src/jdk/nashorn/internal/runtime/regexp/joni/BitSet.java b/src/jdk/nashorn/internal/runtime/regexp/joni/BitSet.java
index 2aac96a8..2747c24c 100644
--- a/src/jdk/nashorn/internal/runtime/regexp/joni/BitSet.java
+++ b/src/jdk/nashorn/internal/runtime/regexp/joni/BitSet.java
@@ -19,6 +19,7 @@
*/
package jdk.nashorn.internal.runtime.regexp.joni;
+@SuppressWarnings("javadoc")
public final class BitSet {
static final int BITS_PER_BYTE = 8;
public static final int SINGLE_BYTE_SIZE = (1 << BITS_PER_BYTE);
@@ -34,7 +35,9 @@ public final class BitSet {
final StringBuilder buffer = new StringBuilder();
buffer.append("BitSet");
for (int i=0; i<SINGLE_BYTE_SIZE; i++) {
- if ((i % (SINGLE_BYTE_SIZE / BITS_TO_STRING_WRAP)) == 0) buffer.append("\n ");
+ if ((i % (SINGLE_BYTE_SIZE / BITS_TO_STRING_WRAP)) == 0) {
+ buffer.append("\n ");
+ }
buffer.append(at(i) ? "1" : "0");
}
return buffer.toString();
@@ -53,44 +56,62 @@ public final class BitSet {
}
public void clear() {
- for (int i=0; i<BITSET_SIZE; i++) bits[i]=0;
+ for (int i=0; i<BITSET_SIZE; i++) {
+ bits[i]=0;
+ }
}
public boolean isEmpty() {
for (int i=0; i<BITSET_SIZE; i++) {
- if (bits[i] != 0) return false;
+ if (bits[i] != 0) {
+ return false;
+ }
}
return true;
}
public void setRange(final int from, final int to) {
- for (int i=from; i<=to && i < SINGLE_BYTE_SIZE; i++) set(i);
+ for (int i=from; i<=to && i < SINGLE_BYTE_SIZE; i++) {
+ set(i);
+ }
}
public void invert() {
- for (int i=0; i<BITSET_SIZE; i++) bits[i] = ~bits[i];
+ for (int i=0; i<BITSET_SIZE; i++) {
+ bits[i] = ~bits[i];
+ }
}
public void invertTo(final BitSet to) {
- for (int i=0; i<BITSET_SIZE; i++) to.bits[i] = ~bits[i];
+ for (int i=0; i<BITSET_SIZE; i++) {
+ to.bits[i] = ~bits[i];
+ }
}
public void and(final BitSet other) {
- for (int i=0; i<BITSET_SIZE; i++) bits[i] &= other.bits[i];
+ for (int i=0; i<BITSET_SIZE; i++) {
+ bits[i] &= other.bits[i];
+ }
}
public void or(final BitSet other) {
- for (int i=0; i<BITSET_SIZE; i++) bits[i] |= other.bits[i];
+ for (int i=0; i<BITSET_SIZE; i++) {
+ bits[i] |= other.bits[i];
+ }
}
public void copy(final BitSet other) {
- for (int i=0; i<BITSET_SIZE; i++) bits[i] = other.bits[i];
+ for (int i=0; i<BITSET_SIZE; i++) {
+ bits[i] = other.bits[i];
+ }
}
public int numOn() {
int num = 0;
for (int i=0; i<SINGLE_BYTE_SIZE; i++) {
- if (at(i)) num++;
+ if (at(i)) {
+ num++;
+ }
}
return num;
}
@@ -99,9 +120,12 @@ public final class BitSet {
return 1 << (pos % SINGLE_BYTE_SIZE);
}
- private static int log2(int n){
+ private static int log2(final int np) {
int log = 0;
- while ((n >>>= 1) != 0) log++;
+ int n = np;
+ while ((n >>>= 1) != 0) {
+ log++;
+ }
return log;
}
diff --git a/src/jdk/nashorn/internal/runtime/regexp/joni/BitStatus.java b/src/jdk/nashorn/internal/runtime/regexp/joni/BitStatus.java
index 25806a51..91cf7198 100644
--- a/src/jdk/nashorn/internal/runtime/regexp/joni/BitStatus.java
+++ b/src/jdk/nashorn/internal/runtime/regexp/joni/BitStatus.java
@@ -34,7 +34,8 @@ final class BitStatus {
return (n < BIT_STATUS_BITS_NUM ? stats & (1 << n) : (stats & 1)) != 0;
}
- public static int bsOnAt(int stats, final int n) {
+ public static int bsOnAt(final int statsp, final int n) {
+ int stats = statsp;
if (n < BIT_STATUS_BITS_NUM) {
stats |= (1 << n);
} else {
@@ -43,12 +44,7 @@ final class BitStatus {
return stats;
}
- public static int bsOnOff(int v, final int f, final boolean negative) {
- if (negative) {
- v &= ~f;
- } else {
- v |= f;
- }
- return v;
+ public static int bsOnOff(final int v, final int f, final boolean negative) {
+ return negative ? (v & ~f) : (v | f);
}
}
diff --git a/src/jdk/nashorn/internal/runtime/regexp/joni/ByteCodeMachine.java b/src/jdk/nashorn/internal/runtime/regexp/joni/ByteCodeMachine.java
index ab28f93c..30cfe907 100644
--- a/src/jdk/nashorn/internal/runtime/regexp/joni/ByteCodeMachine.java
+++ b/src/jdk/nashorn/internal/runtime/regexp/joni/ByteCodeMachine.java
@@ -27,10 +27,8 @@ import static jdk.nashorn.internal.runtime.regexp.joni.Option.isFindNotEmpty;
import static jdk.nashorn.internal.runtime.regexp.joni.Option.isNotBol;
import static jdk.nashorn.internal.runtime.regexp.joni.Option.isNotEol;
import static jdk.nashorn.internal.runtime.regexp.joni.Option.isPosixRegion;
-
import jdk.nashorn.internal.runtime.regexp.joni.ast.CClassNode;
import jdk.nashorn.internal.runtime.regexp.joni.constants.OPCode;
-import jdk.nashorn.internal.runtime.regexp.joni.constants.OPSize;
import jdk.nashorn.internal.runtime.regexp.joni.encoding.IntHolder;
import jdk.nashorn.internal.runtime.regexp.joni.exception.ErrorMessages;
import jdk.nashorn.internal.runtime.regexp.joni.exception.InternalException;
@@ -52,8 +50,8 @@ class ByteCodeMachine extends StackMachine {
this.code = regex.code;
}
- private boolean stringCmpIC(final int caseFlodFlag, int s1, final IntHolder ps2, final int mbLen, final int textEnd) {
-
+ private boolean stringCmpIC(final int caseFlodFlag, final int s1p, final IntHolder ps2, final int mbLen, final int textEnd) {
+ int s1 = s1p;
int s2 = ps2.value;
final int end1 = s1 + mbLen;
@@ -83,12 +81,16 @@ class ByteCodeMachine extends StackMachine {
Config.log.printf("%4d", (s - str)).print("> \"");
int q, i;
for (i=0, q=s; i<7 && q<end && s>=0; i++) {
- if (q < end) Config.log.print(new String(new char[]{chars[q++]}));
+ if (q < end) {
+ Config.log.print(new String(new char[]{chars[q++]}));
+ }
+ }
+ final String string = q < end ? "...\"" : "\"";
+ q += string.length();
+ Config.log.print(string);
+ for (i=0; i<20-(q-s);i++) {
+ Config.log.print(" ");
}
- final String str = q < end ? "...\"" : "\"";
- q += str.length();
- Config.log.print(str);
- for (i=0; i<20-(q-s);i++) Config.log.print(" ");
final StringBuilder sb = new StringBuilder();
new ByteCodePrinter(regex).compiledByteCodeToString(sb, ip);
Config.log.println(sb.toString());
@@ -96,28 +98,34 @@ class ByteCodeMachine extends StackMachine {
}
@Override
- protected final int matchAt(final int range, final int sstart, final int sprev) {
- this.range = range;
- this.sstart = sstart;
- this.sprev = sprev;
+ protected final int matchAt(final int r, final int ss, final int sp) {
+ this.range = r;
+ this.sstart = ss;
+ this.sprev = sp;
stk = 0;
ip = 0;
- if (Config.DEBUG_MATCH) debugMatchBegin();
+ if (Config.DEBUG_MATCH) {
+ debugMatchBegin();
+ }
init();
bestLen = -1;
- s = sstart;
+ s = ss;
- final int[]code = this.code;
+ final int[] c = this.code;
while (true) {
- if (Config.DEBUG_MATCH) debugMatchLoop();
+ if (Config.DEBUG_MATCH) {
+ debugMatchLoop();
+ }
sbegin = s;
- switch (code[ip++]) {
- case OPCode.END: if (opEnd()) return finish(); break;
+ switch (c[ip++]) {
+ case OPCode.END: if (opEnd()) {
+ return finish();
+ } break;
case OPCode.EXACT1: opExact1(); break;
case OPCode.EXACT2: opExact2(); continue;
case OPCode.EXACT3: opExact3(); continue;
@@ -358,10 +366,14 @@ class ByteCodeMachine extends StackMachine {
final char[] bs = regex.templates[code[ip++]];
int ps = code[ip++];
- while (tlen-- > 0) if (bs[ps++] != chars[s++]) {opFail(); return;}
+ while (tlen-- > 0) {
+ if (bs[ps++] != chars[s++]) {opFail(); return;}
+ }
} else {
- while (tlen-- > 0) if (code[ip++] != chars[s++]) {opFail(); return;}
+ while (tlen-- > 0) {
+ if (code[ip++] != chars[s++]) {opFail(); return;}
+ }
}
sprev = s - 1;
}
@@ -380,10 +392,14 @@ class ByteCodeMachine extends StackMachine {
final char[] bs = regex.templates[code[ip++]];
int ps = code[ip++];
- while (tlen-- > 0) if (bs[ps++] != EncodingHelper.toLowerCase(chars[s++])) {opFail(); return;}
+ while (tlen-- > 0) {
+ if (bs[ps++] != EncodingHelper.toLowerCase(chars[s++])) {opFail(); return;}
+ }
} else {
- while (tlen-- > 0) if (code[ip++] != EncodingHelper.toLowerCase(chars[s++])) {opFail(); return;}
+ while (tlen-- > 0) {
+ if (code[ip++] != EncodingHelper.toLowerCase(chars[s++])) {opFail(); return;}
+ }
}
sprev = s - 1;
}
@@ -402,11 +418,15 @@ class ByteCodeMachine extends StackMachine {
private boolean isInClassMB() {
final int tlen = code[ip++];
- if (s >= range) return false;
+ if (s >= range) {
+ return false;
+ }
final int ss = s;
s++;
final int c = chars[ss];
- if (!EncodingHelper.isInCodeRange(code, ip, c)) return false;
+ if (!EncodingHelper.isInCodeRange(code, ip, c)) {
+ return false;
+ }
ip += tlen;
return true;
}
@@ -444,7 +464,9 @@ class ByteCodeMachine extends StackMachine {
final int tlen = code[ip++];
if (!(s + 1 <= range)) {
- if (s >= range) return false;
+ if (s >= range) {
+ return false;
+ }
s = end;
ip += tlen;
return true;
@@ -454,7 +476,9 @@ class ByteCodeMachine extends StackMachine {
s++;
final int c = chars[ss];
- if (EncodingHelper.isInCodeRange(code, ip, c)) return false;
+ if (EncodingHelper.isInCodeRange(code, ip, c)) {
+ return false;
+ }
ip += tlen;
return true;
}
@@ -511,10 +535,10 @@ class ByteCodeMachine extends StackMachine {
}
private void opAnyCharStar() {
- final char[] chars = this.chars;
+ final char[] ch = this.chars;
while (s < range) {
pushAlt(ip, s, sprev);
- if (isNewLine(chars, s, end)) {opFail(); return;}
+ if (isNewLine(ch, s, end)) {opFail(); return;}
sprev = s;
s++;
}
@@ -532,11 +556,13 @@ class ByteCodeMachine extends StackMachine {
private void opAnyCharStarPeekNext() {
final char c = (char)code[ip];
- final char[] chars = this.chars;
+ final char[] ch = this.chars;
while (s < range) {
- final char b = chars[s];
- if (c == b) pushAlt(ip + 1, s, sprev);
+ final char b = ch[s];
+ if (c == b) {
+ pushAlt(ip + 1, s, sprev);
+ }
if (isNewLine(b)) {opFail(); return;}
sprev = s;
s++;
@@ -547,10 +573,12 @@ class ByteCodeMachine extends StackMachine {
private void opAnyCharMLStarPeekNext() {
final char c = (char)code[ip];
- final char[] chars = this.chars;
+ final char[] ch = this.chars;
while (s < range) {
- if (c == chars[s]) pushAlt(ip + 1, s, sprev);
+ if (c == ch[s]) {
+ pushAlt(ip + 1, s, sprev);
+ }
sprev = s;
s++;
}
@@ -592,29 +620,39 @@ class ByteCodeMachine extends StackMachine {
private void opWordBegin() {
if (s < range && EncodingHelper.isWord(chars[s])) {
- if (s == str || !EncodingHelper.isWord(chars[sprev])) return;
+ if (s == str || !EncodingHelper.isWord(chars[sprev])) {
+ return;
+ }
}
opFail();
}
private void opWordEnd() {
if (s != str && EncodingHelper.isWord(chars[sprev])) {
- if (s == end || !EncodingHelper.isWord(chars[s])) return;
+ if (s == end || !EncodingHelper.isWord(chars[s])) {
+ return;
+ }
}
opFail();
}
private void opBeginBuf() {
- if (s != str) opFail();
+ if (s != str) {
+ opFail();
+ }
}
private void opEndBuf() {
- if (s != end) opFail();
+ if (s != end) {
+ opFail();
+ }
}
private void opBeginLine() {
if (s == str) {
- if (isNotBol(msaOptions)) opFail();
+ if (isNotBol(msaOptions)) {
+ opFail();
+ }
return;
} else if (isNewLine(chars, sprev, end) && s != end) {
return;
@@ -626,13 +664,16 @@ class ByteCodeMachine extends StackMachine {
if (s == end) {
if (Config.USE_NEWLINE_AT_END_OF_STRING_HAS_EMPTY_LINE) {
if (str == end || !isNewLine(chars, sprev, end)) {
- if (isNotEol(msaOptions)) opFail();
+ if (isNotEol(msaOptions)) {
+ opFail();
+ }
}
return;
- } else {
- if (isNotEol(msaOptions)) opFail();
- return;
}
+ if (isNotEol(msaOptions)) {
+ opFail();
+ }
+ return;
} else if (isNewLine(chars, s, end)) {
return;
}
@@ -643,13 +684,16 @@ class ByteCodeMachine extends StackMachine {
if (s == end) {
if (Config.USE_NEWLINE_AT_END_OF_STRING_HAS_EMPTY_LINE) {
if (str == end || !isNewLine(chars, sprev, end)) {
- if (isNotEol(msaOptions)) opFail();
+ if (isNotEol(msaOptions)) {
+ opFail();
+ }
}
return;
- } else {
- if (isNotEol(msaOptions)) opFail();
- return;
}
+ if (isNotEol(msaOptions)) {
+ opFail();
+ }
+ return;
} else if (isNewLine(chars, s, end) && s + 1 == end) {
return;
}
@@ -657,7 +701,9 @@ class ByteCodeMachine extends StackMachine {
}
private void opBeginPosition() {
- if (s != msaStart) opFail();
+ if (s != msaStart) {
+ opFail();
+ }
}
private void opMemoryStartPush() {
@@ -726,11 +772,15 @@ class ByteCodeMachine extends StackMachine {
sprev = s;
// STRING_CMP
- while(n-- > 0) if (chars[pstart++] != chars[s++]) {opFail(); return;}
+ while(n-- > 0) {
+ if (chars[pstart++] != chars[s++]) {opFail(); return;}
+ }
// beyond string check
if (sprev < range) {
- while (sprev + 1 < s) sprev++;
+ while (sprev + 1 < s) {
+ sprev++;
+ }
}
}
@@ -764,7 +814,9 @@ class ByteCodeMachine extends StackMachine {
s = value;
// if (sprev < chars.length)
- while (sprev + 1 < s) sprev++;
+ while (sprev + 1 < s) {
+ sprev++;
+ }
}
private void opBackRefMulti() {
@@ -773,7 +825,9 @@ class ByteCodeMachine extends StackMachine {
int i;
loop:for (i=0; i<tlen; i++) {
final int mem = code[ip++];
- if (backrefInvalid(mem)) continue;
+ if (backrefInvalid(mem)) {
+ continue;
+ }
int pstart = backrefStart(mem);
final int pend = backrefEnd(mem);
@@ -785,14 +839,18 @@ class ByteCodeMachine extends StackMachine {
int swork = s;
while (n-- > 0) {
- if (chars[pstart++] != chars[swork++]) continue loop;
+ if (chars[pstart++] != chars[swork++]) {
+ continue loop;
+ }
}
s = swork;
// beyond string check
if (sprev < range) {
- while (sprev + 1 < s) sprev++;
+ while (sprev + 1 < s) {
+ sprev++;
+ }
}
ip += tlen - i - 1; // * SIZE_MEMNUM (1)
@@ -807,7 +865,9 @@ class ByteCodeMachine extends StackMachine {
int i;
loop:for (i=0; i<tlen; i++) {
final int mem = code[ip++];
- if (backrefInvalid(mem)) continue;
+ if (backrefInvalid(mem)) {
+ continue;
+ }
final int pstart = backrefStart(mem);
final int pend = backrefEnd(mem);
@@ -818,11 +878,16 @@ class ByteCodeMachine extends StackMachine {
sprev = s;
value = s;
- if (!stringCmpIC(regex.caseFoldFlag, pstart, this, n, end)) continue loop; // STRING_CMP_VALUE_IC
+ if (!stringCmpIC(regex.caseFoldFlag, pstart, this, n, end))
+ {
+ continue loop; // STRING_CMP_VALUE_IC
+ }
s = value;
// if (sprev < chars.length)
- while (sprev + 1 < s) sprev++;
+ while (sprev + 1 < s) {
+ sprev++;
+ }
ip += tlen - i - 1; // * SIZE_MEMNUM (1)
break; /* success */
@@ -830,10 +895,12 @@ class ByteCodeMachine extends StackMachine {
if (i == tlen) {opFail(); return;}
}
- private boolean memIsInMemp(final int mem, final int num, int memp) {
- for (int i=0; i<num; i++) {
+ private boolean memIsInMemp(final int mem, final int num, final int mempp) {
+ for (int i=0, memp = mempp; i<num; i++) {
final int m = code[memp++];
- if (mem == m) return true;
+ if (mem == m) {
+ return true;
+ }
}
return false;
}
@@ -857,7 +924,9 @@ class ByteCodeMachine extends StackMachine {
if (memIsInMemp(e.getMemNum(), memNum, memp)) {
final int pstart = e.getMemPStr();
if