aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bin/checkintest.sh266
-rw-r--r--bin/dump_octane_code.sh53
-rw-r--r--bin/jjs.bat27
-rw-r--r--bin/jjsdebug.sh (renamed from bin/jjs)20
-rw-r--r--bin/jjssecure29
-rw-r--r--bin/jjssecure.bat27
-rw-r--r--bin/nashorn.bat27
-rw-r--r--bin/nashornsecure29
-rw-r--r--bin/nashornsecure.bat27
-rw-r--r--bin/run_octane.sh (renamed from bin/nashorn)42
-rw-r--r--bin/runopt.sh107
-rw-r--r--bin/verbose_octane.bat59
-rw-r--r--bin/verbose_octane.sh58
-rw-r--r--buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MemberInfo.java15
-rw-r--r--buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MethodGenerator.java6
-rw-r--r--docs/DEVELOPER_README89
-rw-r--r--make/build-benchmark.xml505
-rw-r--r--make/build-nasgen.xml6
-rw-r--r--make/build.xml126
-rw-r--r--make/nbproject/ide-targets.xml3
-rw-r--r--make/project.properties132
-rw-r--r--samples/javafoovars.js103
-rw-r--r--samples/jsobj_example.js73
-rw-r--r--samples/zipfs.js48
-rw-r--r--samples/ziplist.js80
-rw-r--r--src/jdk/internal/dynalink/ChainedCallSite.java64
-rw-r--r--src/jdk/internal/dynalink/DynamicLinker.java22
-rw-r--r--src/jdk/internal/dynalink/DynamicLinkerFactory.java26
-rw-r--r--src/jdk/internal/dynalink/GuardedInvocationFilter.java105
-rw-r--r--src/jdk/internal/dynalink/beans/AbstractJavaLinker.java138
-rw-r--r--src/jdk/internal/dynalink/beans/BeanLinker.java12
-rw-r--r--src/jdk/internal/dynalink/beans/BeansLinker.java20
-rw-r--r--src/jdk/internal/dynalink/beans/CallerSensitiveDynamicMethod.java5
-rw-r--r--src/jdk/internal/dynalink/beans/DynamicMethod.java9
-rw-r--r--src/jdk/internal/dynalink/beans/DynamicMethodLinker.java23
-rw-r--r--src/jdk/internal/dynalink/beans/OverloadedDynamicMethod.java14
-rw-r--r--src/jdk/internal/dynalink/beans/OverloadedMethod.java20
-rw-r--r--src/jdk/internal/dynalink/beans/SimpleDynamicMethod.java20
-rw-r--r--src/jdk/internal/dynalink/beans/SingleDynamicMethod.java6
-rw-r--r--src/jdk/internal/dynalink/beans/StaticClassLinker.java9
-rw-r--r--src/jdk/internal/dynalink/linker/GuardedInvocation.java162
-rw-r--r--src/jdk/internal/dynalink/linker/GuardedTypeConversion.java16
-rw-r--r--src/jdk/internal/dynalink/linker/GuardingDynamicLinker.java14
-rw-r--r--src/jdk/internal/dynalink/linker/LinkRequest.java22
-rw-r--r--src/jdk/internal/dynalink/linker/LinkerServices.java47
-rw-r--r--src/jdk/internal/dynalink/support/CallSiteDescriptorFactory.java16
-rw-r--r--src/jdk/internal/dynalink/support/CompositeTypeBasedGuardingDynamicLinker.java4
-rw-r--r--src/jdk/internal/dynalink/support/DefaultPrelinkFilter.java99
-rw-r--r--src/jdk/internal/dynalink/support/LinkRequestImpl.java20
-rw-r--r--src/jdk/internal/dynalink/support/LinkerServicesImpl.java5
-rw-r--r--src/jdk/internal/dynalink/support/RuntimeContextLinkRequestImpl.java12
-rw-r--r--src/jdk/internal/dynalink/support/TypeConverterFactory.java1
-rw-r--r--src/jdk/internal/dynalink/support/TypeUtilities.java159
-rw-r--r--src/jdk/internal/dynalink/support/messages.properties2
-rw-r--r--src/jdk/nashorn/api/scripting/NashornException.java17
-rw-r--r--src/jdk/nashorn/api/scripting/NashornScriptEngine.java117
-rw-r--r--src/jdk/nashorn/api/scripting/NashornScriptEngineFactory.java2
-rw-r--r--src/jdk/nashorn/api/scripting/ScriptObjectMirror.java23
-rw-r--r--src/jdk/nashorn/api/scripting/ScriptUtils.java11
-rw-r--r--src/jdk/nashorn/api/scripting/resources/engine.js101
-rw-r--r--src/jdk/nashorn/internal/IntDeque.java87
-rw-r--r--src/jdk/nashorn/internal/codegen/ApplySpecialization.java318
-rw-r--r--src/jdk/nashorn/internal/codegen/AssignSymbols.java958
-rw-r--r--src/jdk/nashorn/internal/codegen/Attr.java1947
-rw-r--r--src/jdk/nashorn/internal/codegen/BranchOptimizer.java93
-rw-r--r--src/jdk/nashorn/internal/codegen/ClassEmitter.java114
-rw-r--r--src/jdk/nashorn/internal/codegen/CodeGenerator.java4730
-rw-r--r--src/jdk/nashorn/internal/codegen/CodeGeneratorLexicalContext.java161
-rw-r--r--src/jdk/nashorn/internal/codegen/CompilationPhase.java816
-rw-r--r--src/jdk/nashorn/internal/codegen/CompileUnit.java21
-rw-r--r--src/jdk/nashorn/internal/codegen/Compiler.java865
-rw-r--r--src/jdk/nashorn/internal/codegen/CompilerConstants.java152
-rw-r--r--src/jdk/nashorn/internal/codegen/Condition.java6
-rw-r--r--src/jdk/nashorn/internal/codegen/ConstantData.java25
-rw-r--r--src/jdk/nashorn/internal/codegen/DumpBytecode.java120
-rw-r--r--src/jdk/nashorn/internal/codegen/FieldObjectCreator.java115
-rw-r--r--src/jdk/nashorn/internal/codegen/FinalizeTypes.java199
-rw-r--r--src/jdk/nashorn/internal/codegen/FindScopeDepths.java375
-rw-r--r--src/jdk/nashorn/internal/codegen/FoldConstants.java41
-rw-r--r--src/jdk/nashorn/internal/codegen/FunctionSignature.java10
-rw-r--r--src/jdk/nashorn/internal/codegen/Label.java512
-rw-r--r--src/jdk/nashorn/internal/codegen/LocalStateRestorationInfo.java59
-rw-r--r--src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java1489
-rw-r--r--src/jdk/nashorn/internal/codegen/Lower.java183
-rw-r--r--src/jdk/nashorn/internal/codegen/MapCreator.java80
-rw-r--r--src/jdk/nashorn/internal/codegen/MapTuple.java66
-rw-r--r--src/jdk/nashorn/internal/codegen/MethodEmitter.java859
-rw-r--r--src/jdk/nashorn/internal/codegen/Namespace.java1
-rw-r--r--src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java569
-rw-r--r--src/jdk/nashorn/internal/codegen/ObjectCreator.java54
-rw-r--r--src/jdk/nashorn/internal/codegen/OptimisticTypesCalculator.java284
-rw-r--r--src/jdk/nashorn/internal/codegen/OptimisticTypesPersistence.java314
-rw-r--r--src/jdk/nashorn/internal/codegen/ProgramPoints.java132
-rw-r--r--src/jdk/nashorn/internal/codegen/RangeAnalyzer.java475
-rw-r--r--src/jdk/nashorn/internal/codegen/RuntimeCallSite.java36
-rw-r--r--src/jdk/nashorn/internal/codegen/SharedScopeCall.java28
-rw-r--r--src/jdk/nashorn/internal/codegen/SpillObjectCreator.java177
-rw-r--r--src/jdk/nashorn/internal/codegen/SplitMethodEmitter.java24
-rw-r--r--src/jdk/nashorn/internal/codegen/Splitter.java30
-rw-r--r--src/jdk/nashorn/internal/codegen/TypeEvaluator.java208
-rw-r--r--src/jdk/nashorn/internal/codegen/TypeMap.java166
-rw-r--r--src/jdk/nashorn/internal/codegen/WeighNodes.java50
-rw-r--r--src/jdk/nashorn/internal/codegen/types/BooleanType.java52
-rw-r--r--src/jdk/nashorn/internal/codegen/types/BytecodeNumericOps.java25
-rw-r--r--src/jdk/nashorn/internal/codegen/types/BytecodeOps.java14
-rw-r--r--src/jdk/nashorn/internal/codegen/types/IntType.java137
-rw-r--r--src/jdk/nashorn/internal/codegen/types/LongType.java66
-rw-r--r--src/jdk/nashorn/internal/codegen/types/NumberType.java39
-rw-r--r--src/jdk/nashorn/internal/codegen/types/ObjectType.java48
-rw-r--r--src/jdk/nashorn/internal/codegen/types/Range.java704
-rw-r--r--src/jdk/nashorn/internal/codegen/types/Type.java283
-rw-r--r--src/jdk/nashorn/internal/ir/AccessNode.java54
-rw-r--r--src/jdk/nashorn/internal/ir/BaseNode.java53
-rw-r--r--src/jdk/nashorn/internal/ir/BinaryNode.java333
-rw-r--r--src/jdk/nashorn/internal/ir/Block.java107
-rw-r--r--src/jdk/nashorn/internal/ir/BlockStatement.java8
-rw-r--r--src/jdk/nashorn/internal/ir/BreakNode.java38
-rw-r--r--src/jdk/nashorn/internal/ir/BreakableNode.java10
-rw-r--r--src/jdk/nashorn/internal/ir/BreakableStatement.java24
-rw-r--r--src/jdk/nashorn/internal/ir/CallNode.java190
-rw-r--r--src/jdk/nashorn/internal/ir/CaseNode.java44
-rw-r--r--src/jdk/nashorn/internal/ir/CatchNode.java34
-rw-r--r--src/jdk/nashorn/internal/ir/ContinueNode.java35
-rw-r--r--src/jdk/nashorn/internal/ir/EmptyNode.java2
-rw-r--r--src/jdk/nashorn/internal/ir/Expression.java139
-rw-r--r--src/jdk/nashorn/internal/ir/ExpressionStatement.java9
-rw-r--r--src/jdk/nashorn/internal/ir/Flags.java6
-rw-r--r--src/jdk/nashorn/internal/ir/ForNode.java75
-rw-r--r--src/jdk/nashorn/internal/ir/FunctionCall.java14
-rw-r--r--src/jdk/nashorn/internal/ir/FunctionNode.java664
-rw-r--r--src/jdk/nashorn/internal/ir/IdentNode.java192
-rw-r--r--src/jdk/nashorn/internal/ir/IfNode.java35
-rw-r--r--src/jdk/nashorn/internal/ir/IndexNode.java39
-rw-r--r--src/jdk/nashorn/internal/ir/JoinPredecessor.java49
-rw-r--r--src/jdk/nashorn/internal/ir/JoinPredecessorExpression.java131
-rw-r--r--src/jdk/nashorn/internal/ir/JumpStatement.java99
-rw-r--r--src/jdk/nashorn/internal/ir/LabelNode.java54
-rw-r--r--src/jdk/nashorn/internal/ir/Labels.java43
-rw-r--r--src/jdk/nashorn/internal/ir/LexicalContext.java139
-rw-r--r--src/jdk/nashorn/internal/ir/LexicalContextExpression.java11
-rw-r--r--src/jdk/nashorn/internal/ir/LiteralNode.java371
-rw-r--r--src/jdk/nashorn/internal/ir/LocalVariableConversion.java174
-rw-r--r--src/jdk/nashorn/internal/ir/LoopNode.java23
-rw-r--r--src/jdk/nashorn/internal/ir/Node.java102
-rw-r--r--src/jdk/nashorn/internal/ir/ObjectNode.java13
-rw-r--r--src/jdk/nashorn/internal/ir/Optimistic.java92
-rw-r--r--src/jdk/nashorn/internal/ir/OptimisticLexicalContext.java128
-rw-r--r--src/jdk/nashorn/internal/ir/PropertyNode.java10
-rw-r--r--src/jdk/nashorn/internal/ir/ReturnNode.java4
-rw-r--r--src/jdk/nashorn/internal/ir/RuntimeNode.java125
-rw-r--r--src/jdk/nashorn/internal/ir/SplitNode.java70
-rw-r--r--src/jdk/nashorn/internal/ir/Statement.java30
-rw-r--r--src/jdk/nashorn/internal/ir/SwitchNode.java47
-rw-r--r--src/jdk/nashorn/internal/ir/Symbol.java553
-rw-r--r--src/jdk/nashorn/internal/ir/TemporarySymbols.java169
-rw-r--r--src/jdk/nashorn/internal/ir/Terminal.java37
-rw-r--r--src/jdk/nashorn/internal/ir/TernaryNode.java53
-rw-r--r--src/jdk/nashorn/internal/ir/ThrowNode.java44
-rw-r--r--src/jdk/nashorn/internal/ir/TryNode.java56
-rw-r--r--src/jdk/nashorn/internal/ir/UnaryNode.java213
-rw-r--r--src/jdk/nashorn/internal/ir/VarNode.java9
-rw-r--r--src/jdk/nashorn/internal/ir/WhileNode.java35
-rw-r--r--src/jdk/nashorn/internal/ir/WithNode.java4
-rw-r--r--src/jdk/nashorn/internal/ir/debug/ASTWriter.java27
-rw-r--r--src/jdk/nashorn/internal/ir/debug/ClassHistogramElement.java2
-rw-r--r--src/jdk/nashorn/internal/ir/debug/JSONWriter.java69
-rw-r--r--src/jdk/nashorn/internal/ir/debug/NashornClassReader.java551
-rw-r--r--src/jdk/nashorn/internal/ir/debug/NashornTextifier.java1257
-rw-r--r--src/jdk/nashorn/internal/ir/debug/ObjectSizeCalculator.java4
-rw-r--r--src/jdk/nashorn/internal/ir/debug/PrintVisitor.java126
-rw-r--r--src/jdk/nashorn/internal/ir/visitor/NodeOperatorVisitor.java24
-rw-r--r--src/jdk/nashorn/internal/ir/visitor/NodeVisitor.java22
-rw-r--r--src/jdk/nashorn/internal/lookup/Lookup.java56
-rw-r--r--src/jdk/nashorn/internal/lookup/MethodHandleFactory.java380
-rw-r--r--src/jdk/nashorn/internal/lookup/MethodHandleFunctionality.java25
-rw-r--r--src/jdk/nashorn/internal/objects/AccessorPropertyDescriptor.java5
-rw-r--r--src/jdk/nashorn/internal/objects/ArrayBufferView.java328
-rw-r--r--src/jdk/nashorn/internal/objects/DataPropertyDescriptor.java5
-rw-r--r--src/jdk/nashorn/internal/objects/GenericPropertyDescriptor.java5
-rw-r--r--src/jdk/nashorn/internal/objects/Global.java427
-rw-r--r--src/jdk/nashorn/internal/objects/NativeArray.java306
-rw-r--r--src/jdk/nashorn/internal/objects/NativeArrayBuffer.java153
-rw-r--r--src/jdk/nashorn/internal/objects/NativeDataView.java202
-rw-r--r--src/jdk/nashorn/internal/objects/NativeDate.java2
-rw-r--r--src/jdk/nashorn/internal/objects/NativeDebug.java174
-rw-r--r--src/jdk/nashorn/internal/objects/NativeError.java38
-rw-r--r--src/jdk/nashorn/internal/objects/NativeFloat32Array.java116
-rw-r--r--src/jdk/nashorn/internal/objects/NativeFloat64Array.java126
-rw-r--r--src/jdk/nashorn/internal/objects/NativeFunction.java89
-rw-r--r--src/jdk/nashorn/internal/objects/NativeInt16Array.java109
-rw-r--r--src/jdk/nashorn/internal/objects/NativeInt32Array.java112
-rw-r--r--src/jdk/nashorn/internal/objects/NativeInt8Array.java101
-rw-r--r--src/jdk/nashorn/internal/objects/NativeJSAdapter.java73
-rw-r--r--src/jdk/nashorn/internal/objects/NativeJava.java69
-rw-r--r--src/jdk/nashorn/internal/objects/NativeJavaImporter.java11
-rw-r--r--src/jdk/nashorn/internal/objects/NativeMath.java28
-rw-r--r--src/jdk/nashorn/internal/objects/NativeNumber.java70
-rw-r--r--src/jdk/nashorn/internal/objects/NativeObject.java71
-rw-r--r--src/jdk/nashorn/internal/objects/NativeRegExp.java38
-rw-r--r--src/jdk/nashorn/internal/objects/NativeStrictArguments.java5
-rw-r--r--src/jdk/nashorn/internal/objects/NativeString.java182
-rw-r--r--src/jdk/nashorn/internal/objects/NativeUint16Array.java114
-rw-r--r--src/jdk/nashorn/internal/objects/NativeUint32Array.java122
-rw-r--r--src/jdk/nashorn/internal/objects/NativeUint8Array.java108
-rw-r--r--src/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java160
-rw-r--r--src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java44
-rw-r--r--src/jdk/nashorn/internal/objects/annotations/Optimistic.java62
-rw-r--r--src/jdk/nashorn/internal/objects/annotations/Property.java4
-rw-r--r--src/jdk/nashorn/internal/parser/AbstractParser.java24
-rw-r--r--src/jdk/nashorn/internal/parser/JSONParser.java2
-rw-r--r--src/jdk/nashorn/internal/parser/Lexer.java38
-rw-r--r--src/jdk/nashorn/internal/parser/Parser.java458
-rw-r--r--src/jdk/nashorn/internal/parser/Token.java22
-rw-r--r--src/jdk/nashorn/internal/parser/TokenType.java1
-rw-r--r--src/jdk/nashorn/internal/runtime/AccessorProperty.java722
-rw-r--r--src/jdk/nashorn/internal/runtime/CodeInstaller.java24
-rw-r--r--src/jdk/nashorn/internal/runtime/CodeStore.java95
-rw-r--r--src/jdk/nashorn/internal/runtime/CompiledFunction.java838
-rw-r--r--src/jdk/nashorn/internal/runtime/CompiledFunctions.java114
-rw-r--r--src/jdk/nashorn/internal/runtime/ConsString.java46
-rw-r--r--src/jdk/nashorn/internal/runtime/Context.java391
-rw-r--r--src/jdk/nashorn/internal/runtime/Debug.java36
-rw-r--r--src/jdk/nashorn/internal/runtime/DebugLogger.java304
-rw-r--r--src/jdk/nashorn/internal/runtime/DebuggerSupport.java5
-rw-r--r--src/jdk/nashorn/internal/runtime/DefaultPropertyAccess.java42
-rw-r--r--src/jdk/nashorn/internal/runtime/ECMAErrors.java11
-rw-r--r--src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java65
-rw-r--r--src/jdk/nashorn/internal/runtime/FindProperty.java92
-rw-r--r--src/jdk/nashorn/internal/runtime/FunctionInitializer.java149
-rw-r--r--src/jdk/nashorn/internal/runtime/FunctionScope.java13
-rw-r--r--src/jdk/nashorn/internal/runtime/GlobalConstants.java435
-rw-r--r--src/jdk/nashorn/internal/runtime/GlobalFunctions.java64
-rw-r--r--src/jdk/nashorn/internal/runtime/JSONFunctions.java4
-rw-r--r--src/jdk/nashorn/internal/runtime/JSType.java857
-rw-r--r--src/jdk/nashorn/internal/runtime/Logging.java178
-rw-r--r--src/jdk/nashorn/internal/runtime/NativeJavaPackage.java43
-rw-r--r--src/jdk/nashorn/internal/runtime/OptimisticReturnFilters.java276
-rw-r--r--src/jdk/nashorn/internal/runtime/Property.java278
-rw-r--r--src/jdk/nashorn/internal/runtime/PropertyAccess.java36
-rw-r--r--src/jdk/nashorn/internal/runtime/PropertyDescriptor.java4
-rw-r--r--src/jdk/nashorn/internal/runtime/PropertyHashMap.java85
-rw-r--r--src/jdk/nashorn/internal/runtime/PropertyListeners.java3
-rw-r--r--src/jdk/nashorn/internal/runtime/PropertyMap.java286
-rw-r--r--src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java835
-rw-r--r--src/jdk/nashorn/internal/runtime/RewriteException.java419
-rw-r--r--src/jdk/nashorn/internal/runtime/ScriptEnvironment.java101
-rw-r--r--src/jdk/nashorn/internal/runtime/ScriptFunction.java400
-rw-r--r--src/jdk/nashorn/internal/runtime/ScriptFunctionData.java456
-rw-r--r--src/jdk/nashorn/internal/runtime/ScriptObject.java1285
-rw-r--r--src/jdk/nashorn/internal/runtime/ScriptRuntime.java216
-rw-r--r--src/jdk/nashorn/internal/runtime/ScriptingFunctions.java10
-rw-r--r--src/jdk/nashorn/internal/runtime/SetMethodCreator.java92
-rw-r--r--src/jdk/nashorn/internal/runtime/Source.java158
-rw-r--r--src/jdk/nashorn/internal/runtime/SpillProperty.java217
-rw-r--r--src/jdk/nashorn/internal/runtime/StoredScript.java (renamed from src/jdk/nashorn/internal/runtime/CompiledScript.java)40
-rw-r--r--src/jdk/nashorn/internal/runtime/Timing.java233
-rw-r--r--src/jdk/nashorn/internal/runtime/Undefined.java50
-rw-r--r--src/jdk/nashorn/internal/runtime/UnwarrantedOptimismException.java167
-rw-r--r--src/jdk/nashorn/internal/runtime/UserAccessorProperty.java219
-rw-r--r--src/jdk/nashorn/internal/runtime/WithObject.java90
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/ArrayData.java191
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/ArrayFilter.java21
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/ByteBufferArrayData.java4
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/ContinuousArrayData.java272
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/DeletedRangeArrayFilter.java12
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/IntArrayData.java98
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/LongArrayData.java94
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/NoTypeArrayData.java2
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/NumberArrayData.java84
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/ObjectArrayData.java89
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java54
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/TypedArrayData.java205
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/UndefinedArrayFilter.java28
-rw-r--r--src/jdk/nashorn/internal/runtime/events/RecompilationEvent.java68
-rw-r--r--src/jdk/nashorn/internal/runtime/events/RuntimeEvent.java87
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/Bootstrap.java99
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/BoundDynamicMethod.java6
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/InvokeByName.java2
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java83
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java225
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/JavaAdapterClassLoader.java22
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java2
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/JavaAdapterServices.java42
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/JavaArgumentConverters.java28
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/JavaSuperAdapter.java2
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/JavaSuperAdapterLinker.java10
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/LinkerCallSite.java81
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java8
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java16
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/NashornCallSiteDescriptor.java111
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/NashornGuards.java100
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/NashornLinker.java12
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/NashornPrimitiveLinker.java11
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/PrimitiveLookup.java4
-rw-r--r--src/jdk/nashorn/internal/runtime/logging/DebugLogger.java606
-rw-r--r--src/jdk/nashorn/internal/runtime/logging/Loggable.java56
-rw-r--r--src/jdk/nashorn/internal/runtime/logging/Logger.java48
-rw-r--r--src/jdk/nashorn/internal/runtime/options/KeyValueOption.java2
-rw-r--r--src/jdk/nashorn/internal/runtime/options/LoggingOption.java133
-rw-r--r--src/jdk/nashorn/internal/runtime/options/Options.java50
-rw-r--r--src/jdk/nashorn/internal/runtime/resources/Messages.properties6
-rw-r--r--src/jdk/nashorn/internal/runtime/resources/Options.properties89
-rw-r--r--src/jdk/nashorn/internal/scripts/JO.java13
-rw-r--r--src/jdk/nashorn/tools/Shell.java25
-rw-r--r--test/examples/apply_to_call_benchmark.js68
-rw-r--r--test/examples/string-micro.js8
-rw-r--r--test/script/assert.js6
-rw-r--r--test/script/basic/JDK-8010731.js64
-rw-r--r--test/script/basic/JDK-8012083.js2
-rw-r--r--test/script/basic/JDK-8015969.js18
-rw-r--r--test/script/basic/JDK-8016618.js12
-rw-r--r--test/script/basic/JDK-8016618.js.EXPECTED4
-rw-r--r--test/script/basic/JDK-8020357.js2
-rw-r--r--test/script/basic/JDK-8022903.js2
-rw-r--r--test/script/basic/JDK-8022903.js.EXPECTED4
-rw-r--r--test/script/basic/JDK-8025515.js4
-rw-r--r--test/script/basic/JDK-8026137.js4
-rw-r--r--test/script/basic/JDK-8027042.js44
-rw-r--r--test/script/basic/JDK-8027042.js.EXPECTED2
-rw-r--r--test/script/basic/JDK-8029384.js (renamed from test/script/basic/ranges_disabled.js)7
-rw-r--r--test/script/basic/JDK-8029384.js.EXPECTED1
-rw-r--r--test/script/basic/JDK-8029467.js1
-rw-r--r--test/script/basic/JDK-8029667.js2
-rw-r--r--test/script/basic/JDK-8030182_2.js2
-rw-r--r--test/script/basic/JDK-8030182_2.js.EXPECTED2
-rw-r--r--test/script/basic/JDK-8038413.js38
-rw-r--r--test/script/basic/JDK-8038413.js.EXPECTED1
-rw-r--r--test/script/basic/JDK-8038945.js240
-rw-r--r--test/script/basic/JDK-8038945.js.EXPECTED40
-rw-r--r--test/script/basic/JDK-8040024.js100
-rw-r--r--test/script/basic/JDK-8040024.js.EXPECTED8
-rw-r--r--test/script/basic/JDK-8041995.js69
-rw-r--r--test/script/basic/JDK-8041995.js.EXPECTED9
-rw-r--r--test/script/basic/JDK-8043133.js41
-rw-r--r--test/script/basic/JDK-8043133.js.EXPECTED1
-rw-r--r--test/script/basic/JDK-8043232.js92
-rw-r--r--test/script/basic/JDK-8043232.js.EXPECTED14
-rw-r--r--test/script/basic/JDK-8043235.js58
-rw-r--r--test/script/basic/JDK-8043235.js.EXPECTED2
-rw-r--r--test/script/basic/JDK-8043431.js42
-rw-r--r--test/script/basic/JDK-8043431.js.EXPECTED1
-rw-r--r--test/script/basic/JDK-8044533.js (renamed from test/script/basic/runsunspider-eager.js)8
-rw-r--r--test/script/basic/JDK-8044533.js.EXPECTED1
-rw-r--r--test/script/basic/JDK-8044534.js36
-rw-r--r--test/script/basic/JDK-8044534.js.EXPECTED4
-rw-r--r--test/script/basic/JDK-8046013.js57
-rw-r--r--test/script/basic/JDK-8046013.js.EXPECTED4
-rw-r--r--test/script/basic/JDK-8046026.js49
-rw-r--r--test/script/basic/JDK-8046026.js.EXPECTED1
-rw-r--r--test/script/basic/JDK-8046905.js91
-rw-r--r--test/script/basic/JDK-8046905.js.EXPECTED41
-rw-r--r--test/script/basic/JDK-8047035.js37
-rw-r--r--test/script/basic/JDK-8047035.js.EXPECTED4
-rw-r--r--test/script/basic/JDK-8047057.js75
-rw-r--r--test/script/basic/JDK-8047067.js36
-rw-r--r--test/script/basic/JDK-8047078.js38
-rw-r--r--test/script/basic/JDK-8047166.js31
-rw-r--r--test/script/basic/JDK-8047357.js32
-rw-r--r--test/script/basic/JDK-8047357.js.EXPECTED2
-rw-r--r--test/script/basic/JDK-8047359.js47
-rw-r--r--test/script/basic/JDK-8047369.js186
-rw-r--r--test/script/basic/JDK-8047371.js32
-rw-r--r--test/script/basic/JDK-8047371.js.EXPECTED1
-rw-r--r--test/script/basic/JDK-8047728.js54
-rw-r--r--test/script/basic/JDK-8047959.js59
-rw-r--r--test/script/basic/JDK-8047959.js.EXPECTED15
-rw-r--r--test/script/basic/JDK-8048071.js85
-rw-r--r--test/script/basic/JDK-8048071.js.EXPECTED11
-rw-r--r--test/script/basic/JDK-8048079_1.js35
-rw-r--r--test/script/basic/JDK-8048079_1.js.EXPECTED3
-rw-r--r--test/script/basic/JDK-8048079_2.js35
-rw-r--r--test/script/basic/JDK-8048079_2.js.EXPECTED3
-rw-r--r--test/script/basic/JDK-8048505.js52
-rw-r--r--test/script/basic/JDK-8048505.js.EXPECTED7
-rw-r--r--test/script/basic/JDK-8048586.js41
-rw-r--r--test/script/basic/JDK-8048718.js52
-rw-r--r--test/script/basic/JDK-8049086.js144
-rw-r--r--test/script/basic/JDK-8049086.js.EXPECTED48
-rw-r--r--test/script/basic/JDK-8049242.js78
-rw-r--r--test/script/basic/JDK-8049242.js.EXPECTED10
-rw-r--r--test/script/basic/JDK-8050432.js40
-rw-r--r--test/script/basic/JDK-8051439.js52
-rw-r--r--test/script/basic/JDK-8051439.js.EXPECTED2
-rw-r--r--test/script/basic/JDK-8054503.js50
-rw-r--r--test/script/basic/NASHORN-377.js2
-rw-r--r--test/script/basic/NASHORN-737.js.EXPECTED10
-rw-r--r--test/script/basic/apply_to_call/apply_to_call1.js66
-rw-r--r--test/script/basic/apply_to_call/apply_to_call1.js.EXPECTED8
-rw-r--r--test/script/basic/apply_to_call/apply_to_call2.js63
-rw-r--r--test/script/basic/apply_to_call/apply_to_call2.js.EXPECTED8
-rw-r--r--test/script/basic/apply_to_call/apply_to_call3.js67
-rw-r--r--test/script/basic/apply_to_call/apply_to_call3.js.EXPECTED9
-rw-r--r--test/script/basic/apply_to_call/apply_to_call4.js131
-rw-r--r--test/script/basic/apply_to_call/apply_to_call4.js.EXPECTED31
-rw-r--r--test/script/basic/apply_to_call/apply_to_call_recompile.js44
-rw-r--r--test/script/basic/apply_to_call/apply_to_call_recompile.js.EXPECTED6
-rw-r--r--test/script/basic/apply_to_call/apply_to_call_varargs.js75
-rw-r--r--test/script/basic/apply_to_call/apply_to_call_varargs.js.EXPECTED66
-rw-r--r--test/script/basic/arrays_int_key.js (renamed from test/script/basic/arraysIntKey.js)0
-rw-r--r--test/script/basic/arrays_int_key.js.EXPECTED (renamed from test/script/basic/arraysIntKey.js.EXPECTED)0
-rw-r--r--test/script/basic/boolean_arithmetic.js113
-rw-r--r--test/script/basic/boolean_arithmetic.js.EXPECTED60
-rw-r--r--test/script/basic/closure.js.EXPECTED4
-rw-r--r--test/script/basic/compile-octane-normal.js42
-rw-r--r--test/script/basic/compile-octane-normal.js.EXPECTED30
-rw-r--r--test/script/basic/compile-octane-splitter.js16
-rw-r--r--test/script/basic/compile-octane-splitter.js.EXPECTED44
-rw-r--r--test/script/basic/compile-octane.js121
-rw-r--r--test/script/basic/compile-octane.js.EXPECTED14
-rw-r--r--test/script/basic/dataview_new.js3
-rw-r--r--test/script/basic/exprclosure.js.EXPECTED4
-rw-r--r--test/script/basic/hideLocationProperties.js57
-rw-r--r--test/script/basic/hideLocationProperties.js.EXPECTED6
-rw-r--r--test/script/basic/octane-payload.js58
-rw-r--r--test/script/basic/optimistic_arithmetic_check_type.js77
-rw-r--r--test/script/basic/optimistic_arithmetic_check_type.js.EXPECTED38
-rw-r--r--test/script/basic/optimistic_assignment_check_type.js54
-rw-r--r--test/script/basic/optimistic_assignment_check_type.js.EXPECTED14
-rw-r--r--test/script/basic/optimistic_check_type.js100
-rw-r--r--test/script/basic/optimistic_check_type.js.EXPECTED30
-rw-r--r--test/script/basic/optimistic_logical_check_type.js65
-rw-r--r--test/script/basic/optimistic_logical_check_type.js.EXPECTED28
-rw-r--r--test/script/basic/parser/breakStat.js.EXPECTED20
-rw-r--r--test/script/basic/parser/continueStat.js.EXPECTED20
-rw-r--r--test/script/basic/parser/labelledStat.js.EXPECTED20
-rw-r--r--test/script/basic/parser/lhsExpr.js.EXPECTED40
-rw-r--r--test/script/basic/ranges_disabled.js.EXPECTED4
-rw-r--r--test/script/basic/ranges_enabled.js.EXPECTED4
-rw-r--r--test/script/basic/ranges_payload.js74
-rw-r--r--test/script/basic/relink_index_getter.js (renamed from test/script/basic/ranges_enabled.js)8
-rw-r--r--test/script/basic/relink_index_getter.js.EXPECTED2
-rw-r--r--test/script/basic/run-octane.js82
-rw-r--r--test/script/basic/runsunspider.js481
-rw-r--r--test/script/basic/runsunspider.js.EXPECTED (renamed from test/script/basic/runsunspider-lazy.js.EXPECTED)0
-rw-r--r--test/script/basic/scripting.js.EXPECTED8
-rw-r--r--test/script/basic/typedarrays.js2
-rw-r--r--test/script/basic/typedarrays2.js56
-rw-r--r--test/script/basic/typedarrays2.js.EXPECTED4
-rw-r--r--test/script/currently-failing/JDK-8010697.js (renamed from test/script/basic/JDK-8010697.js)6
-rw-r--r--test/script/currently-failing/JDK-8010697.js.EXPECTED (renamed from test/script/basic/JDK-8010697.js.EXPECTED)0
-rw-r--r--test/script/currently-failing/apply_to_call_bench.js93
-rw-r--r--test/script/currently-failing/apply_to_call_bench.js.EXPECTED6
-rw-r--r--test/script/currently-failing/optimistic_check_type_cases.js42
-rw-r--r--test/script/currently-failing/optimistic_check_type_cases.js.EXPECTED7
-rw-r--r--test/script/currently-failing/property_delete.js (renamed from test/script/maptests/property_delete.js)0
-rw-r--r--test/script/error/JDK-8026039.js.EXPECTED2
-rw-r--r--test/script/maptests/constructor.js2
-rw-r--r--test/script/maptests/maputil.js8
-rw-r--r--test/script/maptests/object_literals.js4
-rw-r--r--test/script/maptests/point.js2
-rw-r--r--test/script/maptests/property_add.js2
-rw-r--r--test/script/markdown.js38
-rw-r--r--test/script/markdown/anchors-by-reference.js33
-rw-r--r--test/script/markdown/anchors-by-reference.js.EXPECTED4
-rw-r--r--test/script/markdown/automatic-anchors.js (renamed from test/script/basic/runsunspider-lazy.js)11
-rw-r--r--test/script/markdown/automatic-anchors.js.EXPECTED1
-rw-r--r--test/script/markdown/blockquote-nested-markdown.js33
-rw-r--r--test/script/markdown/blockquote-nested-markdown.js.EXPECTED13
-rw-r--r--test/script/markdown/blockquote.js33
-rw-r--r--test/script/markdown/blockquote.js.EXPECTED5
-rw-r--r--test/script/markdown/code-block-html-escape.js33
-rw-r--r--test/script/markdown/code-block-html-escape.js.EXPECTED4
-rw-r--r--test/script/markdown/code-block.js33
-rw-r--r--test/script/markdown/code-block.js.EXPECTED4
-rw-r--r--test/script/markdown/doubline-list.js33
-rw-r--r--test/script/markdown/doubline-list.js.EXPECTED4
-rw-r--r--test/script/markdown/emphasis.js33
-rw-r--r--test/script/markdown/emphasis.js.EXPECTED5
-rw-r--r--test/script/markdown/escaped-number-period.js33
-rw-r--r--test/script/markdown/escaped-number-period.js.EXPECTED1
-rw-r--r--test/script/markdown/escaping.js33
-rw-r--r--test/script/markdown/escaping.js.EXPECTED31
-rw-r--r--test/script/markdown/github-style-at-start.js33
-rw-r--r--test/script/markdown/github-style-at-start.js.EXPECTED6
-rw-r--r--test/script/markdown/github-style-codeblock.js33
-rw-r--r--test/script/markdown/github-style-codeblock.js.EXPECTED11
-rw-r--r--test/script/markdown/github-style-linebreaks.js33
-rw-r--r--test/script/markdown/github-style-linebreaks.js.EXPECTED3
-rw-r--r--test/script/markdown/h1-with-double-hash.js33
-rw-r--r--test/script/markdown/h1-with-double-hash.js.EXPECTED1
-rw-r--r--test/script/markdown/h1-with-equals.js33
-rw-r--r--test/script/markdown/h1-with-equals.js.EXPECTED1
-rw-r--r--test/script/markdown/h1-with-single-hash.js33
-rw-r--r--test/script/markdown/h1-with-single-hash.js.EXPECTED1
-rw-r--r--test/script/markdown/h2-with-dashes.js33
-rw-r--r--test/script/markdown/h2-with-dashes.js.EXPECTED1
-rw-r--r--test/script/markdown/h2-with-double-hash.js33
-rw-r--r--test/script/markdown/h2-with-double-hash.js.EXPECTED1
-rw-r--r--test/script/markdown/h2-with-single-hash.js33
-rw-r--r--test/script/markdown/h2-with-single-hash.js.EXPECTED1
-rw-r--r--test/script/markdown/h3-with-double-hash.js33
-rw-r--r--test/script/markdown/h3-with-double-hash.js.EXPECTED1
-rw-r--r--test/script/markdown/h3-with-single-hash.js33
-rw-r--r--test/script/markdown/h3-with-single-hash.js.EXPECTED1
-rw-r--r--test/script/markdown/h4-with-single-hash.js33
-rw-r--r--test/script/markdown/h4-with-single-hash.js.EXPECTED1
-rw-r--r--test/script/markdown/h5-with-single-hash.js33
-rw-r--r--test/script/markdown/h5-with-single-hash.js.EXPECTED1
-rw-r--r--test/script/markdown/h6-with-single-hash.js33
-rw-r--r--test/script/markdown/h6-with-single-hash.js.EXPECTED1
-rw-r--r--test/script/markdown/horizontal-rules.js33
-rw-r--r--test/script/markdown/horizontal-rules.js.EXPECTED9
-rw-r--r--test/script/markdown/html5-strutural-tags.js33
-rw-r--r--test/script/markdown/html5-strutural-tags.js.EXPECTED22
-rw-r--r--test/script/markdown/images.js33
-rw-r--r--test/script/markdown/images.js.EXPECTED5
-rw-r--r--test/script/markdown/implicit-anchors.js33
-rw-r--r--test/script/markdown/implicit-anchors.js.EXPECTED1
-rw-r--r--test/script/markdown/inline-anchors.js33
-rw-r--r--test/script/markdown/inline-anchors.js.EXPECTED3
-rw-r--r--test/script/markdown/inline-code.js33
-rw-r--r--test/script/markdown/inline-code.js.EXPECTED11
-rw-r--r--test/script/markdown/inline-style-tag.js33
-rw-r--r--test/script/markdown/inline-style-tag.js.EXPECTED5
-rw-r--r--test/script/markdown/lazy-blockquote.js33
-rw-r--r--test/script/markdown/lazy-blockquote.js.EXPECTED5
-rw-r--r--test/script/markdown/list-with-blockquote.js33
-rw-r--r--test/script/markdown/list-with-blockquote.js.EXPECTED8
-rw-r--r--test/script/markdown/list-with-code.js33
-rw-r--r--test/script/markdown/list-with-code.js.EXPECTED6
-rw-r--r--test/script/markdown/multi-paragraph-list.js33
-rw-r--r--test/script/markdown/multi-paragraph-list.js.EXPECTED6
-rw-r--r--test/script/markdown/multiline-unordered-list.js33
-rw-r--r--test/script/markdown/multiline-unordered-list.js.EXPECTED5
-rw-r--r--test/script/markdown/nested-blockquote.js33
-rw-r--r--test/script/markdown/nested-blockquote.js.EXPECTED9
-rw-r--r--test/script/markdown/ordered-list-same-number.js33
-rw-r--r--test/script/markdown/ordered-list-same-number.js.EXPECTED5
-rw-r--r--test/script/markdown/ordered-list-wrong-numbers.js33
-rw-r--r--test/script/markdown/ordered-list-wrong-numbers.js.EXPECTED5
-rw-r--r--test/script/markdown/ordered-list.js33
-rw-r--r--test/script/markdown/ordered-list.js.EXPECTED5
-rw-r--r--test/script/markdown/relative-anchors.js33
-rw-r--r--test/script/markdown/relative-anchors.js.EXPECTED1
-rw-r--r--test/script/markdown/simple-paragraph.js33
-rw-r--r--test/script/markdown/simple-paragraph.js.EXPECTED1
-rw-r--r--test/script/markdown/strong.js33
-rw-r--r--test/script/markdown/strong.js.EXPECTED5
-rw-r--r--test/script/markdown/table-basic.js33
-rw-r--r--test/script/markdown/table-basic.js.EXPECTED21
-rw-r--r--test/script/markdown/table-large.js33
-rw-r--r--test/script/markdown/table-large.js.EXPECTED48
-rw-r--r--test/script/markdown/table-with-equals.js33
-rw-r--r--test/script/markdown/table-with-equals.js.EXPECTED21
-rw-r--r--test/script/markdown/unordered-list-asterisk.js33
-rw-r--r--test/script/markdown/unordered-list-asterisk.js.EXPECTED5
-rw-r--r--test/script/markdown/unordered-list-minus.js33
-rw-r--r--test/script/markdown/unordered-list-minus.js.EXPECTED5
-rw-r--r--test/script/markdown/unordered-list-plus.js33
-rw-r--r--test/script/markdown/unordered-list-plus.js.EXPECTED5
-rw-r--r--test/script/markdown/url-with-parenthesis.js33
-rw-r--r--test/script/markdown/url-with-parenthesis.js.EXPECTED1
-rw-r--r--test/script/nosecurity/JDK-8044798.js8
-rw-r--r--test/script/nosecurity/JDK-8044798.js.EXPECTED92
-rw-r--r--test/script/nosecurity/JDK-8044851.js93
-rw-r--r--test/script/nosecurity/JDK-8044851.js.EXPECTED53
-rw-r--r--test/script/nosecurity/JDK-8050964.js58
-rw-r--r--test/script/nosecurity/JDK-8055034.js64
-rw-r--r--test/script/nosecurity/JDK-8055034.js.EXPECTED2
-rw-r--r--test/script/nosecurity/JDK-8055107.js179
-rw-r--r--test/script/trusted/JDK-8006529.js37
-rw-r--r--test/script/trusted/event_queue.js123
-rw-r--r--test/script/trusted/event_queue.js.EXPECTED38
-rw-r--r--test/script/trusted/optimistic_recompilation.js83
-rw-r--r--test/script/trusted/optimistic_recompilation.js.EXPECTED27
-rw-r--r--test/src/UnnamedPackageTestCallback.java8
-rw-r--r--test/src/jdk/nashorn/api/javaaccess/NumberAccessTest.java28
-rw-r--r--test/src/jdk/nashorn/api/javaaccess/NumberBoxingTest.java40
-rw-r--r--test/src/jdk/nashorn/api/javaaccess/SharedObject.java2
-rw-r--r--test/src/jdk/nashorn/api/scripting/ScopeTest.java4
-rw-r--r--test/src/jdk/nashorn/api/scripting/ScriptEngineSecurityTest.java8
-rw-r--r--test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java36
-rw-r--r--test/src/jdk/nashorn/api/scripting/ScriptObjectMirrorTest.java55
-rw-r--r--test/src/jdk/nashorn/internal/parser/ParserTest.java7
-rw-r--r--test/src/jdk/nashorn/internal/runtime/CodeStoreAndPathTest.java30
-rw-r--r--test/src/jdk/nashorn/internal/runtime/ConsStringTest.java131
-rw-r--r--test/src/jdk/nashorn/internal/runtime/ContextTest.java22
-rw-r--r--test/src/jdk/nashorn/internal/runtime/ExceptionsNotSerializable.java77
-rw-r--r--test/src/jdk/nashorn/internal/runtime/NoPersistenceCachingTest.java5
-rw-r--r--test/src/jdk/nashorn/internal/runtime/TrustedScriptEngineTest.java2
-rw-r--r--test/src/jdk/nashorn/internal/test/framework/ParallelTestRunner.java6
-rw-r--r--test/src/jdk/nashorn/internal/test/framework/ScriptRunnable.java10
-rw-r--r--test/src/jdk/nashorn/internal/test/framework/TestHelper.java3
-rw-r--r--test/src/jdk/nashorn/test/tools/StaticTypeInspector.java54
584 files changed, 40692 insertions, 14794 deletions
diff --git a/bin/checkintest.sh b/bin/checkintest.sh
deleted file mode 100644
index c4a9e96c..00000000
--- a/bin/checkintest.sh
+++ /dev/null
@@ -1,266 +0,0 @@
-#!/bin/bash
-#
-# Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
-# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
-#
-# This code is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License version 2 only, as
-# published by the Free Software Foundation.
-#
-# This code is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-# version 2 for more details (a copy is included in the LICENSE file that
-# accompanied this code).
-#
-# You should have received a copy of the GNU General Public License version
-# 2 along with this work; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
-#
-# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
-# or visit www.oracle.com if you need additional information or have any
-# questions.
-#
-
-#best pass rate at test 262 known
-TEST262_PASS_AT_LEAST=435
-
-RUN_TEST="true"
-RUN_TEST262="true"
-RUN_NODE="true"
-KEEP_OUTPUT="true"
-CLEAN_AND_BUILD_NASHORN="true"
-
-#the stable node version to sync against
-NODE_LAST_STABLE=v0.6.18
-
-#parse args
-for arg in $*
-do
- if [ $arg = "--no-test" ]; then
- RUN_TEST="false"
- echo "**** WARNING - you have disabled 'ant test', which is a minimum checkin requirement..."
- elif [ $arg = "--no-262" ]; then
- RUN_TEST262="false"
- elif [ $arg = "--no-node" ]; then
- RUN_NODE="false"
- elif [ $arg = "--no-build" ]; then
- CLEAN_AND_BUILD_NASHORN="false"
- elif [ $arg = "--no-logs" ]; then
- KEEP_OUTPUT="false"
- fi
-done
-
-function lastpart() {
- arr=$(echo $1 | tr "/" "\n")
- for x in $arr
- do
- _last=$x
- done
- echo $_last
-}
-
-function check_installed() {
- which $1 >/dev/null
- if [ $? -ne 0 ]; then
- echo "Error $1 not installed: $?"
- exit 2
- fi
-}
-
-check_installed hg
-check_installed git
-check_installed mv
-check_installed git
-
-PWD=$(pwd);
-
-while [ -z $NASHORN_ROOT ]
-do
- if [ -e $PWD/.hg ]; then
- NASHORN_ROOT=${PWD}
- break
- fi
- PWD=$(dirname ${PWD})
-done
-
-echo "Nashorn root detected at ${NASHORN_ROOT}"
-
-COMMON_ROOT=$(dirname $NASHORN_ROOT)
-echo "Common root is ${COMMON_ROOT}"
-
-echo "Running checkintest..."
-
-ABSOLUTE_NASHORN_HOME=$COMMON_ROOT/$(lastpart $NASHORN_ROOT)
-
-if [ $CLEAN_AND_BUILD_NASHORN != "false" ]; then
- echo "Cleaning and building nashorn at $ABSOLUTE_NASHORN_HOME/nashorn..."
- $(cd $ABSOLUTE_NASHORN_HOME/nashorn; ant clean >/dev/null 2>/dev/null)
- $(cd $ABSOLUTE_NASHORN_HOME/nashorn; ant jar >/dev/null 2>/dev/null)
- echo "Done."
-fi
-
-function failure_check() {
- while read line
- do
- LINE=$(echo $line | grep "Tests run")
- if [ "${LINE}" != "" ]; then
- RESULT=$(echo $line | grep "Failures: 0" | grep "Errors: 0")
- if [ "${RESULT}" == "" ]; then
- TESTNAME=$2
- echo "There were errors in ${TESTNAME} : ${LINE}"
- exit 1
- fi
- fi
- done < $1
-}
-
-function test() {
- TEST_OUTPUT=$ABSOLUTE_NASHORN_HOME/$(mktemp tmp.XXXXX)
- echo "Running 'ant test' on nashorn from ${ABSOLUTE_NASHORN_HOME}/nashorn..."
- $(cd $ABSOLUTE_NASHORN_HOME/nashorn; ant test >$TEST_OUTPUT)
- echo "Done."
-
- failure_check $TEST_OUTPUT
-
- echo "**** SUCCESS: 'ant test' successful"
-
- if [ $KEEP_OUTPUT == "true" ]; then
- cp $TEST_OUTPUT ./checkintest.test.log
- rm -fr $TEST_OUTPUT
- fi
-}
-
-if [ $RUN_TEST != "false" ]; then
- test;
-fi
-
-function test262() {
-
- echo "Running 'ant test262parallel' on nashorn from ${ABSOLUTE_NASHORN_HOME}/nashorn..."
- TEST262_OUTPUT=$ABSOLUTE_NASHORN_HOME/$(mktemp tmp.XXXXX)
-
- echo "Looking for ${ABSOLUTE_NASHORN_HOME}/test/test262..."
-
- if [ ! -e $ABSOLUTE_NASHORN_HOME/nashorn/test/test262 ]; then
- echo "test262 is missing... looking in $COMMON_ROOT..."
- if [ ! -e $COMMON_ROOT/test262 ]; then
- echo "... not there either... cloning from repo..."
- hg clone http://hg.ecmascript.org/tests/test262 $COMMON_ROOT/test262 >/dev/null 2>/dev/null
- echo "Done."
- fi
- echo "Adding soft link ${COMMON_ROOT}/test262 -> ${ABSOLUTE_NASHORN_HOME}/test/test262..."
- ln -s $COMMON_ROOT/test262 $ABSOLUTE_NASHORN_HOME/nashorn/test/test262
- echo "Done."
- fi
-
- echo "Ensuring test262 is up to date..."
- $(cd $ABSOLUTE_NASHORN_HOME/nashorn/test/test262; hg pull -u >/dev/null 2>/dev/null)
- echo "Done."
-
- echo "Running test262..."
- $(cd $ABSOLUTE_NASHORN_HOME/nashorn; ant test262parallel > $TEST262_OUTPUT)
-
- FAILED=$(cat $TEST262_OUTPUT|grep "Tests run:"| cut -d ' ' -f 15 |tr -cd '"[[:digit:]]')
- if [ $FAILED -gt $TEST262_PASS_AT_LEAST ]; then
- echo "FAILURE: There are ${FAILED} failures in test262 and can be no more than ${TEST262_PASS_AT_LEAST}"
- cp $TEST262_OUTPUT ./checkintest.test262.log
- echo "See ./checkintest.test262.log"
- echo "Terminating due to error"
- exit 1
- elif [ $FAILED -lt $TEST262_PASS_AT_LEAST ]; then
- echo "There seem to have been fixes to 262. ${FAILED} < ${TEST262_PASS_AT_LEAST}. Please update limit in bin/checkintest.sh"
- fi
-
- echo "**** SUCCESS: Test262 passed with no more than ${TEST262_PASS_AT_LEAST} failures."
-
- if [ $KEEP_OUTPUT == "true" ]; then
- cp $TEST262_OUTPUT ./checkintest.test262.log
- rm -fr $TEST262_OUTPUT
- fi
-}
-
-if [ $RUN_TEST262 != "false" ]; then
- test262;
-fi;
-
-function testnode() {
- TESTNODEJAR_OUTPUT=$ABSOLUTE_NASHORN_HOME/$(mktemp tmp.XXXXX)
-
- echo "Running node tests..."
-#replace node jar properties nashorn with this nashorn
-
- NODEJAR_PROPERTIES=~/nodejar.properties
-
- NODE_HOME=$(cat $NODEJAR_PROPERTIES | grep ^node.home | cut -f2 -d=)
- NASHORN_HOME=$(cat $NODEJAR_PROPERTIES | grep ^nashorn.home | cut -f2 -d=)
-
- ABSOLUTE_NODE_HOME=$COMMON_ROOT/$(lastpart $NODE_HOME)
-
- echo "Writing nodejar.properties..."
-
- cat > $NODEJAR_PROPERTIES << EOF
-node.home=../node
-nashorn.home=../$(lastpart $NASHORN_ROOT)
-EOF
- echo "Done."
- echo "Checking node home ${ABSOLUTE_NODE_HOME}..."
-
- if [ ! -e $ABSOLUTE_NODE_HOME ]; then
- echo "Node base dir not found. Cloning node..."
- $(cd $COMMON_ROOT; git clone https://github.com/joyent/node.git $(lastpart $NODE_HOME) >/dev/null 2>/dev/null)
- echo "Done."
- echo "Updating to last stable version ${NODE_LAST_STABLE}..."
- $(cd $ABSOLUTE_NODE_HOME; git checkout $NODE_LAST_STABLE >/dev/null 2>/dev/null)
- echo "Done."
- echo "Running configure..."
- $(cd $ABSOLUTE_NODE_HOME; ./configure >/dev/null 2>/dev/null)
- echo "Done."
- fi
-
- echo "Ensuring node is built..."
-#make sure node is built
- $(cd $ABSOLUTE_NODE_HOME; make >/dev/null 2>/dev/null)
- echo "Done."
-
- NODEJAR_HOME=$COMMON_ROOT/nodejar
-
- if [ ! -e $NODEJAR_HOME ]; then
- echo "No node jar home found. cloning from depot..."
- $(cd $COMMON_ROOT; hg clone https://hg.kenai.com/hg/nodejs~source nodejar >/dev/null 2>/dev/null)
- $(cd $COMMON_ROOT/nodejar; ant >/dev/null)
- echo "Done."
- echo "Copying node files..."
- $(cd $COMMON_ROOT/nodejar; ant copy-node-files >/dev/null 2>/dev/null)
- echo "Patching node files..."
- $(cd $COMMON_ROOT/nodejar; ant patch-node-files >/dev/null 2>/dev/null)
- echo "Done."
- fi
-
- echo "Ensuring node.jar is up to date from source depot..."
- $(cd $COMMON_ROOT/nodejar; hg pull -u >/dev/null 2>/dev/null)
- echo "Done."
-
- echo "Installing nashorn..."
- $(cd $COMMON_ROOT/nodejar; ant >/dev/null)
- echo "Done."
-
- echo "Running node.jar test..."
- $(cd $COMMON_ROOT/nodejar; mvn clean verify >$TESTNODEJAR_OUTPUT)
- echo "Done."
-
- failure_check $TESTNODEJAR_OUTPUT
-
- echo "**** SUCCESS: Node test successful."
-
- if [ $KEEP_OUTPUT == "true" ]; then
- rm -fr $TESTNODEJAR_OUTPUT
- cp $TESTNODEJAR_OUTPUT ./checkintest.nodejar.log
- fi
-}
-
-if [ $RUN_NODE != "false" ]; then
- testnode;
-fi;
-
-echo "Finished"
diff --git a/bin/dump_octane_code.sh b/bin/dump_octane_code.sh
deleted file mode 100644
index d24ab2ce..00000000
--- a/bin/dump_octane_code.sh
+++ /dev/null
@@ -1,53 +0,0 @@
-#!/bin/bash
-# Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
-# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
-#
-# This code is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License version 2 only, as
-# published by the Free Software Foundation.
-#
-# This code is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-# version 2 for more details (a copy is included in the LICENSE file that
-# accompanied this code).
-#
-# You should have received a copy of the GNU General Public License version
-# 2 along with this work; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
-#
-# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
-# or visit www.oracle.com if you need additional information or have any
-# questions.
-#
-
-#
-# The purpose of this script is to provide a large amount of IR/bytecode from a known
-# application to be diffed against the same output with a different Nashorn version.
-# That way we can quickly detect if a seemingly minute change modifies a lot of code,
-# which it most likely shouldn't. One example of this was when AccessSpecializer was
-# moved into Lower the first time, it worked fine, but as a lot of Scope information
-# at the time was finalized further down the code pipeline it did a lot fewer callsite
-# specializations. This would have been immediately detected with a before and after
-# diff using the output from this script.
-#
-
-ITERS=$1
-if [ -z $ITERS ]; then
- ITERS=7
-fi
-NASHORN_JAR=dist/nashorn.jar
-JVM_FLAGS="-ea -esa -server -jar ${NASHORN_JAR}"
-
-BENCHMARKS=( "box2d.js" "code-load.js" "crypto.js" "deltablue.js" "earley-boyer.js" "gbemu.js" "mandreel.js" "navier-stokes.js" "pdfjs.js" "raytrace.js" "regexp.js" "richards.js" "splay.js" )
-
-for BENCHMARK in "${BENCHMARKS[@]}"
-do
- echo "START: ${BENCHMARK}"
- CMD="${JAVA_HOME}/bin/java ${JVM_FLAGS} -co --print-lower-parse test/script/external/octane/${BENCHMARK}"
- $CMD
- echo "END: ${BENCHMARK}"
- echo ""
-done
-
-echo "Done"
diff --git a/bin/jjs.bat b/bin/jjs.bat
deleted file mode 100644
index 3c1c1595..00000000
--- a/bin/jjs.bat
+++ /dev/null
@@ -1,27 +0,0 @@
-rem
-rem Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
-rem DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
-rem
-rem This code is free software; you can redistribute it and/or modify it
-rem under the terms of the GNU General Public License version 2 only, as
-rem published by the Free Software Foundation. Oracle designates this
-rem particular file as subject to the "Classpath" exception as provided
-rem by Oracle in the LICENSE file that accompanied this code.
-rem
-rem This code is distributed in the hope that it will be useful, but WITHOUT
-rem ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-rem FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-rem version 2 for more details (a copy is included in the LICENSE file that
-rem accompanied this code).
-rem
-rem You should have received a copy of the GNU General Public License version
-rem 2 along with this work; if not, write to the Free Software Foundation,
-rem Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
-rem
-rem Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
-rem or visit www.oracle.com if you need additional information or have any
-rem questions.
-rem
-@echo off
-
-java -Xms2G -Xmx2G -XX:-TieredCompilation -server -esa -ea -Djava.ext.dirs=%~dp0\..\dist -XX:+HeapDumpOnOutOfMemoryError -Dnashorn.debug=true -Djava.lang.invoke.MethodHandle.DEBUG_NAMES=false jdk.nashorn.tools.Shell
diff --git a/bin/jjs b/bin/jjsdebug.sh
index f89a07c2..509700c3 100644
--- a/bin/jjs
+++ b/bin/jjsdebug.sh
@@ -1,29 +1,25 @@
-#!/bin/bash
+#!/bin/sh
#
-# Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+# 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.
-#
+# published by the Free Software Foundation.
+#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
-#
+#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
-#
+#
# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
# or visit www.oracle.com if you need additional information or have any
# questions.
#
-[ -z "$JAVA_HOME" ] && echo "Please set JAVA_HOME" && exit 1;
-
-$JAVA_HOME/bin/java -server -XX:+TieredCompilation -Xms2G -Xmx2G -esa -ea -Djava.ext.dirs=`dirname $0`/../dist:$JAVA_HOME/jre/lib/ext -XX:+HeapDumpOnOutOfMemoryError -Djava.lang.invoke.MethodHandle.DEBUG_NAMES=false -Dnashorn.debug=true jdk.nashorn.tools.Shell $*
+$JAVA_HOME/bin/jjs -J-Djava.ext.dirs=`dirname $0`/../dist -J-agentlib:jdwp=transport=dt_socket,address=localhost:9009,server=y,suspend=y $*
diff --git a/bin/jjssecure b/bin/jjssecure
deleted file mode 100644
index db6bdfc4..00000000
--- a/bin/jjssecure
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/bin/bash
-#
-# Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
-# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
-#
-# This code is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License version 2 only, as
-# published by the Free Software Foundation. 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.
-#
-
-[ -z "$JAVA_HOME" ] && echo "Please set JAVA_HOME" && exit 1;
-
-$JAVA_HOME/bin/java -Xms2G -Xmx2G -XX:-TieredCompilation -server -esa -ea -Djava.security.properties=`dirname $0`/../make/java.security.override -Djava.ext.dirs=`dirname $0`/../dist:$JAVA_HOME/jre/lib/ext -XX:+HeapDumpOnOutOfMemoryError -Dnashorn.debug=true -Djava.lang.invoke.MethodHandle.DEBUG_NAMES=true -Dnashorn.home=`dirname $0`/.. -Djava.security.manager jdk.nashorn.tools.Shell $*
diff --git a/bin/jjssecure.bat b/bin/jjssecure.bat
deleted file mode 100644
index f8da10aa..00000000
--- a/bin/jjssecure.bat
+++ /dev/null
@@ -1,27 +0,0 @@
-rem
-rem Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
-rem DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
-rem
-rem This code is free software; you can redistribute it and/or modify it
-rem under the terms of the GNU General Public License version 2 only, as
-rem published by the Free Software Foundation. Oracle designates this
-rem particular file as subject to the "Classpath" exception as provided
-rem by Oracle in the LICENSE file that accompanied this code.
-rem
-rem This code is distributed in the hope that it will be useful, but WITHOUT
-rem ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-rem FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-rem version 2 for more details (a copy is included in the LICENSE file that
-rem accompanied this code).
-rem
-rem You should have received a copy of the GNU General Public License version
-rem 2 along with this work; if not, write to the Free Software Foundation,
-rem Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
-rem
-rem Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
-rem or visit www.oracle.com if you need additional information or have any
-rem questions.
-rem
-@echo off
-
-java -Xms2G -Xmx2G -XX:-TieredCompilation -server -esa -ea -Djava.security.properties=%~dp0\..\make\java.security.override -Djava.ext.dirs=%~dp0\..\dist -XX:+HeapDumpOnOutOfMemoryError -Dnashorn.debug=true -Djava.lang.invoke.MethodHandle.DEBUG_NAMES=false -Dnashorn.home=%~dp0\.. -Djava.security.manager jdk.nashorn.tools.Shell
diff --git a/bin/nashorn.bat b/bin/nashorn.bat
deleted file mode 100644
index 2961ac69..00000000
--- a/bin/nashorn.bat
+++ /dev/null
@@ -1,27 +0,0 @@
-rem
-rem Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
-rem DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
-rem
-rem This code is free software; you can redistribute it and/or modify it
-rem under the terms of the GNU General Public License version 2 only, as
-rem published by the Free Software Foundation. Oracle designates this
-rem particular file as subject to the "Classpath" exception as provided
-rem by Oracle in the LICENSE file that accompanied this code.
-rem
-rem This code is distributed in the hope that it will be useful, but WITHOUT
-rem ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-rem FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-rem version 2 for more details (a copy is included in the LICENSE file that
-rem accompanied this code).
-rem
-rem You should have received a copy of the GNU General Public License version
-rem 2 along with this work; if not, write to the Free Software Foundation,
-rem Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
-rem
-rem Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
-rem or visit www.oracle.com if you need additional information or have any
-rem questions.
-rem
-@echo off
-
-jrunscript -J-Xms2G -J-Xmx2G -J-XX:-TieredCompilation -J-server -J-esa -J-ea -J-Djava.ext.dirs=%~dp0\..\dist -J-XX:+HeapDumpOnOutOfMemoryError -J-Dnashorn.debug=true -J-Djava.lang.invoke.MethodHandle.DEBUG_NAMES=false -l nashorn
diff --git a/bin/nashornsecure b/bin/nashornsecure
deleted file mode 100644
index 77c7c529..00000000
--- a/bin/nashornsecure
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/bin/bash
-#
-# Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
-# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
-#
-# This code is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License version 2 only, as
-# published by the Free Software Foundation. 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.
-#
-
-[ -z "$JAVA_HOME" ] && echo "Please set JAVA_HOME" && exit 1;
-
-$JAVA_HOME/bin/jrunscript -J-Djava.security.properties=`dirname $0`/../make/java.security.override -J-Djava.security.manager -J-Xms2G -J-Xmx2G -J-XX:-TieredCompilation -J-server -J-esa -J-ea -J-Djava.ext.dirs=`dirname $0`/../dist:$JAVA_HOME/jre/lib/ext -J-XX:+HeapDumpOnOutOfMemoryError -J-Djava.lang.invoke.MethodHandle.DEBUG_NAMES=false -J-Dnashorn.debug=true -l nashorn $*
diff --git a/bin/nashornsecure.bat b/bin/nashornsecure.bat
deleted file mode 100644
index 5a4eca63..00000000
--- a/bin/nashornsecure.bat
+++ /dev/null
@@ -1,27 +0,0 @@
-rem
-rem Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
-rem DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
-rem
-rem This code is free software; you can redistribute it and/or modify it
-rem under the terms of the GNU General Public License version 2 only, as
-rem published by the Free Software Foundation. Oracle designates this
-rem particular file as subject to the "Classpath" exception as provided
-rem by Oracle in the LICENSE file that accompanied this code.
-rem
-rem This code is distributed in the hope that it will be useful, but WITHOUT
-rem ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-rem FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-rem version 2 for more details (a copy is included in the LICENSE file that
-rem accompanied this code).
-rem
-rem You should have received a copy of the GNU General Public License version
-rem 2 along with this work; if not, write to the Free Software Foundation,
-rem Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
-rem
-rem Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
-rem or visit www.oracle.com if you need additional information or have any
-rem questions.
-rem
-@echo off
-
-jrunscript -J-Djava.security.properties=%~dp0\..\make\java.security.override -J-Djava.security.manager -J-Xms2G -J-Xmx2G -J-XX:-TieredCompilation -J-server -J-esa -J-ea -J-Djava.ext.dirs=%~dp0\..\dist -J-XX:+HeapDumpOnOutOfMemoryError -J-Dnashorn.debug=true -J-Djava.lang.invoke.MethodHandle.DEBUG_NAMES=false -l nashorn
diff --git a/bin/nashorn b/bin/run_octane.sh
index da22be1f..a50137f0 100644
--- a/bin/nashorn
+++ b/bin/run_octane.sh
@@ -1,29 +1,51 @@
#!/bin/bash
#
-# Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
-#
+#
# This code is free software; you can redistribute it and/or modify it
# 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.
-#
+# published by the Free Software Foundation.
+#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
-#
+#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
-#
+#
# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
# or visit www.oracle.com if you need additional information or have any
# questions.
#
-[ -z "$JAVA_HOME" ] && echo "Please set JAVA_HOME" && exit 1;
+LOG="./octane_$(date|sed "s/ /_/g"|sed "s/:/_/g").log"
+
+run_one() {
+ sh ../bin/runopt.sh -scripting ../test/script/basic/run-octane.js -- $1 --verbose --iterations 25 | tee -a $LOG
+}
+
+if [ -z $1 ]; then
+
+ run_one "box2d"
+ run_one "code-load"
+ run_one "crypto"
+ run_one "deltablue"
+ run_one "earley-boyer"
+ run_one "gbemu"
+ run_one "mandreel"
+ run_one "navier-stokes"
+ run_one "pdfjs"
+ run_one "raytrace"
+ run_one "regexp"
+ run_one "richards"
+ run_one "splay"
+ run_one "typescript"
+ run_one "zlib"
-$JAVA_HOME/bin/jrunscript -J-Xms2G -J-Xmx2G -J-XX:-TieredCompilation -J-server -J-esa -J-ea -J-Djava.ext.dirs=`dirname $0`/../dist:$JAVA_HOME/jre/lib/ext -J-XX:+HeapDumpOnOutOfMemoryError -J-Djava.lang.invoke.MethodHandle.DEBUG_NAMES=false -J-Dnashorn.debug=true -l nashorn $*
+else
+ run_one $1
+fi
diff --git a/bin/runopt.sh b/bin/runopt.sh
new file mode 100644
index 00000000..6b26e287
--- /dev/null
+++ b/bin/runopt.sh
@@ -0,0 +1,107 @@
+#!/bin/sh
+#
+# Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+
+###########################################################################################
+# This is a helper script to evaluate nashorn with optimistic types
+# it produces a flight recording for every run, and uses the best
+# known flags for performance for the current configration
+###########################################################################################
+
+# Flags to 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"
+
+
+# Flags to run trusted tests from the Nashorn test suite
+#FLAGS="-Djava.security.manager -Djava.security.policy=../build/nashorn.policy -Dnashorn.debug"
+
+
+# Unique timestamped file name for JFR recordings. For JFR, we also have to
+# crank up the stack cutoff depth to 1024, because of ridiculously long lambda form
+# stack traces.
+#
+# It is also recommended that you go into $JAVA_HOME/jre/lib/jfr/default.jfc and
+# set the "method-sampling-interval" Normal and Maximum sample time as low as you
+# can go (10 ms on most platforms). The default is normally higher. The increased
+# sampling overhead is usually negligible for Nashorn runs, but the data is better
+
+JFR_FILENAME="./nashorn_$(date|sed "s/ /_/g"|sed "s/:/_/g").jfr"
+
+
+# Directory where to look for nashorn.jar in a dist folder. The default is "..", assuming
+# that we run the script from the make dir
+DIR=..
+NASHORN_JAR=$DIR/dist/nashorn.jar
+
+
+# The built Nashorn jar is placed first in the bootclasspath to override the JDK
+# nashorn.jar in $JAVA_HOME/jre/lib/ext. Thus, we also need -esa, as assertions in
+# nashorn count as system assertions in this configuration
+
+$JAVA_HOME/bin/java \
+$FLAGS \
+-ea \
+-esa \
+-Xbootclasspath/p:$NASHORN_JAR \
+-Xms2G -Xmx2G \
+-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 \
+
+
+# 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/bin/verbose_octane.bat b/bin/verbose_octane.bat
deleted file mode 100644
index ab9e4846..00000000
--- a/bin/verbose_octane.bat
+++ /dev/null
@@ -1,59 +0,0 @@
-rem
-rem Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
-rem DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
-rem
-rem This code is free software; you can redistribute it and/or modify it
-rem under the terms of the GNU General Public License version 2 only, as
-rem published by the Free Software Foundation.
-rem
-rem This code is distributed in the hope that it will be useful, but WITHOUT
-rem ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-rem FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-rem version 2 for more details (a copy is included in the LICENSE file that
-rem accompanied this code).
-rem
-rem You should have received a copy of the GNU General Public License version
-rem 2 along with this work; if not, write to the Free Software Foundation,
-rem Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
-rem
-rem Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
-rem or visit www.oracle.com if you need additional information or have any
-rem questions.
-rem
-@echo off
-
-if "%JAVA_HOME%" neq "" (
- call :run "%JAVA_HOME%/bin/java"
-) else (
- call :run java
-)
-goto :EOF
-
-:run
-setlocal
-set NASHORN_JAR=dist/nashorn.jar
-set JVM_FLAGS=-Xms2G -Xmx2G -XX:-TieredCompilation -server -esa -ea -jar %NASHORN_JAR%
-set JVM_FLAGS7=-Xbootclasspath/p:%NASHORN_JAR% %JVM_FLAGS%
-set OCTANE_ARGS=--verbose --iterations 7
-
-%1 -fullversion 2>&1 | findstr /L /C:"version ""1.7"
-if %errorlevel% equ 0 (
- set CMD=%1 %JVM_FLAGS7%
-) else (
- %1 -fullversion
- set CMD=%1 %JVM_FLAGS%
-)
-
-%CMD% test/script/basic/run-octane.js -- test/script/external/octane/box2d.js %OCTANE_ARGS%
-%CMD% test/script/basic/run-octane.js -- test/script/external/octane/code-load.js %OCTANE_ARGS%
-%CMD% test/script/basic/run-octane.js -- test/script/external/octane/crypto.js %OCTANE_ARGS%
-%CMD% test/script/basic/run-octane.js -- test/script/external/octane/deltablue.js %OCTANE_ARGS%
-%CMD% test/script/basic/run-octane.js -- test/script/external/octane/gbemu.js %OCTANE_ARGS%
-%CMD% test/script/basic/run-octane.js -- test/script/external/octane/navier-stokes.js %OCTANE_ARGS%
-%CMD% test/script/basic/run-octane.js -- test/script/external/octane/pdfjs.js %OCTANE_ARGS%
-%CMD% test/script/basic/run-octane.js -- test/script/external/octane/raytrace.js %OCTANE_ARGS%
-%CMD% test/script/basic/run-octane.js -- test/script/external/octane/regexp.js %OCTANE_ARGS%
-%CMD% test/script/basic/run-octane.js -- test/script/external/octane/richards.js %OCTANE_ARGS%
-%CMD% test/script/basic/run-octane.js -- test/script/external/octane/splay.js %OCTANE_ARGS%
-endlocal
-goto :EOF
diff --git a/bin/verbose_octane.sh b/bin/verbose_octane.sh
deleted file mode 100644
index 1895afed..00000000
--- a/bin/verbose_octane.sh
+++ /dev/null
@@ -1,58 +0,0 @@
-#!/bin/bash
-# Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
-# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
-#
-# This code is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License version 2 only, as
-# published by the Free Software Foundation.
-#
-# This code is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-# version 2 for more details (a copy is included in the LICENSE file that
-# accompanied this code).
-#
-# You should have received a copy of the GNU General Public License version
-# 2 along with this work; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
-#
-# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
-# or visit www.oracle.com if you need additional information or have any
-# questions.
-#
-
-ITERS=$1
-if [ -z $ITERS ]; then
- ITERS=7
-fi
-NASHORN_JAR=dist/nashorn.jar
-JVM_FLAGS="-Djava.ext.dirs=`dirname $0`/../dist:$JAVA_HOME/jre/lib/ext -XX:+UnlockDiagnosticVMOptions -Dnashorn.unstable.relink.threshold=8 -Xms2G -Xmx2G -XX:+TieredCompilation -server -jar ${NASHORN_JAR}"
-JVM_FLAGS7="-Xbootclasspath/p:${NASHORN_JAR} ${JVM_FLAGS}"
-OCTANE_ARGS="--verbose --iterations ${ITERS}"
-
-BENCHMARKS=( "box2d.js" "code-load.js" "crypto.js" "deltablue.js" "earley-boyer.js" "gbemu.js" "navier-stokes.js" "pdfjs.js" "raytrace.js" "regexp.js" "richards.js" "splay.js" )
-# TODO mandreel.js has metaspace issues
-
-if [ ! -z $JAVA7_HOME ]; then
- echo "running ${ITERS} iterations with java7 using JAVA_HOME=${JAVA7_HOME}..."
- for BENCHMARK in "${BENCHMARKS[@]}"
- do
- CMD="${JAVA7_HOME}/bin/java ${JVM_FLAGS} test/script/basic/run-octane.js -- test/script/external/octane/${BENCHMARK} ${OCTANE_ARGS}"
- $CMD
- done
-else
- echo "no JAVA7_HOME set. skipping java7"
-fi
-
-if [ ! -z $JAVA8_HOME ]; then
- echo "running ${ITERS} iterations with java8 using JAVA_HOME=${JAVA8_HOME}..."
- for BENCHMARK in "${BENCHMARKS[@]}"
- do
- CMD="${JAVA8_HOME}/bin/java ${JVM_FLAGS} test/script/basic/run-octane.js -- test/script/external/octane/${BENCHMARK} ${OCTANE_ARGS}"
- $CMD
- done
-else
- echo "no JAVA8_HOME set."
-fi
-
-echo "Done"
diff --git a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MemberInfo.java b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MemberInfo.java
index e47f8b16..c8a929dc 100644
--- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MemberInfo.java
+++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MemberInfo.java
@@ -319,7 +319,7 @@ public final class MemberInfo implements Cloneable {
break;
case FUNCTION: {
final Type returnType = Type.getReturnType(javaDesc);
- if (!isValidJSType(returnType)) {
+ if (!(isValidJSType(returnType) || Type.VOID_TYPE == returnType)) {
error("return value of a @Function method should be a valid JS type, found " + returnType);
}
final Type[] argTypes = Type.getArgumentTypes(javaDesc);
@@ -351,7 +351,7 @@ public final class MemberInfo implements Cloneable {
break;
case SPECIALIZED_FUNCTION: {
final Type returnType = Type.getReturnType(javaDesc);
- if (!isValidJSType(returnType)) {
+ if (!(isValidJSType(returnType) || Type.VOID_TYPE == returnType)) {
error("return value of a @SpecializedFunction method should be a valid JS type, found " + returnType);
}
final Type[] argTypes = Type.getArgumentTypes(javaDesc);
@@ -371,9 +371,8 @@ public final class MemberInfo implements Cloneable {
error("first argument of a @Getter method should be of Object type, found: " + argTypes[0]);
}
- final Type returnType = Type.getReturnType(javaDesc);
- if (!isJavaLangObject(returnType)) {
- error("return type of a @Getter method should be Object, found: " + javaDesc);
+ if (Type.getReturnType(javaDesc).equals(Type.VOID_TYPE)) {
+ error("return type of getter should not be void");
}
}
break;
@@ -413,6 +412,10 @@ public final class MemberInfo implements Cloneable {
}
}
}
+ break;
+
+ default:
+ break;
}
}
@@ -451,7 +454,7 @@ public final class MemberInfo implements Cloneable {
if (type.getSort() == Type.OBJECT) {
try {
- final Class clazz = Class.forName(type.getClassName(), false, myLoader);
+ final Class<?> clazz = Class.forName(type.getClassName(), false, myLoader);
return ScriptObject.class.isAssignableFrom(clazz);
} catch (final ClassNotFoundException cnfe) {
return false;
diff --git a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MethodGenerator.java b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MethodGenerator.java
index 479d1d31..a8d6ae2b 100644
--- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MethodGenerator.java
+++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MethodGenerator.java
@@ -413,7 +413,8 @@ public class MethodGenerator extends MethodVisitor {
super.visitMethodInsn(INVOKEVIRTUAL,
"java/io/PrintStream",
"println",
- "(Ljava/lang/String;)V", false);
+ "(Ljava/lang/String;)V",
+ false);
}
// print the object on the top of the stack
@@ -426,6 +427,7 @@ public class MethodGenerator extends MethodVisitor {
super.visitMethodInsn(INVOKEVIRTUAL,
"java/io/PrintStream",
"println",
- "(Ljava/lang/Object;)V", false);
+ "(Ljava/lang/Object;)V",
+ false);
}
}
diff --git a/docs/DEVELOPER_README b/docs/DEVELOPER_README
index 9a2fffdf..fe140954 100644
--- a/docs/DEVELOPER_README
+++ b/docs/DEVELOPER_README
@@ -737,26 +737,6 @@ an implementation based on Joni, the regular expression engine used by
the JRuby project. The default value for this flag is "joni"
-SYSTEM PROPERTY: -Dnashorn.time
-
-This enables timers for various phases of script compilation. The timers
-will be dumped when the Nashorn process exits. We see a percentage value
-of how much time was spent not executing bytecode (i.e. compilation and
-internal tasks) at the end of the report.
-
-Here is an example:
-
-[JavaScript Parsing] 61 ms
-[Constant Folding] 11 ms
-[Control Flow Lowering] 26 ms
-[Type Attribution] 81 ms
-[Range Analysis] 0 ms
-[Code Splitting] 29 ms
-[Type Finalization] 19 ms
-[Bytecode Generation] 189 ms
-[Code Installation] 7 ms
-Total runtime: 508 ms (Non-runtime: 423 ms [83%])
-
===============
2. The loggers.
===============
@@ -887,6 +867,34 @@ etc. It will also show the internal representation of respective field
(Object in the normal case, unless running with the dual field
representation)
+* time
+
+This enables timers for various phases of script compilation. The timers
+will be dumped when the Nashorn process exits. We see a percentage value
+of how much time was spent not executing bytecode (i.e. compilation and
+internal tasks) at the end of the report.
+
+A finer level than "info" will show individual compilation timings as they
+happen.
+
+Here is an example:
+
+[time] Accumulated complation phase Timings:
+[time]
+[time] 'JavaScript Parsing' 1076 ms
+[time] 'Constant Folding' 159 ms
+[time] 'Control Flow Lowering' 303 ms
+[time] 'Program Point Calculation' 282 ms
+[time] 'Builtin Replacement' 71 ms
+[time] 'Code Splitting' 670 ms
+[time] 'Symbol Assignment' 474 ms
+[time] 'Scope Depth Computation' 249 ms
+[time] 'Optimistic Type Assignment' 186 ms
+[time] 'Local Variable Type Calculation' 526 ms
+[time] 'Bytecode Generation' 5177 ms
+[time] 'Class Installation' 1854 ms
+[time]
+[time] Total runtime: 11994 ms (Non-runtime: 11027 ms [91%])
=======================
3. Undocumented options
@@ -914,11 +922,10 @@ A short summary follows:
-cp, -classpath (-cp path. Specify where to find user class files.)
- -co, --compile-only (Compile script without running. Exit after compilation)
+ -co, --compile-only (Compile without running.)
param: [true|false] default: false
- -d, --dump-debug-dir (specify a destination directory to dump class files.
- This must be combined with the --compile-only option to work)
+ -d, --dump-debug-dir (specify a destination directory to dump class files.)
param: <path>
--debug-lines (Generate line number table in .class files.)
@@ -954,10 +961,6 @@ A short summary follows:
-h, -help (Print help for command line flags.)
param: [true|false] default: false
- --lazy-compilation (EXPERIMENTAL: Use lazy code generation strategies - do not compile
- the entire script at once.)
- param: [true|false] default: false
-
--loader-per-compile (Create a new class loader per compile.)
param: [true|false] default: true
@@ -965,16 +968,16 @@ A short summary follows:
param: <locale> default: en-US
--log (Enable logging of a given level for a given number of sub systems.
- [for example: --log=fields:finest,codegen:info])
+ [for example: --log=fields:finest,codegen:info].)
param: <module:level>,*
- -nj, --no-java (No Java support)
+ -nj, --no-java (Disable Java support.)
param: [true|false] default: false
- -nse, --no-syntax-extensions (No non-standard syntax extensions)
+ -nse, --no-syntax-extensions (Disallow non-standard syntax extensions.)
param: [true|false] default: false
- -nta, --no-typed-arrays (No Typed arrays support)
+ -nta, --no-typed-arrays (Disable typed arrays support.)
param: [true|false] default: false
--parse-only (Parse without compiling.)
@@ -983,13 +986,15 @@ A short summary follows:
--print-ast (Print abstract syntax tree.)
param: [true|false] default: false
- --print-code (Print bytecode.)
- param: [true|false] default: false
+ -pc, --print-code (Print generated bytecode. If a directory is specified, nothing will
+ be dumped to stderr. Also, in that case, .dot files will be generated
+ for all functions or for the function with the specified name only.)
+ param: [dir:<output-dir>,function:<name>]
--print-lower-ast (Print lowered abstract syntax tree.)
param: [true|false] default: false
- --print-lower-parse (Print the parse tree after lowering.)
+ -plp, --print-lower-parse (Print the parse tree after lowering.)
param: [true|false] default: false
--print-mem-usage (Print memory usage of IR after each compile stage.)
@@ -998,7 +1003,7 @@ A short summary follows:
--print-no-newline (Print function will not print new line char.)
param: [true|false] default: false
- --print-parse (Print the parse tree.)
+ -pp, --print-parse (Print the parse tree.)
param: [true|false] default: false
--print-symbols (Print the symbol table.)
@@ -1007,21 +1012,13 @@ A short summary follows:
-pcs, --profile-callsites (Dump callsite profile data.)
param: [true|false] default: false
- --range-analysis (EXPERIMENTAL: Do range analysis using known compile time types,
- and try to narrow number types)
- param: [true|false] default: false
-
-scripting (Enable scripting features.)
param: [true|false] default: false
- --specialize-calls (EXPERIMENTAL: Specialize all or a set of method according
- to callsite parameter types)
- param: [=function_1,...,function_n]
-
- --stderr (Redirect stderr to a filename or to another tty, e.g. stdout)
+ --stderr (Redirect stderr to a filename or to another tty, e.g. stdout.)
param: <output console>
- --stdout (Redirect stdout to a filename or to another tty, e.g. stderr)
+ --stdout (Redirect stdout to a filename or to another tty, e.g. stderr.)
param: <output console>
-strict (Run scripts in strict mode.)
@@ -1031,7 +1028,7 @@ A short summary follows:
param: <timezone> default: Europe/Stockholm
-tcs, --trace-callsites (Enable callsite trace mode. Options are: miss [trace callsite misses]
- enterexit [trace callsite enter/exit], objects [print object properties])
+ enterexit [trace callsite enter/exit], objects [print object properties].)
param: [=[option,]*]
--verify-code (Verify byte code before running.)
diff --git a/make/build-benchmark.xml b/make/build-benchmark.xml
index ac87fc12..ae76718f 100644
--- a/make/build-benchmark.xml
+++ b/make/build-benchmark.xml
@@ -1,381 +1,333 @@
<?xml version="1.0" encoding="UTF-8"?>
+
<!--
- Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
- DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
-
- This code is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License version 2 only, as
- published by the Free Software Foundation.
-
- This code is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- version 2 for more details (a copy is included in the LICENSE file that
- accompanied this code).
-
- You should have received a copy of the GNU General Public License version
- 2 along with this work; if not, write to the Free Software Foundation,
- Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
-
- Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- or visit www.oracle.com if you need additional information or have any
- questions.
+ Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+
+ This code is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License version 2 only, as
+ published by the Free Software Foundation.
+
+ This code is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ version 2 for more details (a copy is included in the LICENSE file that
+ accompanied this code).
+
+ You should have received a copy of the GNU General Public License version
+ 2 along with this work; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+
+ Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ or visit www.oracle.com if you need additional information or have any
+ questions.
-->
-<project name="nashorn-benchmarks" default="all" basedir="..">
- <target name="octane-init" depends="jar">
- <property name="octane-tests" value="box2d code-load crypto deltablue earley-boyer gbemu navier-stokes pdfjs raytrace regexp richards splay"/>
- </target>
-
- <!-- ignore benchmarks where rhino crashes -->
- <target name="octane-init-rhino" depends="jar">
- <property name="octane-tests" value="box2d code-load crypto deltablue earley-boyer gbemu navier-stokes raytrace regexp richards splay"/>
- </target>
+
+<project
+ name="nashorn-benchmarks"
+ default="all"
+ basedir=".."
+ xmlns:if="ant:if">
+
+ <!--
+ Below are the octane benchmarks that should be run.
+ The ones that are excluded, as Nashorn currently has
+ some issues with them (functionality or performance)
+ are commented out
+ -->
<!-- box2d -->
- <target name="octane-box2d" depends="jar">
- <antcall target="run-octane">
- <param name="octane-tests" value="box2d"/>
- </antcall>
+ <target name="octane-box2d" depends="octane-box2d-nashorn"/>
+ <target name="octane-box2d-nashorn" depends="jar">
+ <run-one cond="octane.benchmark.box2d" runtime="nashorn"/>
</target>
-
<target name="octane-box2d-v8" depends="jar">
- <antcall target="run-octane-v8">
- <param name="octane-tests" value="box2d"/>
- </antcall>
+ <run-one cond="octane.benchmark.box2d" runtime="v8"/>
</target>
-
<target name="octane-box2d-rhino" depends="jar">
- <antcall target="run-octane-rhino">
- <param name="octane-tests" value="box2d"/>
- </antcall>
+ <run-one cond="octane.benchmark.box2d" runtime="rhino"/>
</target>
-
- <!-- code-load -->
- <target name="octane-code-load" depends="jar">
- <antcall target="run-octane">
- <param name="octane-tests" value="code-load"/>
- </antcall>
+ <!-- code-load -->
+ <target name="octane-code-load" depends="octane-code-load-nashorn"/>
+ <target name="octane-code-load-nashorn" depends="jar">
+ <run-one cond="octane.benchmark.code-load" runtime="nashorn"/>
</target>
-
<target name="octane-code-load-v8" depends="jar">
- <antcall target="run-octane-v8">
- <param name="octane-tests" value="code-load"/>
- </antcall>
+ <run-one cond="octane.benchmark.code-load" runtime="v8"/>
</target>
-
<target name="octane-code-load-rhino" depends="jar">
- <antcall target="run-octane-rhino">
- <param name="octane-tests" value="code-load"/>
- </antcall>
+ <run-one cond="octane.benchmark.code-load" runtime="rhino"/>
</target>
-
<!-- crypto -->
- <target name="octane-crypto" depends="jar">
- <antcall target="run-octane">
- <param name="octane-tests" value="crypto"/>
- </antcall>
+ <target name="octane-crypto" depends="octane-crypto-nashorn"/>
+ <target name="octane-crypto-nashorn" depends="jar">
+ <run-one cond="octane.benchmark.crypto" runtime="nashorn"/>
</target>
-
<target name="octane-crypto-v8" depends="jar">
- <antcall target="run-octane-v8">
- <param name="octane-tests" value="crypto"/>
- </antcall>
+ <run-one cond="octane.benchmark.crypto" runtime="v8"/>
</target>
-
<target name="octane-crypto-rhino" depends="jar">
- <antcall target="run-octane-rhino">
- <param name="octane-tests" value="crypto"/>
- </antcall>
+ <run-one cond="octane.benchmark.crypto" runtime="rhino"/>
</target>
-
<!-- deltablue -->
- <target name="octane-deltablue" depends="jar">
- <antcall target="run-octane">
- <param name="octane-tests" value="deltablue"/>
- </antcall>
+ <target name="octane-deltablue" depends="octane-deltablue-nashorn"/>
+ <target name="octane-deltablue-nashorn" depends="jar">
+ <run-one cond="octane.benchmark.deltablue" runtime="nashorn"/>
</target>
-
<target name="octane-deltablue-v8" depends="jar">
- <antcall target="run-octane-v8">
- <param name="octane-tests" value="deltablue"/>
- </antcall>
+ <run-one cond="octane.benchmark.deltablue" runtime="v8"/>
</target>
-
<target name="octane-deltablue-rhino" depends="jar">
- <antcall target="run-octane-rhino">
- <param name="octane-tests" value="deltablue"/>
- </antcall>
+ <run-one cond="octane.benchmark.deltablue" runtime="rhino"/>
</target>
-
<!-- earley-boyer -->
- <target name="octane-earley-boyer" depends="jar">
- <antcall target="run-octane">
- <param name="octane-tests" value="earley-boyer"/>
- </antcall>
+ <target name="octane-earley-boyer" depends="octane-earley-boyer-nashorn"/>
+ <target name="octane-earley-boyer-nashorn" depends="jar">
+ <run-one cond="octane.benchmark.earley-boyer" runtime="nashorn"/>
</target>
-
<target name="octane-earley-boyer-v8" depends="jar">
- <antcall target="run-octane-v8">
- <param name="octane-tests" value="earley-boyer"/>
- </antcall>
+ <run-one cond="octane.benchmark.earley-boyer" runtime="v8"/>
</target>
-
<target name="octane-earley-boyer-rhino" depends="jar">
- <antcall target="run-octane-rhino">
- <param name="octane-tests" value="earley-boyer"/>
- </antcall>
+ <run-one cond="octane.benchmark.earley-boyer" runtime="rhino"/>
</target>
-
-
- <!-- gbemu -->
- <target name="octane-gbemu" depends="jar">
- <antcall target="run-octane">
- <param name="octane-tests" value="gbemu"/>
- </antcall>
+
+ <!-- gbemu -->
+ <target name="octane-gbemu" depends="octane-gbemu-nashorn"/>
+ <target name="octane-gbemu-nashorn" depends="jar">
+ <run-one cond="octane.benchmark.gbemu" runtime="nashorn"/>
</target>
-
<target name="octane-gbemu-v8" depends="jar">
- <antcall target="run-octane-v8">
- <param name="octane-tests" value="gbemu"/>
- </antcall>
+ <run-one cond="octane.benchmark.gbemu" runtime="v8"/>
</target>
-
<target name="octane-gbemu-rhino" depends="jar">
- <antcall target="run-octane-rhino">
- <param name="octane-tests" value="gbemu"/>
- </antcall>
+ <run-one cond="octane.benchmark.gbemu" runtime="rhino"/>
</target>
-
- <!-- mandreel -->
- <target name="octane-mandreel" depends="jar">
- <antcall target="run-octane">
- <param name="octane-tests" value="mandreel"/>
- </antcall>
+ <!-- mandreel -->
+ <target name="octane-mandreel" depends="octane-mandreel-nashorn"/>
+ <target name="octane-mandreel-nashorn" depends="jar">
+ <run-one cond="octane.benchmark.mandreel" runtime="nashorn"/>
</target>
-
<target name="octane-mandreel-v8" depends="jar">
- <antcall target="run-octane-v8">
- <param name="octane-tests" value="mandreel"/>
- </antcall>
+ <run-one cond="octane.benchmark.mandreel" runtime="v8"/>
</target>
-
<target name="octane-mandreel-rhino" depends="jar">
- <antcall target="run-octane-rhino">
- <param name="octane-tests" value="mandreel"/>
- </antcall>
+ <run-one cond="octane.benchmark.mandreel" runtime="rhino"/>
</target>
-
<!-- navier-stokes -->
- <target name="octane-navier-stokes" depends="jar">
- <antcall target="run-octane">
- <param name="octane-tests" value="navier-stokes"/>
- </antcall>
+ <target name="octane-navier-stokes" depends="octane-navier-stokes-nashorn"/>
+ <target name="octane-navier-stokes-nashorn" depends="jar">
+ <run-one cond="octane.benchmark.navier-stokes" runtime="nashorn"/>
</target>
-
<target name="octane-navier-stokes-v8" depends="jar">
- <antcall target="run-octane-v8">
- <param name="octane-tests" value="navier-stokes"/>
- </antcall>
+ <run-one cond="octane.benchmark.navier-stokes" runtime="v8"/>
</target>
-
<target name="octane-navier-stokes-rhino" depends="jar">
- <antcall target="run-octane-rhino">
- <param name="octane-tests" value="navier-stokes"/>
- </antcall>
+ <run-one cond="octane.benchmark.navier-stokes" runtime="rhino"/>
</target>
-
- <!-- pdfjs -->
- <target name="octane-pdfjs" depends="jar">
- <antcall target="run-octane">
- <param name="octane-tests" value="pdfjs"/>
- </antcall>
+ <!-- pdfjs -->
+ <target name="octane-pdfjs" depends="octane-pdfjs-nashorn"/>
+ <target name="octane-pdfjs-nashorn" depends="jar">
+ <run-one cond="octane.benchmark.pdfjs" runtime="nashorn"/>
</target>
-
<target name="octane-pdfjs-v8" depends="jar">
- <antcall target="run-octane-v8">
- <param name="octane-tests" value="pdfjs"/>
- </antcall>
+ <run-one cond="octane.benchmark.pdfjs" runtime="v8"/>
</target>
-
<target name="octane-pdfjs-rhino" depends="jar">
- <antcall target="run-octane-rhino">
- <param name="octane-tests" value="pdfjs"/>
- </antcall>
+ <run-one cond="octane.benchmark.pdfjs" runtime="rhino"/>
</target>
-
<!-- raytrace -->
- <target name="octane-raytrace" depends="jar">
- <antcall target="run-octane">
- <param name="octane-tests" value="raytrace"/>
- </antcall>
+ <target name="octane-raytrace" depends="octane-raytrace-nashorn"/>
+ <target name="octane-raytrace-nashorn" depends="jar">
+ <run-one cond="octane.benchmark.raytrace" runtime="nashorn"/>
</target>
-
<target name="octane-raytrace-v8" depends="jar">
- <antcall target="run-octane-v8">
- <param name="octane-tests" value="raytrace"/>
- </antcall>
+ <run-one cond="octane.benchmark.raytrace" runtime="v8"/>
</target>
-
<target name="octane-raytrace-rhino" depends="jar">
- <antcall target="run-octane-rhino">
- <param name="octane-tests" value="raytrace"/>
- </antcall>
+ <run-one cond="octane.benchmark.raytrace" runtime="rhino"/>
</target>
-
<!-- regexp -->
- <target name="octane-regexp" depends="jar">
- <antcall target="run-octane">
- <param name="octane-tests" value="regexp"/>
- </antcall>
+ <target name="octane-regexp" depends="octane-regexp-nashorn"/>
+ <target name="octane-regexp-nashorn" depends="jar">
+ <run-one cond="octane.benchmark.regexp" runtime="nashorn"/>
</target>
-
<target name="octane-regexp-v8" depends="jar">
- <antcall target="run-octane-v8">
- <param name="octane-tests" value="regexp"/>
- </antcall>
+ <run-one cond="octane.benchmark.regexp" runtime="v8"/>
</target>
-
<target name="octane-regexp-rhino" depends="jar">
- <antcall target="run-octane-rhino">
- <param name="octane-tests" value="regexp"/>
- </antcall>
+ <run-one cond="octane.benchmark.regexp" runtime="rhino"/>
</target>
-
<!-- richards -->
- <target name="octane-richards" depends="jar">
- <antcall target="run-octane">
- <param name="octane-tests" value="richards"/>
- </antcall>
+ <target name="octane-richards" depends="octane-richards-nashorn"/>
+ <target name="octane-richards-nashorn" depends="jar">
+ <run-one cond="octane.benchmark.richards" runtime="nashorn"/>
</target>
-
<target name="octane-richards-v8" depends="jar">
- <antcall target="run-octane-v8">
- <param name="octane-tests" value="richards"/>
- </antcall>
+ <run-one cond="octane.benchmark.richards" runtime="v8"/>
</target>
-
<target name="octane-richards-rhino" depends="jar">
- <antcall target="run-octane-rhino">
- <param name="octane-tests" value="richards"/>
- </antcall>
+ <run-one cond="octane.benchmark.richards" runtime="rhino"/>
</target>
-
<!-- splay -->
- <target name="octane-splay" depends="jar">
- <antcall target="run-octane">
- <param name="octane-tests" value="splay"/>
- </antcall>
+ <target name="octane-splay" depends="octane-splay-nashorn"/>
+ <target name="octane-splay-nashorn" depends="jar">
+ <run-one cond="octane.benchmark.splay" runtime="nashorn"/>
</target>
-
<target name="octane-splay-v8" depends="jar">
- <antcall target="run-octane-v8">
- <param name="octane-tests" value="splay"/>
- </antcall>
+ <run-one cond="octane.benchmark.splay" runtime="v8"/>
</target>
-
<target name="octane-splay-rhino" depends="jar">
- <antcall target="run-octane-rhino">
- <param name="octane-tests" value="splay"/>
- </antcall>
+ <run-one cond="octane.benchmark.splay" runtime="rhino"/>
</target>
- <!-- splay -->
- <target name="octane-typescript" depends="jar">
- <antcall target="run-octane">
- <param name="octane-tests" value="typescript"/>
- </antcall>
+ <!-- typescript -->
+ <target name="octane-typescript" depends="octane-typescript-nashorn"/>
+ <target name="octane-typescript-nashorn" depends="jar">
+ <run-one cond="octane.benchmark.typescript" runtime="nashorn"/>
</target>
-
<target name="octane-typescript-v8" depends="jar">
- <antcall target="run-octane-v8">
- <param name="octane-typescript" value="typescript"/>
- </antcall>
+ <run-one cond="octane.benchmark.typescript" runtime="v8"/>
</target>
-
<target name="octane-typescript-rhino" depends="jar">
- <antcall target="run-octane-rhino">
- <param name="octane-tests" value="typescript"/>
- </antcall>
+ <run-one cond="octane.benchmark.typescript" runtime="rhino"/>
</target>
<!-- zlib -->
- <target name="octane-zlib" depends="jar">
- <antcall target="run-octane">
- <param name="octane-tests" value="zlib"/>
- </antcall>
+ <target name="octane-zlib" depends="octane-zlib-nashorn"/>
+ <target name="octane-zlib-nashorn" depends="jar">
+ <run-one cond="octane.benchmark.zlib" runtime="nashorn"/>
</target>
-
<target name="octane-zlib-v8" depends="jar">
- <antcall target="run-octane-v8">
- <param name="octane-typescript" value="zlib"/>
- </antcall>
+ <run-one cond="octane.benchmark.zlib" runtime="v8"/>
</target>
-
<target name="octane-zlib-rhino" depends="jar">
- <antcall target="run-octane-rhino">
- <param name="octane-tests" value="zlib"/>
- </antcall>
- </target>
-
- <!-- run octane benchmarks in a single process -->
- <target name="octane-single-process" depends="octane-init">
- <antcall target="run-octane"/>
- </target>
-
- <!-- zlib excluded due to missing implementation of 'read' -->
- <target name="octane-separate-process" depends=
- "octane-box2d, octane-code-load, octane-crypto,
- octane-deltablue, octane-earley-boyer, octane-gbemu,
- octane-mandreel, octane-navier-stokes, octane-pdfjs,
- octane-raytrace, octane-regexp, octane-richards,
- octane-splay, octane-typescript"/>
-
- <target name="--single-process" unless="${octane-test-sys-prop.separate.process}">
- <antcall target="octane-single-process"/>
- </target>
- <target name="--separate-process" if="${octane-test-sys-prop.separate.process}">
- <antcall target="octane-separate-process"/>
- </target>
-
- <!-- run 'octane' in single or separate processes based on config -->
- <target name="octane" depends="init, --single-process, --separate-process"/>
+ <run-one cond="octane.benchmark.zlib" runtime="rhino"/>
+ </target>
+
+ <!--
+ Benchmark runners for one or more benchmarks, single
+ or multiple process
+ -->
+
+ <target name="octane-process-separate" if="${octane-test-sys-prop.separate.process}">
+ <echo message="Running each benchmark in separate processes, starting new JVMs for each."/>
+ <script language="javascript"><![CDATA[
+ var props = [];
+
+ for (var prop in project.getProperties()) {
+ if (prop.startsWith("octane.benchmark.")) {
+ props.push(prop);
+ }
+ }
+
+ //sort benchmark props in alphabetical order by name
+ props.sort(function(a, b) {
+ if (a < b) {
+ return -1;
+ } else if (a > b) {
+ return 1;
+ } else {
+ return 0;
+ }
+ });
+
+ var runtime = project.getProperty("runtime");
+
+ for (var i in props) {
+ var task = project.createTask("run-one");
+ // workaround for https://issues.apache.org/bugzilla/show_bug.cgi?id=53831, still not fixed
+ if (task.getOwningTarget() == null) {
+ task.setOwningTarget(self.getOwningTarget());
+ }
+ var prop = props[i];
+ task.setDynamicAttribute("cond", prop);
+ task.setDynamicAttribute("runtime", runtime);
+ task.perform();
+ }
+ ]]></script>
+ </target>
+
+ <target name="octane-process-single" unless="${octane-test-sys-prop.separate.process}">
+ <echo message="Running all benchmarks in the same process."/>
+ <pathconvert property="octane.benchmarks" pathsep=" ">
+ <propertyset>
+ <propertyref prefix="octane.benchmark."/>
+ </propertyset>
+ </pathconvert>
+ <antcall target="run-octane${runtime}">
+ <param name="octane-tests" value="${octane.benchmarks}"/>
+ </antcall>
+ </target>
+
+ <!--
+ run 'octane' in single or separate processes based on config
+ This uses nashorn as the default runtime
+ -->
+ <target name="octane-nashorn" depends="jar">
+ <property name="runtime" value="nashorn"/>
+ <antcall target="octane-process-separate"/>
+ <antcall target="octane-process-single"/>
+ </target>
+
+ <!-- alias for 'octane' -->
+ <target name="octane" depends="octane-nashorn"/>
<!-- run octane benchmarks using octane as runtime -->
- <target name="octane-v8" depends="octane-init">
- <antcall target="run-octane-v8"/>
+ <target name="octane-v8" depends="jar">
+ <property name="runtime" value="v8"/>
+ <antcall target="octane-process-separate"/>
+ <antcall target="octane-process-single"/>
</target>
<!-- run octane benchmarks using Rhino as runtime -->
- <target name="octane-rhino" depends="octane-init-rhino">
- <antcall target="run-octane-rhino"/>
- </target>
-
- <target name="run-octane">
+ <target name="octane-rhino" depends="jar">
+ <property name="runtime" value="rhino"/>
+ <antcall target="octane-process-separate"/>
+ <antcall target="octane-process-single"/>
+ </target>
+
+ <macrodef name="run-one">
+ <attribute name="cond"/>
+ <attribute name="runtime" default=""/>
+ <sequential>
+ <antcall target="run-octane-@{runtime}" if:set="@{cond}">
+ <param name="octane-tests" value="${@{cond}}"/>
+ </antcall>
+ </sequential>
+ </macrodef>
+
+ <target name="run-octane-nashorn">
<java classname="${nashorn.shell.tool}"
classpath="${run.test.classpath}"
fork="true"
dir=".">
<jvmarg line="${ext.class.path}"/>
<jvmarg line="${run.test.jvmargs.octane} -Xms${run.test.xms} -Xmx${run.test.xmx}"/>
+ <!-- pass on all properties prefixed with 'nashorn' to the runtime -->
+ <syspropertyset>
+ <propertyref prefix="nashorn."/>
+ </syspropertyset>
<arg value="${octane-test-sys-prop.test.js.framework}"/>
+ <arg value="-scripting"/>
<arg value="--"/>
<arg value="${octane-tests}"/>
<arg value="--runtime"/>
- <arg value="Nashorn"/>
+ <arg value="nashorn"/>
<arg value="--verbose"/>
- <arg value="--iterations 8"/>
+ <arg value="--iterations ${octane.iterations}"/>
</java>
</target>
@@ -383,11 +335,11 @@
<exec executable="${v8.shell}">
<arg value="${octane-test-sys-prop.test.js.framework}"/>
<arg value="--"/>
- <arg value="${octane-tests}"/>
+ <arg value="${octane-tests}"/>
<arg value="--runtime"/>
<arg value="v8"/>
<arg value="--verbose"/>
- <arg value="--iterations 8"/>
+ <arg value="--iterations ${octane.iterations}"/>
</exec>
</target>
@@ -397,12 +349,14 @@
fork="true"
dir=".">
<jvmarg line="${run.test.jvmargs.octane} -Xms${run.test.xms} -Xmx${run.test.xmx}"/>
+ <arg value="-opt"/>
+ <arg value="9"/>
<arg value="${octane-test-sys-prop.test.js.framework}"/>
<arg value="${octane-tests}"/>
<arg value="--runtime"/>
- <arg value="Rhino"/>
+ <arg value="rhino"/>
<arg value="--verbose"/>
- <arg value="--iterations 8"/>
+ <arg value="--iterations ${octane.iterations}"/>
</java>
</target>
@@ -413,18 +367,22 @@
<arg value="${octane-tests}/"/>
</exec>
</target>
-
+
<target name="sunspider-init" depends="jar">
<fileset id="sunspider-set"
- dir="${sunspider-test-sys-prop.test.js.roots}"
- excludes="${sunspider-test-sys-prop.test.js.exclude.list}">
+ dir="${sunspider-test-sys-prop.test.js.roots}"
+ excludes="${sunspider-test-sys-prop.test.js.exclude.list}">
<include name="**/*.js"/>
</fileset>
<pathconvert pathsep=" " property="sunspider-tests" refid="sunspider-set"/>
</target>
+ <!--- SUNSPIDER JOB BELOW -->
+
<!-- run sunspider with Nashorn -->
- <target name="sunspider" depends="sunspider-init">
+ <target name="sunspider" depends="sunspider-nashorn"/>
+
+ <target name="sunspider-nashorn" depends="sunspider-init">
<java classname="${nashorn.shell.tool}"
classpath="${run.test.classpath}"
fork="true"
@@ -436,6 +394,9 @@
<arg value="${sunspider-test-sys-prop.test.js.framework}"/>
<arg value="--"/>
<arg value="${sunspider-tests}/"/>
+ <arg value="--verbose"/>
+ <arg value="--times"/>
+ <arg value="${sunspider.iterations}"/>
</java>
</target>
@@ -445,6 +406,9 @@
<arg value="${sunspider-test-sys-prop.test.js.framework}"/>
<arg value="--"/>
<arg value="${sunspider-tests}/"/>
+ <arg value="--verbose"/>
+ <arg value="--times"/>
+ <arg value="${sunspider.iterations}"/>
</exec>
</target>
@@ -455,8 +419,13 @@
fork="true"
dir=".">
<jvmarg line="${run.test.jvmargs} -Xmx${run.test.xmx}"/>
+ <arg value="-opt"/>
+ <arg value="9"/>
<arg value="${sunspider-test-sys-prop.test.js.framework}"/>
<arg value="${sunspider-tests}/"/>
+ <arg value="--verbose"/>
+ <arg value="--times"/>
+ <arg value="${sunspider.iterations}"/>
</java>
</target>
diff --git a/make/build-nasgen.xml b/make/build-nasgen.xml
index 9dca5505..16094cc9 100644
--- a/make/build-nasgen.xml
+++ b/make/build-nasgen.xml
@@ -36,11 +36,13 @@
<pathelement location="${basedir}/jcov2/lib/jcov_j2se_rt.jar"/>
<pathelement location="${basedir}/buildtools/nasgen/dist/nasgen.jar"/>
<pathelement path="${basedir}/build/classes"/>
+ <pathelement location="${dist.dir}/nasgen.jar"/>
+ <pathelement path="${build.dir}/classes"/>
</classpath>
<jvmarg value="-Djava.ext.dirs="/>
- <arg value="${basedir}/build/classes"/>
+ <arg value="${build.dir}/classes"/>
<arg value="jdk.nashorn.internal.objects"/>
- <arg value="${basedir}/build/classes"/>
+ <arg value="${build.dir}/classes"/>
</java>
</target>
diff --git a/make/build.xml b/make/build.xml
index 3b30a089..92d6f5dd 100644
--- a/make/build.xml
+++ b/make/build.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
+
<!--
Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -21,12 +22,11 @@
or visit www.oracle.com if you need additional information or have any
questions.
-->
+
<project name="nashorn" default="test" basedir="..">
<import file="build-nasgen.xml"/>
- <import file="build-benchmark.xml"/>
<import file="code_coverage.xml"/>
-
<target name="init-conditions">
<!-- loading locally defined resources and properties. NB they owerwrite default ones defined later -->
<property file="${user.home}/.nashorn.project.local.properties"/>
@@ -51,7 +51,7 @@
<available property="testng.available" file="${file.reference.testng.jar}"/>
<!-- check if Jemmy ang testng.jar are avaiable -->
<condition property="jemmy.jfx.testng.available" value="true">
- <and>
+ <and>
<available file="${file.reference.jemmyfx.jar}"/>
<available file="${file.reference.jemmycore.jar}"/>
<available file="${file.reference.jemmyawtinput.jar}"/>
@@ -69,13 +69,16 @@
<condition property="exclude.list" value="./exclude/exclude_list_cc.txt" else="./exclude/exclude_list.txt">
<istrue value="${make.code.coverage}" />
</condition>
+
+ <condition property="jfr.options" value="${run.test.jvmargs.jfr}" else="">
+ <istrue value="${jfr}"/>
+ </condition>
</target>
<target name="init" depends="init-conditions, init-cc">
-
<!-- extends jvm args -->
- <property name="run.test.jvmargs" value="${run.test.jvmargs.main} ${run.test.cc.jvmargs}"/>
- <property name="run.test.jvmargs.octane" value="${run.test.jvmargs.octane.main} ${run.test.cc.jvmargs}" />
+ <property name="run.test.jvmargs" value="${run.test.jvmargs.main} ${run.test.cc.jvmargs} ${jfr.options}"/>
+ <property name="run.test.jvmargs.octane" value="${run.test.jvmargs.octane.main} ${run.test.cc.jvmargs} ${jfr.options}"/>
<echo message="run.test.jvmargs=${run.test.jvmargs}"/>
<echo message="run.test.jvmargs.octane=${run.test.jvmargs.octane}"/>
@@ -122,8 +125,7 @@
encoding="${javac.encoding}"
includeantruntime="false" fork="true">
<compilerarg value="-J-Djava.ext.dirs="/>
- <compilerarg value="-Xlint:unchecked"/>
- <compilerarg value="-Xlint:deprecation"/>
+ <compilerarg value="-Xlint:all"/>
<compilerarg value="-XDignore.symbol.file"/>
<compilerarg value="-Xdiags:verbose"/>
</javac>
@@ -291,6 +293,10 @@
<target name="generate-policy-file" depends="prepare">
<echo file="${build.dir}/nashorn.policy">
+grant codeBase "file:/${toString:nashorn.ext.path}/nashorn.jar" {
+ permission java.security.AllPermission;
+};
+
grant codeBase "file:/${basedir}/${nashorn.internal.tests.jar}" {
permission java.security.AllPermission;
};
@@ -298,6 +304,14 @@ grant codeBase "file:/${basedir}/${nashorn.internal.tests.jar}" {
grant codeBase "file:/${basedir}/${file.reference.testng.jar}" {
permission java.security.AllPermission;
};
+//// in case of absolute path:
+grant codeBase "file:/${nashorn.internal.tests.jar}" {
+ permission java.security.AllPermission;
+};
+
+grant codeBase "file:/${file.reference.testng.jar}" {
+ permission java.security.AllPermission;
+};
grant codeBase "file:/${basedir}/test/script/trusted/*" {
permission java.security.AllPermission;
@@ -330,6 +344,10 @@ grant codeBase "file:/${basedir}/test/script/basic/classloader.js" {
permission java.lang.RuntimePermission "nashorn.JavaReflection";
};
+grant codeBase "file:/${basedir}/test/script/markdown.js" {
+ permission java.io.FilePermission "${basedir}/test/script/external/showdown/-", "read";
+};
+
</echo>
<replace file="${build.dir}/nashorn.policy"><replacetoken>\</replacetoken><replacevalue>/</replacevalue></replace> <!--hack for Windows - to make URLs with normal path separators -->
@@ -345,6 +363,7 @@ grant codeBase "file:/${basedir}/test/script/basic/classloader.js" {
<available file="${test.external.dir}/yui" property="test-sys-prop.external.yui"/>
<available file="${test.external.dir}/jquery" property="test-sys-prop.external.jquery"/>
<available file="${test.external.dir}/test262" property="test-sys-prop.external.test262"/>
+ <available file="${test.external.dir}/showdown" property="test-sys-prop.external.markdown"/>
</target>
<target name="check-testng" unless="testng.available">
@@ -380,7 +399,8 @@ grant codeBase "file:/${basedir}/test/script/basic/classloader.js" {
<testng outputdir="${build.nosecurity.test.results.dir}" classfilesetref="test.nosecurity.classes"
verbose="${testng.verbose}" haltonfailure="true" useDefaultListeners="false" listeners="${testng.listeners}" workingDir="${basedir}">
<jvmarg line="${ext.class.path}"/>
- <jvmarg line="${run.test.jvmargs} -Xmx${run.test.xmx}"/>
+ <jvmarg line="${run.test.jvmargs} -Xmx${run.test.xmx} -Dbuild.dir=${build.dir}"/>
+ <sysproperty key="nashorn.jar" value="${dist.dir}/nashorn.jar"/>
<propertyset>
<propertyref prefix="nashorn."/>
</propertyset>
@@ -401,9 +421,12 @@ grant codeBase "file:/${basedir}/test/script/basic/classloader.js" {
<testng outputdir="${build.test.results.dir}" classfilesetref="test.classes"
verbose="${testng.verbose}" haltonfailure="true" useDefaultListeners="false" listeners="${testng.listeners}" workingDir="${basedir}">
<jvmarg line="${ext.class.path}"/>
- <jvmarg line="${run.test.jvmargs} -Xmx${run.test.xmx} ${run.test.jvmsecurityargs}"/>
+ <jvmarg line="${run.test.jvmargs} -Xmx${run.test.xmx} ${run.test.jvmsecurityargs} -Dbuild.dir=${build.dir}"/>
<jvmarg line="${debug.test.jvmargs}"/>
<propertyset>
+ <propertyref prefix="nashorn."/>
+ </propertyset>
+ <propertyset>
<propertyref prefix="test-sys-prop."/>
<mapper from="test-sys-prop.*" to="*" type="glob"/>
</propertyset>
@@ -416,18 +439,6 @@ grant codeBase "file:/${basedir}/test/script/basic/classloader.js" {
<target name="test" depends="jar, -test-classes-all,-test-classes-single, check-testng, check-external-tests, compile-test, generate-policy-file, -test-security, -test-nosecurity" if="testng.available"/>
- <target name="test-basicparallel" depends="jar, check-testng, check-external-tests, compile-test, generate-policy-file">
- <!-- use just build.test.classes.dir to avoid referring to TestNG -->
- <java classname="${parallel.test.runner}" dir="${basedir}" classpath="${build.test.classes.dir}" failonerror="true" fork="true">
- <jvmarg line="${ext.class.path}"/>
- <jvmarg line="${run.test.jvmargs} -Xmx${run.test.xmx} ${run.test.jvmsecurityargs}"/>
- <syspropertyset>
- <propertyref prefix="test-sys-prop."/>
- <mapper type="glob" from="test-sys-prop.*" to="*"/>
- </syspropertyset>
- </java>
- </target>
-
<target name="check-jemmy.jfx.testng" unless="jemmy.jfx.testng.available">
<echo message="WARNING: Jemmy or JavaFX or TestNG not available, will not run tests. Please copy testng.jar, JemmyCore.jar, JemmyFX.jar, JemmyAWTInput.jar under test${file.separator}lib directory. And make sure you have jfxrt.jar in ${java.home}${file.separator}lib${file.separator}ext dir."/>
</target>
@@ -436,19 +447,19 @@ grant codeBase "file:/${basedir}/test/script/basic/classloader.js" {
<fileset id="test.classes" dir="${build.test.classes.dir}">
<include name="**/framework/*Test.class"/>
</fileset>
-
+
<copy file="${file.reference.jfxrt.jar}" todir="dist"/>
-
+
<condition property="jfx.prism.order" value="-Dprism.order=j2d" else=" ">
- <not>
+ <not>
<os family="mac"/>
</not>
- </condition>
-
+ </condition>
+
<testng outputdir="${build.test.results.dir}" classfilesetref="test.classes"
verbose="${testng.verbose}" haltonfailure="true" useDefaultListeners="false" listeners="${testng.listeners}" workingDir="${basedir}">
<jvmarg line="${ext.class.path}"/>
- <jvmarg line="${run.test.jvmargs} -Xmx${run.test.xmx}"/>
+ <jvmarg line="${run.test.jvmargs} -Xmx${run.test.xmx} -Dbuild.dir=${build.dir}"/>
<propertyset>
<propertyref prefix="testjfx-test-sys-prop."/>
<mapper from="testjfx-test-sys-prop.*" to="*" type="glob"/>
@@ -459,7 +470,26 @@ grant codeBase "file:/${basedir}/test/script/basic/classloader.js" {
</classpath>
</testng>
</target>
-
+
+ <target name="testmarkdown" depends="jar, check-testng, check-external-tests, compile-test, generate-policy-file" if="testng.available">
+ <fileset id="test.classes" dir="${build.test.classes.dir}">
+ <include name="**/framework/*Test.class"/>
+ </fileset>
+
+ <testng outputdir="${build.test.results.dir}" classfilesetref="test.classes"
+ verbose="${testng.verbose}" haltonfailure="true" useDefaultListeners="false" listeners="${testng.listeners}" workingDir="${basedir}">
+ <jvmarg line="${ext.class.path}"/>
+ <jvmarg line="${run.test.jvmargs} -Xmx${run.test.xmx} ${run.test.jvmsecurityargs} -Dbuild.dir=${build.dir}"/>
+ <propertyset>
+ <propertyref prefix="testmarkdown-test-sys-prop."/>
+ <mapper from="testmarkdown-test-sys-prop.*" to="*" type="glob"/>
+ </propertyset>
+ <classpath>
+ <pathelement path="${run.test.classpath}"/>
+ </classpath>
+ </testng>
+ </target>
+
<target name="test262" depends="jar, check-testng, check-external-tests, compile-test, generate-policy-file" if="testng.available">
<fileset id="test.classes" dir="${build.test.classes.dir}">
<include name="**/framework/*Test.class"/>
@@ -468,7 +498,10 @@ grant codeBase "file:/${basedir}/test/script/basic/classloader.js" {
<testng outputdir="${build.test.results.dir}" classfilesetref="test.classes"
verbose="${testng.verbose}" haltonfailure="true" useDefaultListeners="false" listeners="${testng.listeners}" workingDir="${basedir}">
<jvmarg line="${ext.class.path}"/>
- <jvmarg line="${run.test.jvmargs} -Xmx${run.test.xmx} ${run.test.jvmsecurityargs}"/>
+ <jvmarg line="${run.test.jvmargs} -Xmx${run.test.xmx} ${run.test.jvmsecurityargs} -Dbuild.dir=${build.dir}"/>
+ <propertyset>
+ <propertyref prefix="nashorn."/>
+ </propertyset>
<propertyset>
<propertyref prefix="test262-test-sys-prop."/>
<mapper from="test262-test-sys-prop.*" to="*" type="glob"/>
@@ -485,7 +518,9 @@ grant codeBase "file:/${basedir}/test/script/basic/classloader.js" {
<!-- use just build.test.classes.dir to avoid referring to TestNG -->
<java classname="${parallel.test.runner}" dir="${basedir}" fork="true">
<jvmarg line="${ext.class.path}"/>
- <jvmarg line="${run.test.jvmargs} -Xmx${run.test.xmx} ${run.test.jvmsecurityargs}"/>
+ <jvmarg line="${run.test.jvmargs} -Xmx${run.test.xmx} ${run.test.jvmsecurityargs} -Dbuild.dir=${build.dir}"/>
+ <!-- avoid too many typeinfo cache files. Each script is run only once anyway -->
+ <jvmarg line="-Dnashorn.typeInfo.disabled=true"/>
<classpath>
<pathelement path="${run.test.classpath}"/>
</classpath>
@@ -496,6 +531,26 @@ grant codeBase "file:/${basedir}/test/script/basic/classloader.js" {
</java>
</target>
+ <target name="testparallel" depends="test-parallel"/>
+
+ <target name="test-parallel" depends="jar, check-testng, check-external-tests, compile-test, generate-policy-file" if="testng.available">
+ <!-- use just build.test.classes.dir to avoid referring to TestNG -->
+ <java classname="${parallel.test.runner}" dir="${basedir}"
+ failonerror="true"
+ fork="true">
+ <jvmarg line="${ext.class.path}"/>
+ <jvmarg line="${run.test.jvmargs} -Xmx${run.test.xmx} ${run.test.jvmsecurityargs}"/>
+ <classpath>
+ <pathelement path="${run.test.classpath}"/>
+ <pathelement path="${build.test.classes.dir}"/>
+ </classpath>
+ <syspropertyset>
+ <propertyref prefix="test-sys-prop."/>
+ <mapper type="glob" from="test-sys-prop.*" to="*"/>
+ </syspropertyset>
+ </java>
+ </target>
+
<target name="all" depends="test, docs"
description="Build, test and generate docs for nashorn"/>
@@ -529,6 +584,8 @@ grant codeBase "file:/${basedir}/test/script/basic/classloader.js" {
<!-- clone test262 git repo -->
<exec executable="${git.executable}">
<arg value="clone"/>
+ <arg value="--branch"/>
+ <arg value="es5-tests"/>
<arg value="https://github.com/tc39/test262"/>
<arg value="${test.external.dir}/test262"/>
</exec>
@@ -604,6 +661,11 @@ grant codeBase "file:/${basedir}/test/script/basic/classloader.js" {
<get src="http://yui.yahooapis.com/3.5.1/build/yui/yui.js" dest="${test.external.dir}/yui" skipexisting="true" ignoreerrors="true"/>
<get src="http://yui.yahooapis.com/3.5.1/build/yui/yui-min.js" dest="${test.external.dir}/yui" skipexisting="true" ignoreerrors="true"/>
+ <!-- showdown -->
+ <mkdir dir="${test.external.dir}/showdown"/>
+ <get src="https://raw.github.com/coreyti/showdown/master/src/showdown.js" dest="${test.external.dir}/showdown" skipexisting="true" ignoreerrors="true"/>
+ <get src="https://raw.github.com/coreyti/showdown/master/src/extensions/table.js" dest="${test.external.dir}/showdown" skipexisting="true" ignoreerrors="true"/>
+
</target>
<!-- update external test suites that are pulled from source control systems -->
@@ -619,4 +681,6 @@ grant codeBase "file:/${basedir}/test/script/basic/classloader.js" {
<target name="alltests" depends="exit-if-no-testng, externals, update-externals, test, test262parallel, perf"/>
+ <import file="build-benchmark.xml"/>
+
</project>
diff --git a/make/nbproject/ide-targets.xml b/make/nbproject/ide-targets.xml
index 70b3e68f..d1e8135f 100644
--- a/make/nbproject/ide-targets.xml
+++ b/make/nbproject/ide-targets.xml
@@ -31,9 +31,10 @@
<classpath path="${run.test.classpath}"/>
</nbjpdastart>
<java classname="jdk.nashorn.tools.Shell" classpath="${run.test.classpath}" dir="samples" fork="true">
+ <jvmarg line="-Dnashorn.optimistic"/>
<jvmarg line="${ext.class.path}"/>
<jvmarg line="${run.test.jvmargs}"/>
- <arg value="test.js"/>
+ <arg value="../samples/test.js"/>
<jvmarg value="-Xdebug"/>
<jvmarg value="-Xrunjdwp:transport=dt_socket,address=${jpda.address}"/>
</java>
diff --git a/make/project.properties b/make/project.properties
index 2abccdeb..800a6295 100644
--- a/make/project.properties
+++ b/make/project.properties
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@@ -53,6 +53,7 @@ parallel.test.runner=jdk.nashorn.internal.test.framework.ParallelTestRunner
# test classes directory
build.test.classes.dir=${build.dir}/test/classes
+
# nashorn test jar - internal tests jar and api tests jar
nashorn.internal.tests.jar=${build.dir}/nashorn-internal-tests.jar
nashorn.api.tests.jar=${build.dir}/nashorn-api-tests.jar
@@ -60,6 +61,7 @@ nashorn.api.tests.jar=${build.dir}/nashorn-api-tests.jar
# test results directory
build.test.results.dir=${build.dir}/test/reports
build.nosecurity.test.results.dir=${build.dir}/test/nosecurity/reports
+build.nooptimistic.test.results.dir=${build.dir}/test/nooptimistic/reports
# This directory is removed when the project is cleaned:
dist.dir=dist
@@ -72,6 +74,9 @@ fxshell.classes.dir = ${build.dir}/fxshell/classes
fxshell.dir = tools/fxshell
fxshell.jar = ${dist.dir}/nashornfx.jar
+# configuration for java flight recorder
+run.test.jvmargs.jfr=-XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:FlightRecorderOptions=defaultrecording=true,disk=true,dumponexit=true,dumponexitpath=${build.dir},stackdepth=128
+
# jars refererred
file.reference.testng.jar=test/lib/testng.jar
@@ -122,6 +127,7 @@ test.external.dir=test/script/external
test262.dir=${test.external.dir}/test262
test262.suite.dir=${test262.dir}/test/suite
testjfx.dir=${test.script.dir}/jfx
+testmarkdown.dir=${test.script.dir}/markdown
test-sys-prop.test.dir=${test.dir}
test-sys-prop.test.js.roots=${test.basic.dir} ${test.maptests.dir} ${test.error.dir} ${test.sandbox.dir} ${test.trusted.dir}
@@ -163,21 +169,14 @@ test-sys-prop.test.js.unchecked.dir=${test262.dir}
# test root for octane
octane-test-sys-prop.test.js.roots=${test.external.dir}/octane/
-# run octane benchmars in separate processes?
+# run octane benchmars in separate processes? (recommended)
octane-test-sys-prop.separate.process=true
# framework root for octane
octane-test-sys-prop.test.js.framework=${test.basic.dir}/run-octane.js
-# list of tests to be excluded
-# mandreel excluded due to OOM
-octane-test-sys-prop.test.js.exclude.list=\
- base.js \
- run.js \
- mandreel.js
-
# test root for sunspider
-sunspider-test-sys-prop.test.js.roots=${test.external.dir}/sunspider/tests/sunspider-1.0/
+sunspider-test-sys-prop.test.js.roots=${test.external.dir}/sunspider/tests/sunspider-1.0.2/
# framework root for sunspider
sunspider-test-sys-prop.test.js.framework=${test.basic.dir}/runsunspider.js
@@ -193,6 +192,7 @@ test262-test-sys-prop.test.js.shared.context=true
# test262 test root
test262-test-sys-prop.test.js.roots=${test262.suite.dir}
+
# test262 enable/disable strict mode tests
test262-test-sys-prop.test.js.enable.strict.mode=true
@@ -202,7 +202,7 @@ test262-test-sys-prop.test.js.enable.strict.mode=true
# list of test262 test dirs to be excluded
test262-test-sys-prop.test.js.exclude.dir=\
${test262.suite.dir}/intl402/ \
- ${test262.suite.dir}/bestPractice/
+ ${test262.suite.dir}/bestPractice/
test262-test-sys-prop.test.failed.list.file=${build.dir}/test/failedTests
@@ -216,8 +216,18 @@ test262-test-sys-prop.test.js.framework=\
${test262.dir}/test/harness/framework.js \
${test262.dir}/test/harness/sta.js
+# testmarkdown test root
+testmarkdown-test-sys-prop.test.js.roots=${testmarkdown.dir}
+
+# execute testmarkdown tests in shared nashorn context or not?
+testmarkdown-test-sys-prop.test.js.shared.context=false
+
+# framework root for markdown script tests
+testmarkdown-test-sys-prop.test.js.framework=\
+ ${test.script.dir}${file.separator}markdown.js
+
# testjfx test root
-testjfx-test-sys-prop.test.js.roots=${testjfx.dir}
+testjfx-test-sys-prop.test.js.roots=${testjfx.dir}
# execute testjfx tests in shared nashorn context or not?
testjfx-test-sys-prop.test.js.shared.context=false
@@ -254,48 +264,114 @@ test.src.dir=test/src
run.test.xmx=2G
run.test.xms=2G
+# uncomment this jfr.args to enable light recordings. the stack needs to be cranked up to 1024 frames,
+# or everything will as of the now drown in lambda forms and be cut off.
+#
+#jfr.args=-XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:FlightRecorderOptions=defaultrecording=true,disk=true,dumponexit=true,dumponexitpath="test_suite.jfr",stackdepth=1024 \
+
+jfr.args=
+
run.test.user.language=tr
run.test.user.country=TR
-run.test.jvmargs.common=-server -XX:+TieredCompilation -Dfile.encoding=UTF-8 -Duser.language=${run.test.user.language} -Duser.country=${run.test.user.country} -XX:+HeapDumpOnOutOfMemoryError
-
-#-XX:-UseCompressedKlassPointers -XX:+PrintHeapAtGC -XX:ClassMetaspaceSize=300M
-# -XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintNMethods
+run.test.jvmargs.common=\
+ -server \
+ -Dfile.encoding=UTF-8 \
+ -Duser.language=${run.test.user.language} \
+ -Duser.country=${run.test.user.country} \
+ -Dnashorn.typeInfo.cacheDir=${build.dir}${file.separator}test${file.separator}type_info_cache \
+ ${jfr.args} \
+ -XX:+HeapDumpOnOutOfMemoryError
# turn on assertions for tests
run.test.jvmargs.main=${run.test.jvmargs.common} -ea
-#-XX:-UseCompressedKlassPointers -XX:+PrintHeapAtGC -XX:ClassMetaspaceSize=300M
-run.test.jvmargs.octane.main=${run.test.jvmargs.common}
+# extra jvmargs that might be useful for debugging
+#
+# -XX:+UnlockDiagnosticVMOptions
+#
+# turn off compressed class pointers in metaspace
+# -XX:-UseCompressedKlassPointers
+#
+# dump the heap after every GC
+# -XX:+PrintHeapAtGC
+#
+# manually set a metaspace size for class data
+# -XX:ClassMetaspaceSize=300M
+#
+# print out methods compiled
+# -XX:+PrintCompilation
+#
+# print all compiled nmethods with oopmaps and lots of other info
+# -XX:+PrintNMethods
-run.test.jvmsecurityargs=-Xverify:all -Djava.security.manager -Djava.security.policy=${basedir}/build/nashorn.policy
+# Use best known performance options for octane
+run.test.jvmargs.octane.main=${run.test.jvmargs.common} -XX:+UnlockDiagnosticVMOptions -XX:+UseNewCode -XX:TypeProfileLevel=222
+
+# Security manager args - make sure that we run with the nashorn.policy that the build creates
+run.test.jvmsecurityargs=-Xverify:all -Djava.security.manager -Djava.security.policy=${build.dir}/nashorn.policy
# VM options for script tests with @fork option
test-sys-prop.test.fork.jvm.options=${run.test.jvmargs.main} -Xmx${run.test.xmx} ${run.test.jvmsecurityargs} -cp ${run.test.classpath}
# path of rhino.jar for benchmarks
-rhino.jar=
+rhino.dir=
+rhino.jar=${rhino.dir}/js.jar
v8.shell=d8
+# How many iterations should 'ant octane' run for each
+# benchmark
+octane.iterations=25
+
+# List of octane tests to run, as properties prefixed with
+# "octane.benchmark." mapping to the benchmark name in
+# the test harness
+#
+# Octane tests that are disabled should have their entire line
+# commented out Tests may be disabled for functionality reasons when
+# they have bugs or when the runtime doesn't handle them (yet)
+octane.benchmark.box2d=box2d
+#octane.benchmark.code-load=code-load
+octane.benchmark.crypto=crypto
+octane.benchmark.deltablue=deltablue
+octane.benchmark.earley-boyer=earley-boyer
+octane.benchmark.gbemu=gbemu
+octane.benchmark.navier-stokes=navier-stokes
+octane.benchmark.mandreel=mandreel
+octane.benchmark.pdfjs=pdfjs
+octane.benchmark.raytrace=raytrace
+octane.benchmark.regexp=regexp
+octane.benchmark.richards=richards
+octane.benchmark.splay=splay
+#octane.benchmark.typescript=typescript
+#octane.benchmark.zlib=zlib
+
#path to rhino jar file
octaneperf-sys-prop.rhino.jar=${rhino.jar}
#timeout for performance tests in minutes
octaneperf-sys-prop.timeout.value=10
-################
-# codecoverage #
-################
- #enable/disable code coverage; please redifine in the ${user.home}/.nashorn.project.local.properties
+#how many iterations to run sunspider after warmup
+sunspider.iterations=3000
+
+#################
+# code coverage #
+#################
+
+#enable/disable code coverage; please redifine in the ${user.home}/.nashorn.project.local.properties
make.code.coverage=false
- #type of codecoverage; one of static or dynamic. Now only dynamic is supported
+
+#type of codecoverage; one of static or dynamic. Now only dynamic is supported
jcov=dynamic
- #naming of CC results
- #NB directory specified in the cc.dir will be cleaned up!!!
+
+#naming of CC results
+#NB directory specified in the cc.dir will be cleaned up!!!
cc.dir=${basedir}/../Codecoverage_Nashorn
cc.result.file.name=CC_${jcov}_nashorn.xml
- #dynamic CC parameters; please redefine in the ${user.home}/.nashorn.project.local.properties
+
+#dynamic CC parameters; please redefine in the ${user.home}/.nashorn.project.local.properties
jcov2.lib.dir=${basedir}/../jcov2/lib
jcov.jar=${jcov2.lib.dir}/jcov.jar
cc.include=jdk\.nashorn\.*
diff --git a/samples/javafoovars.js b/samples/javafoovars.js
new file mode 100644
index 00000000..037c2bf5
--- /dev/null
+++ b/samples/javafoovars.js
@@ -0,0 +1,103 @@
+#// Usage: jjs javafoovars.js -- <directory>
+
+/*
+ * 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.
+ */
+
+// This example demonstrates Java subclassing by Java.extend
+// and javac Compiler and Tree API. This example counts number
+// of variables called "foo" in the given java source files!
+if (arguments.length == 0) {
+ print("Usage: jjs javafoovars.js -- <directory>");
+ exit(1);
+}
+
+// Java types used
+var File = Java.type("java.io.File");
+var Files = Java.type("java.nio.file.Files");
+var FileVisitOption = Java.type("java.nio.file.FileVisitOption");
+var StringArray = Java.type("java.lang.String[]");
+var ToolProvider = Java.type("javax.tools.ToolProvider");
+var Tree = Java.type("com.sun.source.tree.Tree");
+var TreeScanner = Java.type("com.sun.source.util.TreeScanner");
+var VariableTree = Java.type("com.sun.source.tree.VariableTree");
+
+// count "foo"-s in the given .java files
+function countFoo() {
+ // get the system compiler tool
+ var compiler = ToolProvider.systemJavaCompiler;
+ // get standard file manager
+ var fileMgr = compiler.getStandardFileManager(null, null, null);
+ // Using Java.to convert script array (arguments) to a Java String[]
+ var compUnits = fileMgr.getJavaFileObjects(
+ Java.to(arguments, StringArray));
+ // create a new compilation task
+ var task = compiler.getTask(null, fileMgr, null, null, null, compUnits);
+ // subclass SimpleTreeVisitor - to count variables called "foo"
+ var FooCounterVisitor = Java.extend(TreeScanner);
+ var fooCount = 0;
+
+ var visitor = new FooCounterVisitor() {
+ visitVariable: function (node, p) {
+ if (node.name.toString() == "foo") {
+ fooCount++;
+ }
+ }
+ }
+
+ for each (var cu in task.parse()) {
+ cu.accept(visitor, null);
+ }
+ return fooCount;
+}
+
+// for each ".java" file in directory (recursively) count "foo".
+function main(dir) {
+ var totalCount = 0;
+ Files.walk(dir.toPath(), FileVisitOption.FOLLOW_LINKS).
+ forEach(function(p) {
+ var name = p.toFile().absolutePath;
+ if (name.endsWith(".java")) {
+ var count = 0;
+ try {
+ count = countFoo(p.toFile().getAbsolutePath());
+ } catch (e) {
+ print(e);
+ }
+ if (count != 0) {
+ print(name + ": " + count);
+ }
+ totalCount += count;
+ }
+ });
+ print("Total foo count: " + totalCount);
+}
+
+main(new File(arguments[0]));
diff --git a/samples/jsobj_example.js b/samples/jsobj_example.js
new file mode 100644
index 00000000..d608876e
--- /dev/null
+++ b/samples/jsobj_example.js
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+// Flexible script object using AbstractJSObject subclass
+
+var AbstractJSObject = Java.type("jdk.nashorn.api.scripting.AbstractJSObject");
+
+// JSObject example that uses a map for properties and
+// falls back to with methods on a java object (for missing
+// properties
+
+function makeJSObj(map, fallback) {
+ return new AbstractJSObject() {
+ getMember: function(name) {
+ if (map.containsKey(name)) {
+ return map.get(name);
+ }
+
+ var val = fallback[name];
+ if (typeof val == 'function') {
+ return function() {
+ var a = arguments;
+ switch (a.length) {
+ case 0: return fallback[name]();
+ case 1: return fallback[name](a[0]);
+ case 2: return fallback[name](a[0], a[1]);
+ case 3: return fallback[name](a[0], a[1], a[2]);
+ case 4: return fallback[name](a[0], a[1], a[2], a[3]);
+ }
+ }
+ }
+ }
+ }
+}
+
+var m = new java.util.HashMap();
+m.put("foo", 42);
+m.put("bar", 'hello');
+
+var obj = makeJSObj(m, new java.io.File("."));
+
+print(obj.foo);
+print(obj.bar);
+print(obj.getAbsolutePath());
+print(obj.isDirectory());
diff --git a/samples/zipfs.js b/samples/zipfs.js
new file mode 100644
index 00000000..ecb6f61a
--- /dev/null
+++ b/samples/zipfs.js
@@ -0,0 +1,48 @@
+/*
+ * 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 (arguments.length == 0) {
+ print("Usage: jjs zipfs.js -- <.zip/.jar file>")
+ exit(1)
+}
+
+var Files = Java.type("java.nio.file.Files")
+var FileSystems = Java.type("java.nio.file.FileSystems")
+var FileVisitOption = Java.type("java.nio.file.FileVisitOption")
+var Paths = Java.type("java.nio.file.Paths")
+
+var zipfile = Paths.get(arguments[0])
+var fs = FileSystems.newFileSystem(zipfile, null)
+var root = fs.rootDirectories[0]
+Files.walk(root, FileVisitOption.FOLLOW_LINKS).forEach(
+ function(p) (print(p), print(Files.readAttributes(p, "zip:*")))
+)
+fs.close()
diff --git a/samples/ziplist.js b/samples/ziplist.js
new file mode 100644
index 00000000..214dd351
--- /dev/null
+++ b/samples/ziplist.js
@@ -0,0 +1,80 @@
+/*
+ * 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 (arguments.length == 0) {
+ print("Usage: jjs ziplist -- <zip-file>");
+ exit(1);
+}
+
+// list the content details of a .zip or .jar file
+var file = arguments[0];
+
+// java classes used
+var Attributes = Java.type("java.util.jar.Attributes");
+var FileTime = Java.type("java.nio.file.attribute.FileTime");
+var JarFile = Java.type("java.util.jar.JarFile");
+var ZipEntry = Java.type("java.util.zip.ZipEntry");
+var ZipFile = Java.type("java.util.zip.ZipFile");
+
+var zf = file.endsWith(".jar")? new JarFile(file) : new ZipFile(file);
+
+var entries = zf.entries();
+// make overall output a valid JSON
+var zfObj = {
+ name: zf.name,
+ comment: zf.comment,
+ size: zf.size(),
+ entries: []
+};
+
+while (entries.hasMoreElements()) {
+ zfObj.entries.push(entries.nextElement());
+}
+
+print(JSON.stringify(zfObj, function (key, value) {
+ if (value instanceof ZipEntry) {
+ return Object.bindProperties({}, value);
+ } else if (value instanceof FileTime) {
+ return value.toString();
+ } else if (value instanceof Attributes) {
+ var attrs = {};
+ var itr = value.entrySet().iterator();
+ while (itr.hasNext()) {
+ var n = itr.next();
+ attrs[n.key] = String(n.value);
+ }
+ return attrs;
+ }
+
+ return value;
+}, ' '));
+
+zf.close();
diff --git a/src/jdk/internal/dynalink/ChainedCallSite.java b/src/jdk/internal/dynalink/ChainedCallSite.java
index 31346e6d..45f191a0 100644
--- a/src/jdk/internal/dynalink/ChainedCallSite.java
+++ b/src/jdk/internal/dynalink/ChainedCallSite.java
@@ -103,8 +103,27 @@ import jdk.internal.dynalink.support.Lookup;
* handle is always at the start of the chain.
*/
public class ChainedCallSite extends AbstractRelinkableCallSite {
- private static final MethodHandle PRUNE = Lookup.findOwnSpecial(MethodHandles.lookup(), "prune", MethodHandle.class,
- MethodHandle.class);
+ private static final MethodHandle PRUNE_CATCHES =
+ MethodHandles.insertArguments(
+ Lookup.findOwnSpecial(
+ MethodHandles.lookup(),
+ "prune",
+ MethodHandle.class,
+ MethodHandle.class,
+ boolean.class),
+ 2,
+ true);
+
+ private static final MethodHandle PRUNE_SWITCHPOINTS =
+ MethodHandles.insertArguments(
+ Lookup.findOwnSpecial(
+ MethodHandles.lookup(),
+ "prune",
+ MethodHandle.class,
+ MethodHandle.class,
+ boolean.class),
+ 2,
+ false);
private final AtomicReference<LinkedList<GuardedInvocation>> invocations = new AtomicReference<>();
@@ -127,23 +146,25 @@ public class ChainedCallSite extends AbstractRelinkableCallSite {
@Override
public void relink(final GuardedInvocation guardedInvocation, final MethodHandle fallback) {
- relinkInternal(guardedInvocation, fallback, false);
+ relinkInternal(guardedInvocation, fallback, false, false);
}
@Override
public void resetAndRelink(final GuardedInvocation guardedInvocation, final MethodHandle fallback) {
- relinkInternal(guardedInvocation, fallback, true);
+ relinkInternal(guardedInvocation, fallback, true, false);
}
- private MethodHandle relinkInternal(final GuardedInvocation invocation, final MethodHandle relink, final boolean reset) {
+ private MethodHandle relinkInternal(final GuardedInvocation invocation, final MethodHandle relink, final boolean reset, final boolean removeCatches) {
final LinkedList<GuardedInvocation> currentInvocations = invocations.get();
@SuppressWarnings({ "unchecked", "rawtypes" })
final LinkedList<GuardedInvocation> newInvocations =
currentInvocations == null || reset ? new LinkedList<>() : (LinkedList)currentInvocations.clone();
- // First, prune the chain of invalidated switchpoints.
+ // First, prune the chain of invalidated switchpoints, we always do this
+ // We also remove any catches if the remove catches flag is set
for(final Iterator<GuardedInvocation> it = newInvocations.iterator(); it.hasNext();) {
- if(it.next().hasBeenInvalidated()) {
+ final GuardedInvocation inv = it.next();
+ if(inv.hasBeenInvalidated() || (removeCatches && inv.getException() != null)) {
it.remove();
}
}
@@ -160,12 +181,13 @@ public class ChainedCallSite extends AbstractRelinkableCallSite {
// prune-and-invoke is used as the fallback for invalidated switchpoints. If a switchpoint gets invalidated, we
// rebuild the chain and get rid of all invalidated switchpoints instead of letting them linger.
- final MethodHandle pruneAndInvoke = makePruneAndInvokeMethod(relink);
+ final MethodHandle pruneAndInvokeSwitchPoints = makePruneAndInvokeMethod(relink, getPruneSwitchpoints());
+ final MethodHandle pruneAndInvokeCatches = makePruneAndInvokeMethod(relink, getPruneCatches());
// Fold the new chain
MethodHandle target = relink;
for(final GuardedInvocation inv: newInvocations) {
- target = inv.compose(pruneAndInvoke, target);
+ target = inv.compose(target, pruneAndInvokeSwitchPoints, pruneAndInvokeCatches);
}
// If nobody else updated the call site while we were rebuilding the chain, set the target to our chain. In case
@@ -178,14 +200,30 @@ public class ChainedCallSite extends AbstractRelinkableCallSite {
}
/**
+ * Get the switchpoint pruning function for a chained call site
+ * @return function that removes invalidated switchpoints tied to callsite guard chain and relinks
+ */
+ protected MethodHandle getPruneSwitchpoints() {
+ return PRUNE_SWITCHPOINTS;
+ }
+
+ /**
+ * Get the catch pruning function for a chained call site
+ * @return function that removes all catches tied to callsite guard chain and relinks
+ */
+ protected MethodHandle getPruneCatches() {
+ return PRUNE_CATCHES;
+ }
+
+ /**
* Creates a method that rebuilds our call chain, pruning it of any invalidated switchpoints, and then invokes that
* chain.
* @param relink the ultimate fallback for the chain (the {@code DynamicLinker}'s relink).
* @return a method handle for prune-and-invoke
*/
- private MethodHandle makePruneAndInvokeMethod(final MethodHandle relink) {
+ private MethodHandle makePruneAndInvokeMethod(final MethodHandle relink, final MethodHandle prune) {
// Bind prune to (this, relink)
- final MethodHandle boundPrune = MethodHandles.insertArguments(PRUNE, 0, this, relink);
+ final MethodHandle boundPrune = MethodHandles.insertArguments(prune, 0, this, relink);
// Make it ignore all incoming arguments
final MethodHandle ignoreArgsPrune = MethodHandles.dropArguments(boundPrune, 0, type().parameterList());
// Invoke prune, then invoke the call site target with original arguments
@@ -193,7 +231,7 @@ public class ChainedCallSite extends AbstractRelinkableCallSite {
}
@SuppressWarnings("unused")
- private MethodHandle prune(final MethodHandle relink) {
- return relinkInternal(null, relink, false);
+ private MethodHandle prune(final MethodHandle relink, final boolean catches) {
+ return relinkInternal(null, relink, false, catches);
}
}
diff --git a/src/jdk/internal/dynalink/DynamicLinker.java b/src/jdk/internal/dynalink/DynamicLinker.java
index 489272c6..e7e58d2a 100644
--- a/src/jdk/internal/dynalink/DynamicLinker.java
+++ b/src/jdk/internal/dynalink/DynamicLinker.java
@@ -140,7 +140,6 @@ import jdk.internal.dynalink.support.RuntimeContextLinkRequestImpl;
* @author Attila Szegedi
*/
public class DynamicLinker {
-
private static final String CLASS_NAME = DynamicLinker.class.getName();
private static final String RELINK_METHOD_NAME = "relink";
@@ -148,6 +147,7 @@ public class DynamicLinker {
private static final String INITIAL_LINK_METHOD_NAME = "linkCallSite";
private final LinkerServices linkerServices;
+ private final GuardedInvocationFilter prelinkFilter;
private final int runtimeContextArgCount;
private final boolean syncOnRelink;
private final int unstableRelinkThreshold;
@@ -156,18 +156,20 @@ public class DynamicLinker {
* Creates a new dynamic linker.
*
* @param linkerServices the linkerServices used by the linker, created by the factory.
+ * @param prelinkFilter see {@link DynamicLinkerFactory#setPrelinkFilter(GuardedInvocationFilter)}
* @param runtimeContextArgCount see {@link DynamicLinkerFactory#setRuntimeContextArgCount(int)}
*/
- DynamicLinker(final LinkerServices linkerServices, final int runtimeContextArgCount, final boolean syncOnRelink,
- final int unstableRelinkThreshold) {
+ DynamicLinker(final LinkerServices linkerServices, final GuardedInvocationFilter prelinkFilter, final int runtimeContextArgCount,
+ final boolean syncOnRelink, final int unstableRelinkThreshold) {
if(runtimeContextArgCount < 0) {
throw new IllegalArgumentException("runtimeContextArgCount < 0");
}
if(unstableRelinkThreshold < 0) {
throw new IllegalArgumentException("unstableRelinkThreshold < 0");
}
- this.runtimeContextArgCount = runtimeContextArgCount;
this.linkerServices = linkerServices;
+ this.prelinkFilter = prelinkFilter;
+ this.runtimeContextArgCount = runtimeContextArgCount;
this.syncOnRelink = syncOnRelink;
this.unstableRelinkThreshold = unstableRelinkThreshold;
}
@@ -224,11 +226,10 @@ public class DynamicLinker {
final boolean unstableDetectionEnabled = unstableRelinkThreshold > 0;
final boolean callSiteUnstable = unstableDetectionEnabled && relinkCount >= unstableRelinkThreshold;
final LinkRequest linkRequest =
- runtimeContextArgCount == 0 ? new LinkRequestImpl(callSiteDescriptor, callSiteUnstable, arguments)
- : new RuntimeContextLinkRequestImpl(callSiteDescriptor, callSiteUnstable, arguments,
- runtimeContextArgCount);
+ runtimeContextArgCount == 0 ?
+ new LinkRequestImpl(callSiteDescriptor, callSite, relinkCount, callSiteUnstable, arguments) :
+ new RuntimeContextLinkRequestImpl(callSiteDescriptor, callSite, relinkCount, callSiteUnstable, arguments, runtimeContextArgCount);
- // Find a suitable method handle with a guard
GuardedInvocation guardedInvocation = linkerServices.getGuardedInvocation(linkRequest);
// None found - throw an exception
@@ -248,6 +249,11 @@ public class DynamicLinker {
}
}
+ // Make sure we filter the invocation before linking it into the call site. This is typically used to match the
+ // return type of the invocation to the call site.
+ guardedInvocation = prelinkFilter.filter(guardedInvocation, linkRequest, linkerServices);
+ guardedInvocation.getClass(); // null pointer check
+
int newRelinkCount = relinkCount;
// Note that the short-circuited "&&" evaluation below ensures we'll increment the relinkCount until
// threshold + 1 but not beyond that. Threshold + 1 is treated as a special value to signal that resetAndRelink
diff --git a/src/jdk/internal/dynalink/DynamicLinkerFactory.java b/src/jdk/internal/dynalink/DynamicLinkerFactory.java
index 0d4c23d5..af5eb119 100644
--- a/src/jdk/internal/dynalink/DynamicLinkerFactory.java
+++ b/src/jdk/internal/dynalink/DynamicLinkerFactory.java
@@ -102,14 +102,15 @@ import jdk.internal.dynalink.support.BottomGuardingDynamicLinker;
import jdk.internal.dynalink.support.ClassLoaderGetterContextProvider;
import jdk.internal.dynalink.support.CompositeGuardingDynamicLinker;
import jdk.internal.dynalink.support.CompositeTypeBasedGuardingDynamicLinker;
+import jdk.internal.dynalink.support.DefaultPrelinkFilter;
import jdk.internal.dynalink.support.LinkerServicesImpl;
import jdk.internal.dynalink.support.TypeConverterFactory;
/**
* A factory class for creating {@link DynamicLinker}s. The most usual dynamic linker is a linker that is a composition
* of all {@link GuardingDynamicLinker}s known and pre-created by the caller as well as any
- * {@link AutoDiscovery automatically discovered} guarding linkers and the standard fallback {@link BeansLinker}. See
- * {@link DynamicLinker} documentation for tips on how to use this class.
+ * {@link AutoDiscovery automatically discovered} guarding linkers and the standard fallback {@link BeansLinker} and a
+ * {@link DefaultPrelinkFilter}. See {@link DynamicLinker} documentation for tips on how to use this class.
*
* @author Attila Szegedi
*/
@@ -128,6 +129,7 @@ public class DynamicLinkerFactory {
private int runtimeContextArgCount = 0;
private boolean syncOnRelink = false;
private int unstableRelinkThreshold = DEFAULT_UNSTABLE_RELINK_THRESHOLD;
+ private GuardedInvocationFilter prelinkFilter;
/**
* Sets the class loader for automatic discovery of available linkers. If not set explicitly, then the thread
@@ -246,7 +248,19 @@ public class DynamicLinkerFactory {
}
/**
- * Creates a new dynamic linker consisting of all the prioritized, autodiscovered, and fallback linkers.
+ * Set the pre-link filter. This is a {@link GuardedInvocationFilter} that will get the final chance to modify the
+ * guarded invocation after it has been created by a component linker and before the dynamic linker links it into
+ * the call site. It is normally used to adapt the return value type of the invocation to the type of the call site.
+ * When not set explicitly, {@link DefaultPrelinkFilter} will be used.
+ * @param prelinkFilter the pre-link filter for the dynamic linker.
+ */
+ public void setPrelinkFilter(final GuardedInvocationFilter prelinkFilter) {
+ this.prelinkFilter = prelinkFilter;
+ }
+
+ /**
+ * Creates a new dynamic linker consisting of all the prioritized, autodiscovered, and fallback linkers as well as
+ * the pre-link filter.
*
* @return the new dynamic Linker
*/
@@ -306,8 +320,12 @@ public class DynamicLinkerFactory {
}
}
+ if(prelinkFilter == null) {
+ prelinkFilter = new DefaultPrelinkFilter();
+ }
+
return new DynamicLinker(new LinkerServicesImpl(new TypeConverterFactory(typeConverters), composite),
- runtimeContextArgCount, syncOnRelink, unstableRelinkThreshold);
+ prelinkFilter, runtimeContextArgCount, syncOnRelink, unstableRelinkThreshold);
}
private static ClassLoader getThreadContextClassLoader() {
diff --git a/src/jdk/internal/dynalink/GuardedInvocationFilter.java b/src/jdk/internal/dynalink/GuardedInvocationFilter.java
new file mode 100644
index 00000000..8560d82f
--- /dev/null
+++ b/src/jdk/internal/dynalink/GuardedInvocationFilter.java
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ */
+
+/*
+ * 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 2009-2013 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;
+
+import jdk.internal.dynalink.linker.GuardedInvocation;
+import jdk.internal.dynalink.linker.LinkRequest;
+import jdk.internal.dynalink.linker.LinkerServices;
+
+/**
+ * Interface for objects that are used to transform one guarded invocation into another one. Typical usage is for
+ * implementing {@link DynamicLinkerFactory#setPrelinkFilter(GuardedInvocationFilter) pre-link filters}.
+ */
+public interface GuardedInvocationFilter {
+ /**
+ * Given a guarded invocation, return a potentially different guarded invocation.
+ * @param inv the original guarded invocation. Null is never passed.
+ * @param linkRequest the link request for which the invocation was generated (usually by some linker).
+ * @param linkerServices the linker services that can be used during creation of a new invocation.
+ * @return either the passed guarded invocation or a different one, with the difference usually determined based on
+ * information in the link request and the differing invocation created with the assistance of the linker services.
+ * Whether or not {@code null} is an accepted return value is dependent on the user of the filter.
+ */
+ public GuardedInvocation filter(GuardedInvocation inv, LinkRequest linkRequest, LinkerServices linkerServices);
+}
diff --git a/src/jdk/internal/dynalink/beans/AbstractJavaLinker.java b/src/jdk/internal/dynalink/beans/AbstractJavaLinker.java
index a2567f95..dab6cbf5 100644
--- a/src/jdk/internal/dynalink/beans/AbstractJavaLinker.java
+++ b/src/jdk/internal/dynalink/beans/AbstractJavaLinker.java
@@ -106,6 +106,7 @@ import jdk.internal.dynalink.linker.LinkerServices;
import jdk.internal.dynalink.support.CallSiteDescriptorFactory;
import jdk.internal.dynalink.support.Guards;
import jdk.internal.dynalink.support.Lookup;
+import jdk.internal.dynalink.support.TypeUtilities;
/**
* A base class for both {@link StaticClassLinker} and {@link BeanLinker}. Deals with common aspects of property
@@ -289,7 +290,7 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
return new CallerSensitiveDynamicMethod(m);
}
final Member member = (Member)m;
- return new SimpleDynamicMethod(unreflectSafely(m), member.getDeclaringClass(), member.getName());
+ return new SimpleDynamicMethod(unreflectSafely(m), member.getDeclaringClass(), member.getName(), m instanceof Constructor);
}
/**
@@ -389,6 +390,10 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
return new GuardedInvocationComponent(invocation, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
}
+ SingleDynamicMethod getConstructorMethod(final String signature) {
+ return null;
+ }
+
private MethodHandle getAssignableGuard(final MethodType type) {
return Guards.asType(assignableGuard, type);
}
@@ -411,18 +416,18 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
return inv == null ? null : new GuardedInvocation(inv, getClassGuard(callSiteDescriptor.getMethodType()));
}
- private static MethodHandle getDynamicMethodInvocation(final CallSiteDescriptor callSiteDescriptor,
+ private MethodHandle getDynamicMethodInvocation(final CallSiteDescriptor callSiteDescriptor,
final LinkerServices linkerServices, final String methodName, final Map<String, DynamicMethod> methodMap) {
final DynamicMethod dynaMethod = getDynamicMethod(methodName, methodMap);
return dynaMethod != null ? dynaMethod.getInvocation(callSiteDescriptor, linkerServices) : null;
}
- private static DynamicMethod getDynamicMethod(final String methodName, final Map<String, DynamicMethod> methodMap) {
+ private DynamicMethod getDynamicMethod(final String methodName, final Map<String, DynamicMethod> methodMap) {
final DynamicMethod dynaMethod = methodMap.get(methodName);
return dynaMethod != null ? dynaMethod : getExplicitSignatureDynamicMethod(methodName, methodMap);
}
- private static SingleDynamicMethod getExplicitSignatureDynamicMethod(final String methodName,
+ private SingleDynamicMethod getExplicitSignatureDynamicMethod(final String fullName,
final Map<String, DynamicMethod> methodsMap) {
// What's below is meant to support the "name(type, type, ...)" syntax that programmers can use in a method name
// to manually pin down an exact overloaded variant. This is not usually required, as the overloaded method
@@ -432,23 +437,33 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
// for performance reasons.
// Is the method name lexically of the form "name(types)"?
- final int lastChar = methodName.length() - 1;
- if(methodName.charAt(lastChar) != ')') {
+ final int lastChar = fullName.length() - 1;
+ if(fullName.charAt(lastChar) != ')') {
return null;
}
- final int openBrace = methodName.indexOf('(');
+ final int openBrace = fullName.indexOf('(');
if(openBrace == -1) {
return null;
}
+ final String name = fullName.substring(0, openBrace);
+ final String signature = fullName.substring(openBrace + 1, lastChar);
+
// Find an existing method for the "name" part
- final DynamicMethod simpleNamedMethod = methodsMap.get(methodName.substring(0, openBrace));
+ final DynamicMethod simpleNamedMethod = methodsMap.get(name);
if(simpleNamedMethod == null) {
+ // explicit signature constructor access
+ // Java.type("java.awt.Color")["(int,int,int)"]
+ // will get Color(int,int,int) constructor of Color class.
+ if (name.isEmpty()) {
+ return getConstructorMethod(signature);
+ }
+
return null;
}
// Try to get a narrowed dynamic method for the explicit parameter types.
- return simpleNamedMethod.getMethodForExactParamTypes(methodName.substring(openBrace + 1, lastChar));
+ return simpleNamedMethod.getMethodForExactParamTypes(signature);
}
private static final MethodHandle IS_METHOD_HANDLE_NOT_NULL = Guards.isNotNull().asType(MethodType.methodType(
@@ -458,12 +473,16 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
private GuardedInvocationComponent getPropertySetter(final CallSiteDescriptor callSiteDescriptor,
final LinkerServices linkerServices, final List<String> operations) throws Exception {
- final MethodType type = callSiteDescriptor.getMethodType();
switch(callSiteDescriptor.getNameTokenCount()) {
case 2: {
// Must have three arguments: target object, property name, and property value.
assertParameterCount(callSiteDescriptor, 3);
+ // 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);
+
// What's below is basically:
// foldArguments(guardWithTest(isNotNull, invoke, null|nextComponent.invocation),
// get_setter_handle(type, linkerServices))
@@ -472,8 +491,8 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
// component's invocation.
// Call site type is "ret_type(object_type,property_name_type,property_value_type)", which we'll
- // abbreviate to R(O, N, V) going forward.
- // We want setters that conform to "R(O, V)"
+ // abbreviate to R(O, N, V) going forward, although we don't really use R here (see above about using
+ // Object return type).
final MethodType setterType = type.dropParameterTypes(1, 2);
// Bind property setter handle to the expected setter type and linker services. Type is
// MethodHandle(Object, String, Object)
@@ -494,11 +513,11 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
final MethodHandle fallbackFolded;
if(nextComponent == null) {
- // Object(MethodHandle)->R(MethodHandle, O, N, V); returns constant null
+ // Object(MethodHandle)->Object(MethodHandle, O, N, V); returns constant null
fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_METHOD_HANDLE, 1,
type.parameterList()).asType(type.insertParameterTypes(0, MethodHandle.class));
} else {
- // R(O, N, V)->R(MethodHandle, O, N, V); adapts the next component's invocation to drop the
+ // Object(O, N, V)->Object(MethodHandle, O, N, V); adapts the next component's invocation to drop the
// extra argument resulting from fold
fallbackFolded = MethodHandles.dropArguments(nextComponent.getGuardedInvocation().getInvocation(),
0, MethodHandle.class);
@@ -544,9 +563,12 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
private GuardedInvocationComponent getPropertyGetter(final CallSiteDescriptor callSiteDescriptor,
final LinkerServices linkerServices, final List<String> ops) throws Exception {
- final MethodType type = callSiteDescriptor.getMethodType();
switch(callSiteDescriptor.getNameTokenCount()) {
case 2: {
+ // Since we can't know what kind of a getter we'll get back on different invocations, we'll just
+ // conservatively presume Object. Note we can't just coerce to a narrower call site type as the linking
+ // runtime might not allow coercing at that call site.
+ final MethodType type = callSiteDescriptor.getMethodType().changeReturnType(Object.class);
// Must have exactly two arguments: receiver and name
assertParameterCount(callSiteDescriptor, 2);
@@ -562,11 +584,11 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
GET_ANNOTATED_METHOD, 1, callSiteDescriptor.getLookup());
final MethodHandle callSiteBoundInvoker = MethodHandles.filterArguments(GETTER_INVOKER, 0,
callSiteBoundMethodGetter);
- // Object(AnnotatedDynamicMethod, Object)->R(AnnotatedDynamicMethod, T0)
+ // Object(AnnotatedDynamicMethod, Object)->Object(AnnotatedDynamicMethod, T0)
final MethodHandle invokeHandleTyped = linkerServices.asType(callSiteBoundInvoker,
MethodType.methodType(type.returnType(), AnnotatedDynamicMethod.class, type.parameterType(0)));
// Since it's in the target of a fold, drop the unnecessary second argument
- // R(AnnotatedDynamicMethod, T0)->R(AnnotatedDynamicMethod, T0, T1)
+ // Object(AnnotatedDynamicMethod, T0)->Object(AnnotatedDynamicMethod, T0, T1)
final MethodHandle invokeHandleFolded = MethodHandles.dropArguments(invokeHandleTyped, 2,
type.parameterType(1));
final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
@@ -574,17 +596,19 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
final MethodHandle fallbackFolded;
if(nextComponent == null) {
- // Object(AnnotatedDynamicMethod)->R(AnnotatedDynamicMethod, T0, T1); returns constant null
+ // Object(AnnotatedDynamicMethod)->Object(AnnotatedDynamicMethod, T0, T1); returns constant null
fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_ANNOTATED_METHOD, 1,
type.parameterList()).asType(type.insertParameterTypes(0, AnnotatedDynamicMethod.class));
} else {
- // R(T0, T1)->R(AnnotatedDynamicMethod, T0, T1); adapts the next component's invocation to drop the
- // extra argument resulting from fold
- fallbackFolded = MethodHandles.dropArguments(nextComponent.getGuardedInvocation().getInvocation(),
- 0, AnnotatedDynamicMethod.class);
+ // Object(T0, T1)->Object(AnnotatedDynamicMethod, T0, T1); adapts the next component's invocation to
+ // drop the extra argument resulting from fold and to change its return type to Object.
+ final MethodHandle nextInvocation = nextComponent.getGuardedInvocation().getInvocation();
+ final MethodType nextType = nextInvocation.type();
+ fallbackFolded = MethodHandles.dropArguments(nextInvocation.asType(
+ nextType.changeReturnType(Object.class)), 0, AnnotatedDynamicMethod.class);
}
- // fold(R(AnnotatedDynamicMethod, T0, T1), AnnotatedDynamicMethod(T0, T1))
+ // fold(Object(AnnotatedDynamicMethod, T0, T1), AnnotatedDynamicMethod(T0, T1))
final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(
IS_ANNOTATED_METHOD_NOT_NULL, invokeHandleFolded, fallbackFolded), typedGetter);
if(nextComponent == null) {
@@ -611,8 +635,8 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
// value is null.
final ValidationType validationType = annGetter.validationType;
// TODO: we aren't using the type that declares the most generic getter here!
- return new GuardedInvocationComponent(linkerServices.asType(getter, type), getGuard(validationType,
- type), clazz, validationType);
+ return new GuardedInvocationComponent(getter, getGuard(validationType,
+ callSiteDescriptor.getMethodType()), clazz, validationType);
}
default: {
// Can't do anything with more than 3 name components
@@ -641,21 +665,25 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
}
}
- private static final MethodHandle IS_DYNAMIC_METHOD_NOT_NULL = Guards.asType(Guards.isNotNull(),
- MethodType.methodType(boolean.class, DynamicMethod.class));
- private static final MethodHandle DYNAMIC_METHOD_IDENTITY = MethodHandles.identity(DynamicMethod.class);
+ private static final MethodHandle IS_DYNAMIC_METHOD = Guards.isInstance(DynamicMethod.class,
+ MethodType.methodType(boolean.class, Object.class));
+ private static final MethodHandle OBJECT_IDENTITY = MethodHandles.identity(Object.class);
private GuardedInvocationComponent getMethodGetter(final CallSiteDescriptor callSiteDescriptor,
final LinkerServices linkerServices, final List<String> ops) throws Exception {
- final MethodType type = callSiteDescriptor.getMethodType();
+ // The created method handle will always return a DynamicMethod (or null), but since we don't want that type to
+ // be visible outside of this linker, declare it to return Object.
+ final MethodType type = callSiteDescriptor.getMethodType().changeReturnType(Object.class);
switch(callSiteDescriptor.getNameTokenCount()) {
case 2: {
// Must have exactly two arguments: receiver and name
assertParameterCount(callSiteDescriptor, 2);
final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
linkerServices, ops);
- if(nextComponent == null) {
- // No next component operation; just return a component for this operation.
+ if(nextComponent == null || !TypeUtilities.areAssignable(DynamicMethod.class,
+ nextComponent.getGuardedInvocation().getInvocation().type().returnType())) {
+ // No next component operation, or it can never produce a dynamic method; just return a component
+ // for this operation.
return getClassGuardedInvocationComponent(linkerServices.asType(getDynamicMethod, type), type);
}
@@ -664,21 +692,20 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
// bunch of method signature adjustments. Basically, execute method getter; if it returns a non-null
// DynamicMethod, use identity to return it, otherwise delegate to nextComponent's invocation.
- final MethodHandle typedGetter = linkerServices.asType(getDynamicMethod, type.changeReturnType(
- DynamicMethod.class));
+ final MethodHandle typedGetter = linkerServices.asType(getDynamicMethod, type);
// Since it is part of the foldArgument() target, it will have extra args that we need to drop.
final MethodHandle returnMethodHandle = linkerServices.asType(MethodHandles.dropArguments(
- DYNAMIC_METHOD_IDENTITY, 1, type.parameterList()), type.insertParameterTypes(0,
- DynamicMethod.class));
+ OBJECT_IDENTITY, 1, type.parameterList()), type.insertParameterTypes(0, Object.class));
final MethodHandle nextComponentInvocation = nextComponent.getGuardedInvocation().getInvocation();
- // The assumption is that getGuardedInvocationComponent() already asType()'d it correctly
- assert nextComponentInvocation.type().equals(type);
+ // The assumption is that getGuardedInvocationComponent() already asType()'d it correctly modulo the
+ // return type.
+ assert nextComponentInvocation.type().changeReturnType(type.returnType()).equals(type);
// Since it is part of the foldArgument() target, we have to drop an extra arg it receives.
final MethodHandle nextCombinedInvocation = MethodHandles.dropArguments(nextComponentInvocation, 0,
- DynamicMethod.class);
+ Object.class);
// Assemble it all into a fold(guard(isNotNull, identity, nextInvocation), get)
final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(
- IS_DYNAMIC_METHOD_NOT_NULL, returnMethodHandle, nextCombinedInvocation), typedGetter);
+ IS_DYNAMIC_METHOD, returnMethodHandle, nextCombinedInvocation), typedGetter);
return nextComponent.compose(compositeGetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
}
@@ -694,7 +721,7 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
// No delegation to the next component of the composite operation; if we have a method with that name,
// we'll always return it at this point.
return getClassGuardedInvocationComponent(linkerServices.asType(MethodHandles.dropArguments(
- MethodHandles.constant(DynamicMethod.class, method), 0, type.parameterType(0)), type), type);
+ MethodHandles.constant(Object.class, method), 0, type.parameterType(0)), type), type);
}
default: {
// Can't do anything with more than 3 name components
@@ -703,6 +730,30 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
}
}
+ static class MethodPair {
+ final MethodHandle method1;
+ final MethodHandle method2;
+
+ MethodPair(final MethodHandle method1, final MethodHandle method2) {
+ this.method1 = method1;
+ this.method2 = method2;
+ }
+
+ MethodHandle guardWithTest(final MethodHandle test) {
+ return MethodHandles.guardWithTest(test, method1, method2);
+ }
+ }
+
+ static MethodPair matchReturnTypes(final MethodHandle m1, final MethodHandle m2) {
+ final MethodType type1 = m1.type();
+ final MethodType type2 = m2.type();
+ final Class<?> commonRetType = TypeUtilities.getCommonLosslessConversionType(type1.returnType(),
+ type2.returnType());
+ return new MethodPair(
+ m1.asType(type1.changeReturnType(commonRetType)),
+ m2.asType(type2.changeReturnType(commonRetType)));
+ }
+
private static void assertParameterCount(final CallSiteDescriptor descriptor, final int paramCount) {
if(descriptor.getMethodType().parameterCount() != paramCount) {
throw new BootstrapMethodError(descriptor.getName() + " must have exactly " + paramCount + " parameters.");
@@ -738,11 +789,14 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
}
private static MethodHandle GET_DYNAMIC_METHOD = MethodHandles.dropArguments(privateLookup.findOwnSpecial(
- "getDynamicMethod", DynamicMethod.class, Object.class), 1, Object.class);
+ "getDynamicMethod", Object.class, Object.class), 1, Object.class);
private final MethodHandle getDynamicMethod = GET_DYNAMIC_METHOD.bindTo(this);
@SuppressWarnings("unused")
- private DynamicMethod getDynamicMethod(final Object name) {
+ // This method is marked to return Object instead of DynamicMethod as it's used as a linking component and we don't
+ // want to make the DynamicMethod type observable externally (e.g. as the return type of a MethodHandle returned for
+ // "dyn:getMethod" linking).
+ private Object getDynamicMethod(final Object name) {
return getDynamicMethod(String.valueOf(name), methods);
}
diff --git a/src/jdk/internal/dynalink/beans/BeanLinker.java b/src/jdk/internal/dynalink/beans/BeanLinker.java
index 5bc0e9b3..f7f0c94a 100644
--- a/src/jdk/internal/dynalink/beans/BeanLinker.java
+++ b/src/jdk/internal/dynalink/beans/BeanLinker.java
@@ -237,8 +237,9 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
} else {
checkGuard = convertArgToInt(RANGE_CHECK_ARRAY, linkerServices, callSiteDescriptor);
}
- return nextComponent.compose(MethodHandles.guardWithTest(binder.bindTest(checkGuard),
- binder.bind(invocation), nextComponent.getGuardedInvocation().getInvocation()), gi.getGuard(),
+ final MethodPair matchedInvocations = matchReturnTypes(binder.bind(invocation),
+ nextComponent.getGuardedInvocation().getInvocation());
+ return nextComponent.compose(matchedInvocations.guardWithTest(binder.bindTest(checkGuard)), gi.getGuard(),
gic.getValidatorClass(), gic.getValidationType());
}
@@ -308,7 +309,7 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
}
/*private*/ MethodHandle bind(final MethodHandle handle) {
- return bindToFixedKey(linkerServices.asType(handle, methodType));
+ return bindToFixedKey(linkerServices.asTypeLosslessReturn(handle, methodType));
}
/*private*/ MethodHandle bindTest(final MethodHandle handle) {
@@ -440,8 +441,9 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
final MethodHandle checkGuard = convertArgToInt(invocation == SET_LIST_ELEMENT ? RANGE_CHECK_LIST :
RANGE_CHECK_ARRAY, linkerServices, callSiteDescriptor);
- return nextComponent.compose(MethodHandles.guardWithTest(binder.bindTest(checkGuard),
- binder.bind(invocation), nextComponent.getGuardedInvocation().getInvocation()), gi.getGuard(),
+ final MethodPair matchedInvocations = matchReturnTypes(binder.bind(invocation),
+ nextComponent.getGuardedInvocation().getInvocation());
+ return nextComponent.compose(matchedInvocations.guardWithTest(binder.bindTest(checkGuard)), gi.getGuard(),
gic.getValidatorClass(), gic.getValidationType());
}
diff --git a/src/jdk/internal/dynalink/beans/BeansLinker.java b/src/jdk/internal/dynalink/beans/BeansLinker.java
index b1862de0..439c5a65 100644
--- a/src/jdk/internal/dynalink/beans/BeansLinker.java
+++ b/src/jdk/internal/dynalink/beans/BeansLinker.java
@@ -169,6 +169,26 @@ public class BeansLinker implements GuardingDynamicLinker {
}
/**
+ * Returns true if the object is a Dynalink Java constructor.
+ *
+ * @param obj the object we want to test for being a constructor
+ * @return true if it is a constructor, false otherwise.
+ */
+ public static boolean isDynamicConstructor(final Object obj) {
+ return obj instanceof DynamicMethod && ((DynamicMethod)obj).isConstructor();
+ }
+
+ /**
+ * Return the dynamic method of constructor of the given class and the given signature.
+ * @param clazz the class
+ * @param signature full signature of the constructor
+ * @return DynamicMethod for the constructor
+ */
+ public static Object getConstructorMethod(final Class<?> clazz, final String signature) {
+ return StaticClassLinker.getConstructorMethod(clazz, signature);
+ }
+
+ /**
* Returns a collection of names of all readable instance properties of a class.
* @param clazz the class
* @return a collection of names of all readable instance properties of a class.
diff --git a/src/jdk/internal/dynalink/beans/CallerSensitiveDynamicMethod.java b/src/jdk/internal/dynalink/beans/CallerSensitiveDynamicMethod.java
index 5fceb1a7..2896732e 100644
--- a/src/jdk/internal/dynalink/beans/CallerSensitiveDynamicMethod.java
+++ b/src/jdk/internal/dynalink/beans/CallerSensitiveDynamicMethod.java
@@ -155,4 +155,9 @@ class CallerSensitiveDynamicMethod extends SingleDynamicMethod {
return StaticClassIntrospector.editConstructorMethodHandle(Lookup.unreflectConstructor(lookup,
(Constructor<?>)target));
}
+
+ @Override
+ boolean isConstructor() {
+ return target instanceof Constructor;
+ }
}
diff --git a/src/jdk/internal/dynalink/beans/DynamicMethod.java b/src/jdk/internal/dynalink/beans/DynamicMethod.java
index e72ca6dc..7b7a4d80 100644
--- a/src/jdk/internal/dynalink/beans/DynamicMethod.java
+++ b/src/jdk/internal/dynalink/beans/DynamicMethod.java
@@ -147,4 +147,13 @@ abstract class DynamicMethod {
public String toString() {
return "[" + getClass().getName() + " " + getName() + "]";
}
+
+ /**
+ * True if this method happens to be a constructor method.
+ *
+ * @return true if this represents a constructor.
+ */
+ boolean isConstructor() {
+ return false;
+ }
}
diff --git a/src/jdk/internal/dynalink/beans/DynamicMethodLinker.java b/src/jdk/internal/dynalink/beans/DynamicMethodLinker.java
index 08f2a258..7067c2ce 100644
--- a/src/jdk/internal/dynalink/beans/DynamicMethodLinker.java
+++ b/src/jdk/internal/dynalink/beans/DynamicMethodLinker.java
@@ -114,15 +114,30 @@ class DynamicMethodLinker implements TypeBasedGuardingDynamicLinker {
return null;
}
final String operator = desc.getNameToken(CallSiteDescriptor.OPERATOR);
- if(operator == "call") {
- final MethodHandle invocation = ((DynamicMethod)receiver).getInvocation(
+ final DynamicMethod dynMethod = (DynamicMethod)receiver;
+ final boolean constructor = dynMethod.isConstructor();
+ final MethodHandle invocation;
+
+ if (operator == "call" && !constructor) {
+ invocation = dynMethod.getInvocation(
CallSiteDescriptorFactory.dropParameterTypes(desc, 0, 1), linkerServices);
- if(invocation == null) {
+ } else if (operator == "new" && constructor) {
+ final MethodHandle ctorInvocation = dynMethod.getInvocation(desc, linkerServices);
+ if(ctorInvocation == null) {
return null;
}
+
+ // Insert null for StaticClass parameter
+ invocation = MethodHandles.insertArguments(ctorInvocation, 0, (Object)null);
+ } else {
+ return null;
+ }
+
+ if (invocation != null) {
return new GuardedInvocation(MethodHandles.dropArguments(invocation, 0,
- desc.getMethodType().parameterType(0)), Guards.getIdentityGuard(receiver));
+ desc.getMethodType().parameterType(0)), Guards.getIdentityGuard(receiver));
}
+
return null;
}
}
diff --git a/src/jdk/internal/dynalink/beans/OverloadedDynamicMethod.java b/src/jdk/internal/dynalink/beans/OverloadedDynamicMethod.java
index 618f71a6..83156e3b 100644
--- a/src/jdk/internal/dynalink/beans/OverloadedDynamicMethod.java
+++ b/src/jdk/internal/dynalink/beans/OverloadedDynamicMethod.java
@@ -148,7 +148,6 @@ class OverloadedDynamicMethod extends DynamicMethod {
}
}
- @SuppressWarnings("fallthrough")
@Override
public MethodHandle getInvocation(final CallSiteDescriptor callSiteDescriptor, final LinkerServices linkerServices) {
final MethodType callSiteType = callSiteDescriptor.getMethodType();
@@ -207,7 +206,7 @@ class OverloadedDynamicMethod extends DynamicMethod {
case 1: {
// Very lucky, we ended up with a single candidate method handle based on the call site signature; we
// can link it very simply by delegating to the SingleDynamicMethod.
- invokables.iterator().next().getInvocation(callSiteDescriptor, linkerServices);
+ return invokables.iterator().next().getInvocation(callSiteDescriptor, linkerServices);
}
default: {
// We have more than one candidate. We have no choice but to link to a method that resolves overloads on
@@ -237,6 +236,12 @@ class OverloadedDynamicMethod extends DynamicMethod {
return false;
}
+ @Override
+ public boolean isConstructor() {
+ assert !methods.isEmpty();
+ return methods.getFirst().isConstructor();
+ }
+
ClassLoader getClassLoader() {
return classLoader;
}
@@ -304,6 +309,11 @@ class OverloadedDynamicMethod extends DynamicMethod {
* @param method a method to add
*/
public void addMethod(final SingleDynamicMethod method) {
+ assert constructorFlagConsistent(method);
methods.add(method);
}
+
+ private boolean constructorFlagConsistent(final SingleDynamicMethod method) {
+ return methods.isEmpty()? true : (methods.getFirst().isConstructor() == method.isConstructor());
+ }
}
diff --git a/src/jdk/internal/dynalink/beans/OverloadedMethod.java b/src/jdk/internal/dynalink/beans/OverloadedMethod.java
index 95d91a92..70ec495a 100644
--- a/src/jdk/internal/dynalink/beans/OverloadedMethod.java
+++ b/src/jdk/internal/dynalink/beans/OverloadedMethod.java
@@ -93,6 +93,7 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import jdk.internal.dynalink.linker.LinkerServices;
import jdk.internal.dynalink.support.Lookup;
+import jdk.internal.dynalink.support.TypeUtilities;
/**
* Represents a subset of overloaded methods for a certain method name on a certain class. It can be either a fixarg or
@@ -114,13 +115,15 @@ class OverloadedMethod {
OverloadedMethod(final List<MethodHandle> methodHandles, final OverloadedDynamicMethod parent, final MethodType callSiteType,
final LinkerServices linkerServices) {
this.parent = parent;
- this.callSiteType = callSiteType;
+ final Class<?> commonRetType = getCommonReturnType(methodHandles);
+ this.callSiteType = callSiteType.changeReturnType(commonRetType);
this.linkerServices = linkerServices;
fixArgMethods = new ArrayList<>(methodHandles.size());
varArgMethods = new ArrayList<>(methodHandles.size());
final int argNum = callSiteType.parameterCount();
- for(final MethodHandle mh: methodHandles) {
+ for(MethodHandle mh: methodHandles) {
+ mh = mh.asType(mh.type().changeReturnType(commonRetType));
if(mh.isVarargsCollector()) {
final MethodHandle asFixed = mh.asFixedArity();
if(argNum == asFixed.type().parameterCount()) {
@@ -137,7 +140,7 @@ class OverloadedMethod {
final MethodHandle bound = SELECT_METHOD.bindTo(this);
final MethodHandle collecting = SingleDynamicMethod.collectArguments(bound, argNum).asType(
callSiteType.changeReturnType(MethodHandle.class));
- invoker = MethodHandles.foldArguments(MethodHandles.exactInvoker(callSiteType), collecting);
+ invoker = MethodHandles.foldArguments(MethodHandles.exactInvoker(this.callSiteType), collecting);
}
MethodHandle getInvoker() {
@@ -149,7 +152,7 @@ class OverloadedMethod {
@SuppressWarnings("unused")
private MethodHandle selectMethod(final Object[] args) throws NoSuchMethodException {
- final Class<?>[] argTypes = new Class[args.length];
+ final Class<?>[] argTypes = new Class<?>[args.length];
for(int i = 0; i < argTypes.length; ++i) {
final Object arg = args[i];
argTypes[i] = arg == null ? ClassString.NULL_CLASS : arg.getClass();
@@ -262,4 +265,13 @@ class OverloadedMethod {
b.append(classes[l - 1].getComponentType().getCanonicalName()).append("...");
}
}
+
+ private static Class<?> getCommonReturnType(final List<MethodHandle> methodHandles) {
+ final Iterator<MethodHandle> it = methodHandles.iterator();
+ Class<?> retType = it.next().type().returnType();
+ while(it.hasNext()) {
+ retType = TypeUtilities.getCommonLosslessConversionType(retType, it.next().type().returnType());
+ }
+ return retType;
+ }
}
diff --git a/src/jdk/internal/dynalink/beans/SimpleDynamicMethod.java b/src/jdk/internal/dynalink/beans/SimpleDynamicMethod.java
index 2965cb4e..f4a8b0a0 100644
--- a/src/jdk/internal/dynalink/beans/SimpleDynamicMethod.java
+++ b/src/jdk/internal/dynalink/beans/SimpleDynamicMethod.java
@@ -98,6 +98,7 @@ import java.lang.invoke.MethodType;
*/
class SimpleDynamicMethod extends SingleDynamicMethod {
private final MethodHandle target;
+ private final boolean constructor;
/**
* Creates a new simple dynamic method, with a name constructed from the class name, method name, and handle
@@ -108,8 +109,22 @@ class SimpleDynamicMethod extends SingleDynamicMethod {
* @param name the simple name of the method
*/
SimpleDynamicMethod(final MethodHandle target, final Class<?> clazz, final String name) {
+ this(target, clazz, name, false);
+ }
+
+ /**
+ * Creates a new simple dynamic method, with a name constructed from the class name, method name, and handle
+ * signature.
+ *
+ * @param target the target method handle
+ * @param clazz the class declaring the method
+ * @param name the simple name of the method
+ * @param constructor does this represent a constructor?
+ */
+ SimpleDynamicMethod(final MethodHandle target, final Class<?> clazz, final String name, final boolean constructor) {
super(getName(target, clazz, name));
this.target = target;
+ this.constructor = constructor;
}
private static String getName(final MethodHandle target, final Class<?> clazz, final String name) {
@@ -130,4 +145,9 @@ class SimpleDynamicMethod extends SingleDynamicMethod {
MethodHandle getTarget(final Lookup lookup) {
return target;
}
+
+ @Override
+ boolean isConstructor() {
+ return constructor;
+ }
}
diff --git a/src/jdk/internal/dynalink/beans/SingleDynamicMethod.java b/src/jdk/internal/dynalink/beans/SingleDynamicMethod.java
index 98edcfba..79dca9a3 100644
--- a/src/jdk/internal/dynalink/beans/SingleDynamicMethod.java
+++ b/src/jdk/internal/dynalink/beans/SingleDynamicMethod.java
@@ -156,7 +156,9 @@ abstract class SingleDynamicMethod extends DynamicMethod {
/**
* Given a method handle and a call site type, adapts the method handle to the call site type. Performs type
* conversions as needed using the specified linker services, and in case that the method handle is a vararg
- * collector, matches it to the arity of the call site.
+ * collector, matches it to the arity of the call site. The type of the return value is only changed if it can be
+ * converted using a conversion that loses neither precision nor magnitude, see
+ * {@link LinkerServices#asTypeLosslessReturn(MethodHandle, MethodType)}.
* @param target the method handle to adapt
* @param callSiteType the type of the call site
* @param linkerServices the linker services used for type conversions
@@ -286,7 +288,7 @@ abstract class SingleDynamicMethod extends DynamicMethod {
private static MethodHandle createConvertingInvocation(final MethodHandle sizedMethod,
final LinkerServices linkerServices, final MethodType callSiteType) {
- return linkerServices.asType(sizedMethod, callSiteType);
+ return linkerServices.asTypeLosslessReturn(sizedMethod, callSiteType);
}
private static boolean typeMatchesDescription(final String paramTypes, final MethodType type) {
diff --git a/src/jdk/internal/dynalink/beans/StaticClassLinker.java b/src/jdk/internal/dynalink/beans/StaticClassLinker.java
index 41955bb9..ab9c884a 100644
--- a/src/jdk/internal/dynalink/beans/StaticClassLinker.java
+++ b/src/jdk/internal/dynalink/beans/StaticClassLinker.java
@@ -160,6 +160,15 @@ class StaticClassLinker implements TypeBasedGuardingDynamicLinker {
}
return null;
}
+
+ @Override
+ SingleDynamicMethod getConstructorMethod(final String signature) {
+ return constructor != null? constructor.getMethodForExactParamTypes(signature) : null;
+ }
+ }
+
+ static Object getConstructorMethod(final Class<?> clazz, final String signature) {
+ return linkers.get(clazz).getConstructorMethod(signature);
}
static Collection<String> getReadableStaticPropertyNames(final Class<?> clazz) {
diff --git a/src/jdk/internal/dynalink/linker/GuardedInvocation.java b/src/jdk/internal/dynalink/linker/GuardedInvocation.java
index 6cf62d74..00ee9dc7 100644
--- a/src/jdk/internal/dynalink/linker/GuardedInvocation.java
+++ b/src/jdk/internal/dynalink/linker/GuardedInvocation.java
@@ -83,6 +83,8 @@
package jdk.internal.dynalink.linker;
+import static jdk.nashorn.internal.lookup.Lookup.MH;
+
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
@@ -104,7 +106,18 @@ import jdk.internal.dynalink.support.Guards;
public class GuardedInvocation {
private final MethodHandle invocation;
private final MethodHandle guard;
- private final SwitchPoint switchPoint;
+ private final Class<? extends Throwable> exception;
+ private final SwitchPoint[] switchPoints;
+
+ /**
+ * Creates a new guarded invocation. This invocation is unconditional as it has no invalidations.
+ *
+ * @param invocation the method handle representing the invocation. Must not be null.
+ * @throws NullPointerException if invocation is null.
+ */
+ public GuardedInvocation(final MethodHandle invocation) {
+ this(invocation, null, (SwitchPoint)null, null);
+ }
/**
* Creates a new guarded invocation.
@@ -116,7 +129,18 @@ public class GuardedInvocation {
* @throws NullPointerException if invocation is null.
*/
public GuardedInvocation(final MethodHandle invocation, final MethodHandle guard) {
- this(invocation, guard, null);
+ this(invocation, guard, (SwitchPoint)null, null);
+ }
+
+ /**
+ * Creates a new guarded invocation.
+ *
+ * @param invocation the method handle representing the invocation. Must not be null.
+ * @param switchPoint the optional switch point that can be used to invalidate this linkage.
+ * @throws NullPointerException if invocation is null.
+ */
+ public GuardedInvocation(final MethodHandle invocation, final SwitchPoint switchPoint) {
+ this(invocation, null, switchPoint, null);
}
/**
@@ -130,25 +154,49 @@ public class GuardedInvocation {
* @throws NullPointerException if invocation is null.
*/
public GuardedInvocation(final MethodHandle invocation, final MethodHandle guard, final SwitchPoint switchPoint) {
+ this(invocation, guard, switchPoint, null);
+ }
+
+ /**
+ * Creates a new guarded invocation.
+ *
+ * @param invocation the method handle representing the invocation. Must not be null.
+ * @param guard the method handle representing the guard. Must have the same method type as the invocation, except
+ * it must return boolean. For some useful guards, check out the {@link Guards} class. It can be null. If both it
+ * and the switch point are null, this represents an unconditional invocation, which is legal but unusual.
+ * @param switchPoint the optional switch point that can be used to invalidate this linkage.
+ * @param exception the optional exception type that is expected to be thrown by the invocation and that also
+ * invalidates the linkage.
+ * @throws NullPointerException if invocation is null.
+ */
+ public GuardedInvocation(final MethodHandle invocation, final MethodHandle guard, final SwitchPoint switchPoint, final Class<? extends Throwable> exception) {
invocation.getClass(); // NPE check
this.invocation = invocation;
this.guard = guard;
- this.switchPoint = switchPoint;
+ this.switchPoints = switchPoint == null ? null : new SwitchPoint[] { switchPoint };
+ this.exception = exception;
}
/**
- * Creates a new guarded invocation.
+ * Creates a new guarded invocation
*
* @param invocation the method handle representing the invocation. Must not be null.
- * @param switchPoint the optional switch point that can be used to invalidate this linkage.
* @param guard the method handle representing the guard. Must have the same method type as the invocation, except
* it must return boolean. For some useful guards, check out the {@link Guards} class. It can be null. If both it
* and the switch point are null, this represents an unconditional invocation, which is legal but unusual.
+ * @param switchPoints the optional switch points that can be used to invalidate this linkage.
+ * @param exception the optional exception type that is expected to be thrown by the invocation and that also
+ * invalidates the linkage.
* @throws NullPointerException if invocation is null.
*/
- public GuardedInvocation(final MethodHandle invocation, final SwitchPoint switchPoint, final MethodHandle guard) {
- this(invocation, guard, switchPoint);
+ public GuardedInvocation(final MethodHandle invocation, final MethodHandle guard, final SwitchPoint[] switchPoints, final Class<? extends Throwable> exception) {
+ invocation.getClass(); // NPE check
+ this.invocation = invocation;
+ this.guard = guard;
+ this.switchPoints = switchPoints == null ? null : switchPoints.clone();
+ this.exception = exception;
}
+
/**
* Returns the invocation method handle.
*
@@ -172,8 +220,17 @@ public class GuardedInvocation {
*
* @return the switch point that can be used to invalidate the invocation handle. Can be null.
*/
- public SwitchPoint getSwitchPoint() {
- return switchPoint;
+ public SwitchPoint[] getSwitchPoints() {
+ return switchPoints == null ? null : switchPoints.clone();
+ }
+
+ /**
+ * Returns the exception type that if thrown should be used to invalidate the linkage.
+ *
+ * @return the exception type that if thrown should be used to invalidate the linkage. Can be null.
+ */
+ public Class<? extends Throwable> getException() {
+ return exception;
}
/**
@@ -181,7 +238,15 @@ public class GuardedInvocation {
* @return true if and only if this guarded invocation has a switchpoint, and that switchpoint has been invalidated.
*/
public boolean hasBeenInvalidated() {
- return switchPoint != null && switchPoint.hasBeenInvalidated();
+ if (switchPoints == null) {
+ return false;
+ }
+ for (final SwitchPoint sp : switchPoints) {
+ if (sp.hasBeenInvalidated()) {
+ return true;
+ }
+ }
+ return false;
}
/**
@@ -193,7 +258,7 @@ public class GuardedInvocation {
*/
public void assertType(final MethodType type) {
assertType(invocation, type);
- if(guard != null) {
+ if (guard != null) {
assertType(guard, type.changeReturnType(Boolean.TYPE));
}
}
@@ -206,11 +271,33 @@ public class GuardedInvocation {
* @return a new guarded invocation with the replaced methods and the same switch point as this invocation.
*/
public GuardedInvocation replaceMethods(final MethodHandle newInvocation, final MethodHandle newGuard) {
- return new GuardedInvocation(newInvocation, newGuard, switchPoint);
+ return new GuardedInvocation(newInvocation, newGuard, switchPoints, exception);
+ }
+
+ /**
+ * Add a switchpoint to this guarded invocation
+ * @param newSwitchPoint new switchpoint, or null for nop
+ * @return new guarded invocation with the extra switchpoint
+ */
+ public GuardedInvocation addSwitchPoint(final SwitchPoint newSwitchPoint) {
+ if (newSwitchPoint == null) {
+ return this;
+ }
+
+ final SwitchPoint[] newSwitchPoints;
+ if (switchPoints != null) {
+ newSwitchPoints = new SwitchPoint[switchPoints.length + 1];
+ System.arraycopy(switchPoints, 0, newSwitchPoints, 0, switchPoints.length);
+ newSwitchPoints[switchPoints.length] = newSwitchPoint;
+ } else {
+ newSwitchPoints = new SwitchPoint[] { newSwitchPoint };
+ }
+
+ return new GuardedInvocation(invocation, guard, newSwitchPoints, exception);
}
private GuardedInvocation replaceMethodsOrThis(final MethodHandle newInvocation, final MethodHandle newGuard) {
- if(newInvocation == invocation && newGuard == guard) {
+ if (newInvocation == invocation && newGuard == guard) {
return this;
}
return replaceMethods(newInvocation, newGuard);
@@ -241,6 +328,20 @@ public class GuardedInvocation {
}
/**
+ * Changes the type of the invocation, as if {@link LinkerServices#asTypeLosslessReturn(MethodHandle, MethodType)} was
+ * applied to its invocation and {@link LinkerServices#asType(MethodHandle, MethodType)} applied to its guard, if it
+ * has one (with return type changed to boolean, and parameter count potentially truncated for the guard). If the
+ * invocation doesn't change its type, returns this object.
+ * @param linkerServices the linker services to use for the conversion
+ * @param newType the new type of the invocation.
+ * @return a guarded invocation with the new type applied to it.
+ */
+ public GuardedInvocation asTypeSafeReturn(final LinkerServices linkerServices, final MethodType newType) {
+ return replaceMethodsOrThis(linkerServices.asTypeLosslessReturn(invocation, newType), guard == null ? null :
+ Guards.asType(linkerServices, guard, newType));
+ }
+
+ /**
* Changes the type of the invocation, as if {@link MethodHandle#asType(MethodType)} was applied to its invocation
* and its guard, if it has one (with return type changed to boolean for guard). If the invocation already is of the
* required type, returns this object.
@@ -291,19 +392,46 @@ public class GuardedInvocation {
* @return a composite method handle.
*/
public MethodHandle compose(final MethodHandle fallback) {
- return compose(fallback, fallback);
+ return compose(fallback, fallback, fallback);
}
/**
* Composes the invocation, switchpoint, and the guard into a composite method handle that knows how to fall back.
* @param switchpointFallback the fallback method handle in case switchpoint is invalidated.
* @param guardFallback the fallback method handle in case guard returns false.
+ * @param catchFallback the fallback method in case the exception handler triggers
* @return a composite method handle.
*/
- public MethodHandle compose(final MethodHandle switchpointFallback, final MethodHandle guardFallback) {
+ public MethodHandle compose(final MethodHandle guardFallback, final MethodHandle switchpointFallback, final MethodHandle catchFallback) {
final MethodHandle guarded =
- guard == null ? invocation : MethodHandles.guardWithTest(guard, invocation, guardFallback);
- return switchPoint == null ? guarded : switchPoint.guardWithTest(guarded, switchpointFallback);
+ guard == null ?
+ invocation :
+ MethodHandles.guardWithTest(
+ guard,
+ invocation,
+ guardFallback);
+
+ final MethodHandle catchGuarded =
+ exception == null ?
+ guarded :
+ MH.catchException(
+ guarded,
+ exception,
+ MethodHandles.dropArguments(
+ catchFallback,
+ 0,
+ exception));
+
+ if (switchPoints == null) {
+ return catchGuarded;
+ }
+
+ MethodHandle spGuarded = catchGuarded;
+ for (final SwitchPoint sp : switchPoints) {
+ spGuarded = sp.guardWithTest(spGuarded, switchpointFallback);
+ }
+
+ return spGuarded;
}
private static void assertType(final MethodHandle mh, final MethodType type) {
diff --git a/src/jdk/internal/dynalink/linker/GuardedTypeConversion.java b/src/jdk/internal/dynalink/linker/GuardedTypeConversion.java
index 9baed7c0..88357ff0 100644
--- a/src/jdk/internal/dynalink/linker/GuardedTypeConversion.java
+++ b/src/jdk/internal/dynalink/linker/GuardedTypeConversion.java
@@ -83,19 +83,35 @@
package jdk.internal.dynalink.linker;
+/**
+ * Guarded type conversion
+ */
public class GuardedTypeConversion {
private final GuardedInvocation conversionInvocation;
private final boolean cacheable;
+ /**
+ * Constructor
+ * @param conversionInvocation guarded invocation for this type conversion
+ * @param cacheable is this invocation cacheable
+ */
public GuardedTypeConversion(final GuardedInvocation conversionInvocation, final boolean cacheable) {
this.conversionInvocation = conversionInvocation;
this.cacheable = cacheable;
}
+ /**
+ * Get the invocation
+ * @return invocation
+ */
public GuardedInvocation getConversionInvocation() {
return conversionInvocation;
}
+ /**
+ * Check if invocation is cacheable
+ * @return true if cachable, false otherwise
+ */
public boolean isCacheable() {
return cacheable;
}
diff --git a/src/jdk/internal/dynalink/linker/GuardingDynamicLinker.java b/src/jdk/internal/dynalink/linker/GuardingDynamicLinker.java
index 2cd0d0f0..82a36197 100644
--- a/src/jdk/internal/dynalink/linker/GuardingDynamicLinker.java
+++ b/src/jdk/internal/dynalink/linker/GuardingDynamicLinker.java
@@ -101,10 +101,16 @@ public interface GuardingDynamicLinker {
* @return a guarded invocation with a method handle suitable for the arguments, as well as a guard condition that
* if fails should trigger relinking. Must return null if it can't resolve the invocation. If the returned
* invocation is unconditional (which is actually quite rare), the guard in the return value can be null. The
- * invocation can also have a switch point for asynchronous invalidation of the linkage. If the linker does not
- * recognize any native language runtime contexts in arguments, or does recognize its own, but receives a call site
- * descriptor without its recognized context in the arguments, it should invoke
- * {@link LinkRequest#withoutRuntimeContext()} and link for that.
+ * invocation can also have a switch point for asynchronous invalidation of the linkage, as well as a
+ * {@link Throwable} subclass that describes an expected exception condition that also triggers relinking (often it
+ * is faster to rely on an infrequent but expected {@link ClassCastException} than on an always evaluated
+ * {@code instanceof} guard). If the linker does not recognize any native language runtime contexts in arguments, or
+ * does recognize its own, but receives a call site descriptor without its recognized context in the arguments, it
+ * should invoke {@link LinkRequest#withoutRuntimeContext()} and link for that. While the linker must produce an
+ * invocation with parameter types matching those in the call site descriptor of the link request, it should not try
+ * to match the return type expected at the call site except when it can do it with only the conversions that lose
+ * neither precision nor magnitude, see {@link LinkerServices#asTypeLosslessReturn(java.lang.invoke.MethodHandle,
+ * java.lang.invoke.MethodType)}.
* @throws Exception if the operation fails for whatever reason
*/
public GuardedInvocation getGuardedInvocation(LinkRequest linkRequest, LinkerServices linkerServices)
diff --git a/src/jdk/internal/dynalink/linker/LinkRequest.java b/src/jdk/internal/dynalink/linker/LinkRequest.java
index 1a82a4fa..63501cdc 100644
--- a/src/jdk/internal/dynalink/linker/LinkRequest.java
+++ b/src/jdk/internal/dynalink/linker/LinkRequest.java
@@ -101,6 +101,17 @@ public interface LinkRequest {
public CallSiteDescriptor getCallSiteDescriptor();
/**
+ * Returns the call site token for the call site being linked. This token is an opaque object that is guaranteed to
+ * have different identity for different call sites, and is also guaranteed to not become weakly reachable before
+ * the call site does and to become weakly reachable some time after the call site does. This makes it ideal as a
+ * candidate for a key in a weak hash map in which a linker might want to keep per-call site linking state (usually
+ * profiling information).
+ *
+ * @return the call site token for the call site being linked.
+ */
+ public Object getCallSiteToken();
+
+ /**
* Returns the arguments for the invocation being linked. The returned array is a clone; modifications to it won't
* affect the arguments in this request.
*
@@ -116,6 +127,17 @@ public interface LinkRequest {
public Object getReceiver();
/**
+ * Returns the number of times this callsite has been linked/relinked. This can be useful if you want to
+ * change e.g. exception based relinking to guard based relinking. It's probably not a good idea to keep,
+ * for example, expensive exception throwing relinkage based on failed type checks/ClassCastException in
+ * a nested callsite tree where the exception is thrown repeatedly for the common case. There it would be
+ * much more performant to use exact type guards instead.
+ *
+ * @return link count for call site
+ */
+ public int getLinkCount();
+
+ /**
* Returns true if the call site is considered unstable, that is, it has been relinked more times than was
* specified in {@link DynamicLinkerFactory#setUnstableRelinkThreshold(int)}. Linkers should use this as a
* hint to prefer producing linkage that is more stable (its guard fails less frequently), even if that assumption
diff --git a/src/jdk/internal/dynalink/linker/LinkerServices.java b/src/jdk/internal/dynalink/linker/LinkerServices.java
index deaf820f..9e35d7f6 100644
--- a/src/jdk/internal/dynalink/linker/LinkerServices.java
+++ b/src/jdk/internal/dynalink/linker/LinkerServices.java
@@ -87,7 +87,9 @@ import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import jdk.internal.dynalink.DynamicLinker;
+import jdk.internal.dynalink.DynamicLinkerFactory;
import jdk.internal.dynalink.linker.ConversionComparator.Comparison;
+import jdk.internal.dynalink.support.TypeUtilities;
/**
* Interface for services provided to {@link GuardingDynamicLinker} instances by the {@link DynamicLinker} that owns
@@ -103,18 +105,34 @@ public interface LinkerServices {
* 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. It doesn't use language-specific conversions on
- * the return type.
+ * provided by {@link GuardingTypeConverterFactory} implementations.
*
* @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
- * {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)} with
- * {@link GuardingTypeConverterFactory} produced type converters as filters.
+ * @return a method handle that is a suitable combination of {@link MethodHandle#asType(MethodType)},
+ * {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)}, and
+ * {@link MethodHandles#filterReturnValue(MethodHandle, MethodHandle)} with
+ * {@link GuardingTypeConverterFactory}-produced type converters as filters.
*/
public MethodHandle asType(MethodHandle handle, MethodType fromType);
/**
+ * Similar to {@link #asType(MethodHandle, MethodType)} except it only converts the return type of the method handle
+ * when it can be done using a conversion that loses neither precision nor magnitude, otherwise it leaves it
+ * unchanged. The idea is that other conversions should not be performed by individual linkers, but instead the
+ * {@link DynamicLinkerFactory#setPrelinkFilter(jdk.internal.dynalink.GuardedInvocationFilter) pre-link filter of
+ * the dynamic linker} should implement the strategy of dealing with potentially lossy return type conversions in a
+ * manner specific to the language runtime.
+ *
+ * @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
+ * {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)} with
+ * {@link GuardingTypeConverterFactory}-produced type converters as filters.
+ */
+ public MethodHandle asTypeLosslessReturn(MethodHandle handle, MethodType fromType);
+
+ /**
* Given a source and target type, returns a method handle that converts between them. Never returns null; in worst
* case it will return an identity conversion (that might fail for some values at runtime). You rarely need to use
* this method directly; you should mostly rely on {@link #asType(MethodHandle, MethodType)} instead. You really
@@ -161,4 +179,23 @@ public interface LinkerServices {
* conversion.
*/
public Comparison compareConversion(Class<?> sourceType, Class<?> targetType1, Class<?> targetType2);
+
+ /**
+ * If we could just use Java 8 constructs, then {@code asTypeSafeReturn} would be a method with default
+ * implementation. Since we can't do that, we extract common default implementations into this static class.
+ */
+ public static class Implementation {
+ /**
+ * Default implementation for {@link LinkerServices#asTypeLosslessReturn(MethodHandle, MethodType)}.
+ * @param linkerServices the linker services that delegates to this implementation
+ * @param handle the passed handle
+ * @param fromType the passed type
+ * @return the converted method handle, as per the {@code asTypeSafeReturn} semantics.
+ */
+ public static MethodHandle asTypeLosslessReturn(final LinkerServices linkerServices, final MethodHandle handle, final MethodType fromType) {
+ final Class<?> handleReturnType = handle.type().returnType();
+ return linkerServices.asType(handle, TypeUtilities.isConvertibleWithoutLoss(handleReturnType, fromType.returnType()) ?
+ fromType : fromType.changeReturnType(handleReturnType));
+ }
+ }
}
diff --git a/src/jdk/internal/dynalink/support/CallSiteDescriptorFactory.java b/src/jdk/internal/dynalink/support/CallSiteDescriptorFactory.java
index 0e7af404..9cbd5f04 100644
--- a/src/jdk/internal/dynalink/support/CallSiteDescriptorFactory.java
+++ b/src/jdk/internal/dynalink/support/CallSiteDescriptorFactory.java
@@ -86,6 +86,7 @@ package jdk.internal.dynalink.support;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
+import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.Collections;
@@ -103,7 +104,7 @@ import jdk.internal.dynalink.CallSiteDescriptor;
* @author Attila Szegedi
*/
public class CallSiteDescriptorFactory {
- private static final WeakHashMap<CallSiteDescriptor, WeakReference<CallSiteDescriptor>> publicDescs =
+ private static final WeakHashMap<CallSiteDescriptor, Reference<CallSiteDescriptor>> publicDescs =
new WeakHashMap<>();
@@ -134,18 +135,27 @@ public class CallSiteDescriptorFactory {
static CallSiteDescriptor getCanonicalPublicDescriptor(final CallSiteDescriptor desc) {
synchronized(publicDescs) {
- final WeakReference<CallSiteDescriptor> ref = publicDescs.get(desc);
+ final Reference<CallSiteDescriptor> ref = publicDescs.get(desc);
if(ref != null) {
final CallSiteDescriptor canonical = ref.get();
if(canonical != null) {
return canonical;
}
}
- publicDescs.put(desc, new WeakReference<>(desc));
+ publicDescs.put(desc, createReference(desc));
}
return desc;
}
+ /**
+ * Override this to use a different kind of references for the cache
+ * @param desc desc
+ * @return reference
+ */
+ protected static Reference<CallSiteDescriptor> createReference(final CallSiteDescriptor desc) {
+ return new WeakReference<>(desc);
+ }
+
private static CallSiteDescriptor createPublicCallSiteDescriptor(final String[] tokenizedName, final MethodType methodType) {
final int l = tokenizedName.length;
if(l > 0 && tokenizedName[0] == "dyn") {
diff --git a/src/jdk/internal/dynalink/support/CompositeTypeBasedGuardingDynamicLinker.java b/src/jdk/internal/dynalink/support/CompositeTypeBasedGuardingDynamicLinker.java
index 814fc693..d1ff0280 100644
--- a/src/jdk/internal/dynalink/support/CompositeTypeBasedGuardingDynamicLinker.java
+++ b/src/jdk/internal/dynalink/support/CompositeTypeBasedGuardingDynamicLinker.java
@@ -111,7 +111,7 @@ public class CompositeTypeBasedGuardingDynamicLinker implements TypeBasedGuardin
private final TypeBasedGuardingDynamicLinker[] linkers;
private final List<TypeBasedGuardingDynamicLinker>[] singletonLinkers;
- @SuppressWarnings("unchecked")
+ @SuppressWarnings({"unchecked", "rawtypes"})
ClassToLinker(final TypeBasedGuardingDynamicLinker[] linkers) {
this.linkers = linkers;
singletonLinkers = new List[linkers.length];
@@ -120,6 +120,7 @@ public class CompositeTypeBasedGuardingDynamicLinker implements TypeBasedGuardin
}
}
+ @SuppressWarnings("fallthrough")
@Override
protected List<TypeBasedGuardingDynamicLinker> computeValue(final Class<?> clazz) {
List<TypeBasedGuardingDynamicLinker> list = NO_LINKER;
@@ -134,7 +135,6 @@ public class CompositeTypeBasedGuardingDynamicLinker implements TypeBasedGuardin
case 1: {
list = new LinkedList<>(list);
}
- //$FALL-THROUGH$
default: {
list.add(linker);
}
diff --git a/src/jdk/internal/dynalink/support/DefaultPrelinkFilter.java b/src/jdk/internal/dynalink/support/DefaultPrelinkFilter.java
new file mode 100644
index 00000000..ad9679f6
--- /dev/null
+++ b/src/jdk/internal/dynalink/support/DefaultPrelinkFilter.java
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ */
+
+/*
+ * 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 2009-2013 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.support;
+
+import jdk.internal.dynalink.GuardedInvocationFilter;
+import jdk.internal.dynalink.linker.GuardedInvocation;
+import jdk.internal.dynalink.linker.LinkRequest;
+import jdk.internal.dynalink.linker.LinkerServices;
+
+/**
+ * Default filter for guarded invocation pre link filtering
+ */
+public class DefaultPrelinkFilter implements GuardedInvocationFilter {
+ @Override
+ public GuardedInvocation filter(final GuardedInvocation inv, final LinkRequest request, final LinkerServices linkerServices) {
+ return inv.asType(linkerServices, request.getCallSiteDescriptor().getMethodType());
+ }
+}
diff --git a/src/jdk/internal/dynalink/support/LinkRequestImpl.java b/src/jdk/internal/dynalink/support/LinkRequestImpl.java
index 0f967ed3..2d4c0b18 100644
--- a/src/jdk/internal/dynalink/support/LinkRequestImpl.java
+++ b/src/jdk/internal/dynalink/support/LinkRequestImpl.java
@@ -95,18 +95,24 @@ import jdk.internal.dynalink.linker.LinkRequest;
public class LinkRequestImpl implements LinkRequest {
private final CallSiteDescriptor callSiteDescriptor;
+ private final Object callSiteToken;
private final Object[] arguments;
private final boolean callSiteUnstable;
+ private final int linkCount;
/**
* Creates a new link request.
*
* @param callSiteDescriptor the descriptor for the call site being linked
+ * @param callSiteToken the opaque token for the call site being linked.
+ * @param linkCount how many times this callsite has been linked/relinked
* @param callSiteUnstable true if the call site being linked is considered unstable
* @param arguments the arguments for the invocation
*/
- public LinkRequestImpl(final CallSiteDescriptor callSiteDescriptor, final boolean callSiteUnstable, final Object... arguments) {
+ public LinkRequestImpl(final CallSiteDescriptor callSiteDescriptor, final Object callSiteToken, final int linkCount, final boolean callSiteUnstable, final Object... arguments) {
this.callSiteDescriptor = callSiteDescriptor;
+ this.callSiteToken = callSiteToken;
+ this.linkCount = linkCount;
this.callSiteUnstable = callSiteUnstable;
this.arguments = arguments;
}
@@ -127,17 +133,27 @@ public class LinkRequestImpl implements LinkRequest {
}
@Override
+ public Object getCallSiteToken() {
+ return callSiteToken;
+ }
+
+ @Override
public boolean isCallSiteUnstable() {
return callSiteUnstable;
}
@Override
+ public int getLinkCount() {
+ return linkCount;
+ }
+
+ @Override
public LinkRequest withoutRuntimeContext() {
return this;
}
@Override
public LinkRequest replaceArguments(final CallSiteDescriptor newCallSiteDescriptor, final Object[] newArguments) {
- return new LinkRequestImpl(newCallSiteDescriptor, callSiteUnstable, newArguments);
+ return new LinkRequestImpl(newCallSiteDescriptor, callSiteToken, linkCount, callSiteUnstable, newArguments);
}
}
diff --git a/src/jdk/internal/dynalink/support/LinkerServicesImpl.java b/src/jdk/internal/dynalink/support/LinkerServicesImpl.java
index b8be7e2c..38d93c84 100644
--- a/src/jdk/internal/dynalink/support/LinkerServicesImpl.java
+++ b/src/jdk/internal/dynalink/support/LinkerServicesImpl.java
@@ -127,6 +127,11 @@ public class LinkerServicesImpl implements LinkerServices {
}
@Override
+ public MethodHandle asTypeLosslessReturn(final MethodHandle handle, final MethodType fromType) {
+ return Implementation.asTypeLosslessReturn(this, handle, fromType);
+ }
+
+ @Override
public MethodHandle getTypeConverter(final Class<?> sourceType, final Class<?> targetType) {
return typeConverterFactory.getTypeConverter(sourceType, targetType);
}
diff --git a/src/jdk/internal/dynalink/support/RuntimeContextLinkRequestImpl.java b/src/jdk/internal/dynalink/support/RuntimeContextLinkRequestImpl.java
index cccbaf39..3f43621a 100644
--- a/src/jdk/internal/dynalink/support/RuntimeContextLinkRequestImpl.java
+++ b/src/jdk/internal/dynalink/support/RuntimeContextLinkRequestImpl.java
@@ -101,15 +101,17 @@ public class RuntimeContextLinkRequestImpl extends LinkRequestImpl {
* Creates a new link request.
*
* @param callSiteDescriptor the descriptor for the call site being linked
+ * @param callSiteToken the opaque token for the call site being linked.
* @param arguments the arguments for the invocation
+ * @param linkCount number of times callsite has been linked/relinked
* @param callSiteUnstable true if the call site being linked is considered unstable
* @param runtimeContextArgCount the number of the leading arguments on the stack that represent the language
* runtime specific context arguments.
* @throws IllegalArgumentException if runtimeContextArgCount is less than 1.
*/
- public RuntimeContextLinkRequestImpl(final CallSiteDescriptor callSiteDescriptor, final boolean callSiteUnstable,
- final Object[] arguments, final int runtimeContextArgCount) {
- super(callSiteDescriptor, callSiteUnstable, arguments);
+ public RuntimeContextLinkRequestImpl(final CallSiteDescriptor callSiteDescriptor, final Object callSiteToken,
+ final int linkCount, final boolean callSiteUnstable, final Object[] arguments, final int runtimeContextArgCount) {
+ super(callSiteDescriptor, callSiteToken, linkCount, callSiteUnstable, arguments);
if(runtimeContextArgCount < 1) {
throw new IllegalArgumentException("runtimeContextArgCount < 1");
}
@@ -121,14 +123,14 @@ public class RuntimeContextLinkRequestImpl extends LinkRequestImpl {
if(contextStrippedRequest == null) {
contextStrippedRequest =
new LinkRequestImpl(CallSiteDescriptorFactory.dropParameterTypes(getCallSiteDescriptor(), 1,
- runtimeContextArgCount + 1), isCallSiteUnstable(), getTruncatedArguments());
+ runtimeContextArgCount + 1), getCallSiteToken(), getLinkCount(), isCallSiteUnstable(), getTruncatedArguments());
}
return contextStrippedRequest;
}
@Override
public LinkRequest replaceArguments(final CallSiteDescriptor callSiteDescriptor, final Object[] arguments) {
- return new RuntimeContextLinkRequestImpl(callSiteDescriptor, isCallSiteUnstable(), arguments,
+ return new RuntimeContextLinkRequestImpl(callSiteDescriptor, getCallSiteToken(), getLinkCount(), isCallSiteUnstable(), arguments,
runtimeContextArgCount);
}
diff --git a/src/jdk/internal/dynalink/support/TypeConverterFactory.java b/src/jdk/internal/dynalink/support/TypeConverterFactory.java
index 5b3d1a1e..79f6549b 100644
--- a/src/jdk/internal/dynalink/support/TypeConverterFactory.java
+++ b/src/jdk/internal/dynalink/support/TypeConverterFactory.java
@@ -372,6 +372,7 @@ public class TypeConverterFactory {
/*private*/ static final MethodHandle IDENTITY_CONVERSION = MethodHandles.identity(Object.class);
+ @SuppressWarnings("serial")
private static class NotCacheableConverter extends RuntimeException {
final MethodHandle converter;
diff --git a/src/jdk/internal/dynalink/support/TypeUtilities.java b/src/jdk/internal/dynalink/support/TypeUtilities.java
index d84c93e9..6403f3d5 100644
--- a/src/jdk/internal/dynalink/support/TypeUtilities.java
+++ b/src/jdk/internal/dynalink/support/TypeUtilities.java
@@ -106,38 +106,49 @@ public class TypeUtilities {
}
/**
- * Given two types represented by c1 and c2, returns a type that is their most specific common superclass or
- * superinterface.
+ * Given two types represented by c1 and c2, returns a type that is their most specific common supertype for
+ * purposes of lossless conversions.
*
* @param c1 one type
* @param c2 another type
- * @return their most common superclass or superinterface. If they have several unrelated superinterfaces as their
- * most specific common type, or the types themselves are completely unrelated interfaces, {@link java.lang.Object}
- * is returned.
+ * @return their most common superclass or superinterface for purposes of lossless conversions. If they have several
+ * unrelated superinterfaces as their most specific common type, or the types themselves are completely
+ * unrelated interfaces, {@link java.lang.Object} is returned.
*/
- public static Class<?> getMostSpecificCommonType(final Class<?> c1, final Class<?> c2) {
+ public static Class<?> getCommonLosslessConversionType(final Class<?> c1, final Class<?> c2) {
if(c1 == c2) {
return c1;
+ } 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;
}
- Class<?> c3 = c2;
- if(c3.isPrimitive()) {
- if(c3 == Byte.TYPE)
- c3 = Byte.class;
- else if(c3 == Short.TYPE)
- c3 = Short.class;
- else if(c3 == Character.TYPE)
- c3 = Character.class;
- else if(c3 == Integer.TYPE)
- c3 = Integer.class;
- else if(c3 == Float.TYPE)
- c3 = Float.class;
- else if(c3 == Long.TYPE)
- c3 = Long.class;
- else if(c3 == Double.TYPE)
- c3 = Double.class;
- }
- final Set<Class<?>> a1 = getAssignables(c1, c3);
- final Set<Class<?>> a2 = getAssignables(c3, c1);
+ if(c1.isPrimitive() && c2.isPrimitive()) {
+ if((c1 == byte.class && c2 == char.class) || (c1 == char.class && c2 == byte.class)) {
+ // byte + char = int
+ return int.class;
+ } else if((c1 == short.class && c2 == char.class) || (c1 == char.class && c2 == short.class)) {
+ // short + char = int
+ return int.class;
+ } else if((c1 == int.class && c2 == float.class) || (c1 == float.class && c2 == int.class)) {
+ // int + float = double
+ return double.class;
+ }
+ }
+ // For all other cases. This will handle long + (float|double) = Number case as well as boolean + anything = Object case too.
+ return getMostSpecificCommonTypeUnequalNonprimitives(c1, c2);
+ }
+
+ private static Class<?> getMostSpecificCommonTypeUnequalNonprimitives(final Class<?> c1, final Class<?> c2) {
+ final Class<?> npc1 = c1.isPrimitive() ? getWrapperType(c1) : c1;
+ final Class<?> npc2 = c2.isPrimitive() ? getWrapperType(c2) : c2;
+ final Set<Class<?>> a1 = getAssignables(npc1, npc2);
+ final Set<Class<?>> a2 = getAssignables(npc2, npc1);
a1.retainAll(a2);
if(a1.isEmpty()) {
// Can happen when at least one of the arguments is an interface,
@@ -168,7 +179,7 @@ public class TypeUtilities {
max.add(clazz);
}
if(max.size() > 1) {
- return OBJECT_CLASS;
+ return Object.class;
}
return max.get(0);
}
@@ -232,30 +243,59 @@ public class TypeUtilities {
* {@link #isSubtype(Class, Class)}) as well as boxing conversion (JLS 5.1.7) optionally followed by widening
* reference conversion and unboxing conversion (JLS 5.1.8) optionally followed by widening primitive conversion.
*
- * @param callSiteType the parameter type at the call site
- * @param methodType the parameter type in the method declaration
- * @return true if callSiteType is method invocation convertible to the methodType.
+ * @param sourceType the type being converted from (call site type for parameter types, method type for return types)
+ * @param targetType the parameter type being converted to (method type for parameter types, call site type for return types)
+ * @return true if source type is method invocation convertible to target type.
*/
- public static boolean isMethodInvocationConvertible(final Class<?> callSiteType, final Class<?> methodType) {
- if(methodType.isAssignableFrom(callSiteType)) {
+ public static boolean isMethodInvocationConvertible(final Class<?> sourceType, final Class<?> targetType) {
+ if(targetType.isAssignableFrom(sourceType)) {
return true;
}
- if(callSiteType.isPrimitive()) {
- if(methodType.isPrimitive()) {
- return isProperPrimitiveSubtype(callSiteType, methodType);
+ if(sourceType.isPrimitive()) {
+ if(targetType.isPrimitive()) {
+ return isProperPrimitiveSubtype(sourceType, targetType);
}
// Boxing + widening reference conversion
- return methodType.isAssignableFrom(WRAPPER_TYPES.get(callSiteType));
+ assert WRAPPER_TYPES.get(sourceType) != null : sourceType.getName();
+ return targetType.isAssignableFrom(WRAPPER_TYPES.get(sourceType));
}
- if(methodType.isPrimitive()) {
- final Class<?> unboxedCallSiteType = PRIMITIVE_TYPES.get(callSiteType);
+ if(targetType.isPrimitive()) {
+ final Class<?> unboxedCallSiteType = PRIMITIVE_TYPES.get(sourceType);
return unboxedCallSiteType != null
- && (unboxedCallSiteType == methodType || isProperPrimitiveSubtype(unboxedCallSiteType, methodType));
+ && (unboxedCallSiteType == targetType || isProperPrimitiveSubtype(unboxedCallSiteType, targetType));
}
return false;
}
/**
+ * Determines whether a type can be converted to another without losing any
+ * precision.
+ *
+ * @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)) {
+ return true;
+ }
+ if(sourceType.isPrimitive()) {
+ if(sourceType == void.class) {
+ return false; // Void can't be losslessly represented by any type
+ }
+ if(targetType.isPrimitive()) {
+ return isProperPrimitiveLosslessSubtype(sourceType, targetType);
+ }
+ // Boxing + widening reference conversion
+ assert WRAPPER_TYPES.get(sourceType) != null : sourceType.getName();
+ return targetType.isAssignableFrom(WRAPPER_TYPES.get(sourceType));
+ }
+ // Can't convert from any non-primitive type to any primitive type without data loss because of null.
+ // Also, can't convert non-assignable reference types.
+ return false;
+ }
+
+ /**
* Determines whether one type can be potentially converted to another type at runtime. Allows a conversion between
* any subtype and supertype in either direction, and also allows a conversion between any two primitive types, as
* well as between any primitive type and any reference type that can hold a boxed primitive.
@@ -266,7 +306,7 @@ public class TypeUtilities {
*/
public static boolean isPotentiallyConvertible(final Class<?> callSiteType, final Class<?> methodType) {
// Widening or narrowing reference conversion
- if(methodType.isAssignableFrom(callSiteType) || callSiteType.isAssignableFrom(methodType)) {
+ if(areAssignable(callSiteType, methodType)) {
return true;
}
if(callSiteType.isPrimitive()) {
@@ -287,6 +327,16 @@ public class TypeUtilities {
}
/**
+ * Returns true if either of the types is assignable from the other.
+ * @param c1 one of the types
+ * @param c2 another one of the types
+ * @return true if either c1 is assignable from c2 or c2 is assignable from c1.
+ */
+ public static boolean areAssignable(final Class<?> c1, final Class<?> c2) {
+ return c1.isAssignableFrom(c2) || c2.isAssignableFrom(c1);
+ }
+
+ /**
* Determines whether one type is a subtype of another type, as per JLS 4.10 "Subtyping". Note: this is not strict
* or proper subtype, therefore true is also returned for identical types; to be completely precise, it allows
* identity conversion (JLS 5.1.1), widening primitive conversion (JLS 5.1.2) and widening reference conversion (JLS
@@ -353,6 +403,37 @@ public class TypeUtilities {
return false;
}
+ /**
+ * Similar to {@link #isProperPrimitiveSubtype(Class, Class)}, except it disallows conversions from int and long to
+ * float, and from long to double, as those can lose precision. It also disallows conversion from and to char and
+ * anything else (similar to boolean) as char is not meant to be an arithmetic type.
+ * @param subType the supposed subtype
+ * @param superType the supposed supertype
+ * @return true if subType is a proper (not identical to) primitive subtype of the superType that can be represented
+ * by the supertype without no precision loss.
+ */
+ private static boolean isProperPrimitiveLosslessSubtype(final Class<?> subType, final Class<?> superType) {
+ if(superType == boolean.class || subType == boolean.class) {
+ return false;
+ }
+ if(superType == char.class || subType == char.class) {
+ return false;
+ }
+ if(subType == byte.class) {
+ return true;
+ }
+ if(subType == short.class) {
+ return superType != byte.class;
+ }
+ if(subType == int.class) {
+ return superType == long.class || superType == double.class;
+ }
+ if(subType == float.class) {
+ return superType == double.class;
+ }
+ return false;
+ }
+
private static final Map<Class<?>, Class<?>> WRAPPER_TO_PRIMITIVE_TYPES = createWrapperToPrimitiveTypes();
private static Map<Class<?>, Class<?>> createWrapperToPrimitiveTypes() {
diff --git a/src/jdk/internal/dynalink/support/messages.properties b/src/jdk/internal/dynalink/support/messages.properties
index 88d59908..ed26299e 100644
--- a/src/jdk/internal/dynalink/support/messages.properties
+++ b/src/jdk/internal/dynalink/support/messages.properties
@@ -83,4 +83,4 @@ isOfClassGuardAlwaysTrue=isOfClass guard for {0} in position {1} in method type
isOfClassGuardAlwaysFalse=isOfClass guard for {0} in position {1} in method type {2} at {3} will always return false
isArrayGuardAlwaysTrue=isArray guard in position {0} in method type {1} at {2} will always return true
-isArrayGuardAlwaysFalse=isArray guard in position {0} in method type {1} at {2} will always return false \ No newline at end of file
+isArrayGuardAlwaysFalse=isArray guard in position {0} in method type {1} at {2} will always return false
diff --git a/src/jdk/nashorn/api/scripting/NashornException.java b/src/jdk/nashorn/api/scripting/NashornException.java
index a5f8c24a..098f8508 100644
--- a/src/jdk/nashorn/api/scripting/NashornException.java
+++ b/src/jdk/nashorn/api/scripting/NashornException.java
@@ -53,9 +53,6 @@ public abstract class NashornException extends RuntimeException {
// underlying ECMA error object - lazily initialized
private Object ecmaError;
- /** script source name used for "engine.js" */
- public static final String ENGINE_SCRIPT_SOURCE_NAME = "nashorn:engine/resources/engine.js";
-
/**
* Constructor
*
@@ -182,7 +179,7 @@ public abstract class NashornException extends RuntimeException {
if (ECMAErrors.isScriptFrame(st)) {
final String className = "<" + st.getFileName() + ">";
String methodName = st.getMethodName();
- if (methodName.equals(CompilerConstants.RUN_SCRIPT.symbolName())) {
+ if (methodName.equals(CompilerConstants.PROGRAM.symbolName())) {
methodName = "<program>";
}
@@ -224,10 +221,22 @@ public abstract class NashornException extends RuntimeException {
return buf.toString();
}
+ /**
+ * Get the thrown object. Subclass responsibility
+ * @return thrown object
+ */
protected Object getThrown() {
return null;
}
+ /**
+ * Initialization function for ECMA errors. Stores the error
+ * in the ecmaError field of this class. It is only initialized
+ * once, and then reused
+ *
+ * @param global the global
+ * @return initialized exception
+ */
protected NashornException initEcmaError(final ScriptObject global) {
if (ecmaError != null) {
return this; // initialized already!
diff --git a/src/jdk/nashorn/api/scripting/NashornScriptEngine.java b/src/jdk/nashorn/api/scripting/NashornScriptEngine.java
index 6f09e963..ce2b83ae 100644
--- a/src/jdk/nashorn/api/scripting/NashornScriptEngine.java
+++ b/src/jdk/nashorn/api/scripting/NashornScriptEngine.java
@@ -25,8 +25,6 @@
package jdk.nashorn.api.scripting;
-import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError;
-import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
import static jdk.nashorn.internal.runtime.Source.sourceFor;
import java.io.IOException;
@@ -34,13 +32,10 @@ import java.io.Reader;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
-import java.net.URL;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.Permissions;
import java.security.PrivilegedAction;
-import java.security.PrivilegedActionException;
-import java.security.PrivilegedExceptionAction;
import java.security.ProtectionDomain;
import java.text.MessageFormat;
import java.util.Locale;
@@ -58,7 +53,6 @@ import javax.script.SimpleBindings;
import jdk.nashorn.internal.objects.Global;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.ErrorManager;
-import jdk.nashorn.internal.runtime.Property;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.ScriptRuntime;
@@ -98,9 +92,6 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
// This is the initial default Nashorn global object.
// This is used as "shared" global if above option is true.
private final Global global;
- // initialized bit late to be made 'final'.
- // Property object for "context" property of global object.
- private volatile Property contextProperty;
// default options passed to Nashorn Options object
private static final String[] DEFAULT_OPTIONS = new String[] { "-doe" };
@@ -122,31 +113,6 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
}
}
- // load engine.js
- @SuppressWarnings("resource")
- private static Source loadEngineJSSource() {
- final String script = "resources/engine.js";
- try {
- return AccessController.doPrivileged(
- new PrivilegedExceptionAction<Source>() {
- @Override
- public Source run() throws IOException {
- final URL url = NashornScriptEngine.class.getResource(script);
- return sourceFor(NashornException.ENGINE_SCRIPT_SOURCE_NAME, url);
- }
- }
- );
- } catch (final PrivilegedActionException e) {
- if (Context.DEBUG) {
- e.printStackTrace();
- }
- throw new RuntimeException(e);
- }
- }
-
- // Source object for engine.js
- private static final Source ENGINE_SCRIPT_SRC = loadEngineJSSource();
-
NashornScriptEngine(final NashornScriptEngineFactory factory, final ClassLoader appLoader) {
this(factory, DEFAULT_OPTIONS, appLoader);
}
@@ -249,33 +215,6 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
return getInterfaceInner(thiz, clazz);
}
- // These are called from the "engine.js" script
-
- /**
- * This hook is used to search js global variables exposed from Java code.
- *
- * @param self 'this' passed from the script
- * @param ctxt current ScriptContext in which name is searched
- * @param name name of the variable searched
- * @return the value of the named variable
- */
- public Object __noSuchProperty__(final Object self, final ScriptContext ctxt, final String name) {
- if (ctxt != null) {
- final int scope = ctxt.getAttributesScope(name);
- final Global ctxtGlobal = getNashornGlobalFrom(ctxt);
- if (scope != -1) {
- return ScriptObjectMirror.unwrap(ctxt.getAttribute(name, scope), ctxtGlobal);
- }
-
- if (self == UNDEFINED) {
- // scope access and so throw ReferenceError
- throw referenceError(ctxtGlobal, "not.defined", name);
- }
- }
-
- return UNDEFINED;
- }
-
// Implementation only below this point
private static Source makeSource(final Reader reader, final ScriptContext ctxt) throws ScriptException {
@@ -427,45 +366,10 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
}
}, CREATE_GLOBAL_ACC_CTXT);
- nashornContext.initGlobal(newGlobal);
-
- final int NON_ENUMERABLE_CONSTANT = Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE | Property.NOT_WRITABLE;
- // current ScriptContext exposed as "context"
- // "context" is non-writable from script - but script engine still
- // needs to set it and so save the context Property object
- contextProperty = newGlobal.addOwnProperty("context", NON_ENUMERABLE_CONSTANT, ctxt);
- // current ScriptEngine instance exposed as "engine". We added @SuppressWarnings("LeakingThisInConstructor") as
- // NetBeans identifies this assignment as such a leak - this is a false positive as we're setting this property
- // in the Global of a Context we just created - both the Context and the Global were just created and can not be
- // seen from another thread outside of this constructor.
- newGlobal.addOwnProperty("engine", NON_ENUMERABLE_CONSTANT, this);
- // global script arguments with undefined value
- newGlobal.addOwnProperty("arguments", Property.NOT_ENUMERABLE, UNDEFINED);
- // file name default is null
- newGlobal.addOwnProperty(ScriptEngine.FILENAME, Property.NOT_ENUMERABLE, null);
- // evaluate engine.js initialization script this new global object
- try {
- evalImpl(compileImpl(ENGINE_SCRIPT_SRC, newGlobal), ctxt, newGlobal);
- } catch (final ScriptException exp) {
- throw new RuntimeException(exp);
- }
- return newGlobal;
- }
+ nashornContext.initGlobal(newGlobal, this);
+ newGlobal.setScriptContext(ctxt);
- // scripts should see "context" and "engine" as variables in the given global object
- private void setContextVariables(final Global ctxtGlobal, final ScriptContext ctxt) {
- // set "context" global variable via contextProperty - because this
- // property is non-writable
- contextProperty.setObjectValue(ctxtGlobal, ctxtGlobal, ctxt, false);
- Object args = ScriptObjectMirror.unwrap(ctxt.getAttribute("arguments"), ctxtGlobal);
- if (args == null || args == UNDEFINED) {
- args = ScriptRuntime.EMPTY_ARRAY;
- }
- // if no arguments passed, expose it
- if (! (args instanceof ScriptObject)) {
- args = ctxtGlobal.wrapAsObject(args);
- ctxtGlobal.set("arguments", args, false);
- }
+ return newGlobal;
}
private Object invokeImpl(final Object selfObject, final String name, final Object... args) throws ScriptException, NoSuchMethodException {
@@ -525,7 +429,7 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
return evalImpl(script, ctxt, getNashornGlobalFrom(ctxt));
}
- private Object evalImpl(final Context.MultiGlobalCompiledScript mgcs, final ScriptContext ctxt, final Global ctxtGlobal) throws ScriptException {
+ private static Object evalImpl(final Context.MultiGlobalCompiledScript mgcs, final ScriptContext ctxt, final Global ctxtGlobal) throws ScriptException {
final Global oldGlobal = Context.getGlobal();
final boolean globalChanged = (oldGlobal != ctxtGlobal);
try {
@@ -534,11 +438,7 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
}
final ScriptFunction script = mgcs.getFunction(ctxtGlobal);
-
- // set ScriptContext variables if ctxt is non-null
- if (ctxt != null) {
- setContextVariables(ctxtGlobal, ctxt);
- }
+ ctxtGlobal.setScriptContext(ctxt);
return ScriptObjectMirror.translateUndefined(ScriptObjectMirror.wrap(ScriptRuntime.apply(script, ctxtGlobal), ctxtGlobal));
} catch (final Exception e) {
throwAsScriptException(e, ctxtGlobal);
@@ -550,7 +450,7 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
}
}
- private Object evalImpl(final ScriptFunction script, final ScriptContext ctxt, final Global ctxtGlobal) throws ScriptException {
+ private static Object evalImpl(final ScriptFunction script, final ScriptContext ctxt, final Global ctxtGlobal) throws ScriptException {
if (script == null) {
return null;
}
@@ -561,10 +461,7 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
Context.setGlobal(ctxtGlobal);
}
- // set ScriptContext variables if ctxt is non-null
- if (ctxt != null) {
- setContextVariables(ctxtGlobal, ctxt);
- }
+ ctxtGlobal.setScriptContext(ctxt);
return ScriptObjectMirror.translateUndefined(ScriptObjectMirror.wrap(ScriptRuntime.apply(script, ctxtGlobal), ctxtGlobal));
} catch (final Exception e) {
throwAsScriptException(e, ctxtGlobal);
diff --git a/src/jdk/nashorn/api/scripting/NashornScriptEngineFactory.java b/src/jdk/nashorn/api/scripting/NashornScriptEngineFactory.java
index 470502d4..777edc9d 100644
--- a/src/jdk/nashorn/api/scripting/NashornScriptEngineFactory.java
+++ b/src/jdk/nashorn/api/scripting/NashornScriptEngineFactory.java
@@ -164,7 +164,7 @@ public final class NashornScriptEngineFactory implements ScriptEngineFactory {
* @param args arguments array passed to script engine.
* @return newly created script engine.
*/
- public ScriptEngine getScriptEngine(final String[] args) {
+ public ScriptEngine getScriptEngine(final String... args) {
checkConfigPermission();
return new NashornScriptEngine(this, args, getAppClassLoader());
}
diff --git a/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java b/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java
index db80a441..b261a1b6 100644
--- a/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java
+++ b/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java
@@ -169,6 +169,12 @@ public final class ScriptObjectMirror extends AbstractJSObject implements Bindin
});
}
+ /**
+ * Call member function
+ * @param functionName function name
+ * @param args arguments
+ * @return return value of function
+ */
public Object callMember(final String functionName, final Object... args) {
functionName.getClass(); // null check
final Global oldGlobal = Context.getGlobal();
@@ -709,6 +715,23 @@ public final class ScriptObjectMirror extends AbstractJSObject implements Bindin
return newArgs;
}
+ /**
+ * Are the given objects mirrors to same underlying object?
+ *
+ * @param obj1 first object
+ * @param obj2 second object
+ * @return true if obj1 and obj2 are identical script objects or mirrors of it.
+ */
+ public static boolean identical(final Object obj1, final Object obj2) {
+ final Object o1 = (obj1 instanceof ScriptObjectMirror)?
+ ((ScriptObjectMirror)obj1).sobj : obj1;
+
+ final Object o2 = (obj2 instanceof ScriptObjectMirror)?
+ ((ScriptObjectMirror)obj2).sobj : obj2;
+
+ return o1 == o2;
+ }
+
// package-privates below this.
ScriptObjectMirror(final ScriptObject sobj, final Global global) {
diff --git a/src/jdk/nashorn/api/scripting/ScriptUtils.java b/src/jdk/nashorn/api/scripting/ScriptUtils.java
index da5b9bdb..4de2cbf5 100644
--- a/src/jdk/nashorn/api/scripting/ScriptUtils.java
+++ b/src/jdk/nashorn/api/scripting/ScriptUtils.java
@@ -25,6 +25,8 @@
package jdk.nashorn.api.scripting;
+import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
+
import java.lang.invoke.MethodHandle;
import jdk.internal.dynalink.beans.StaticClass;
import jdk.internal.dynalink.linker.LinkerServices;
@@ -69,12 +71,15 @@ public final class ScriptUtils {
* Create a wrapper function that calls {@code func} synchronized on {@code sync} or, if that is undefined,
* {@code self}. Used to implement "sync" function in resources/mozilla_compat.js.
*
- * @param func the function to invoke
+ * @param func the function to wrap
* @param sync the object to synchronize on
* @return a synchronizing wrapper function
*/
- public static Object makeSynchronizedFunction(final ScriptFunction func, final Object sync) {
- return func.makeSynchronizedFunction(sync);
+ public static Object makeSynchronizedFunction(final Object func, final Object sync) {
+ if (func instanceof ScriptFunction) {
+ return ((ScriptFunction)func).makeSynchronizedFunction(sync);
+ }
+ throw typeError("not.a.function", ScriptRuntime.safeToString(func));
}
/**
diff --git a/src/jdk/nashorn/api/scripting/resources/engine.js b/src/jdk/nashorn/api/scripting/resources/engine.js
deleted file mode 100644
index 5aed7e6e..00000000
--- a/src/jdk/nashorn/api/scripting/resources/engine.js
+++ /dev/null
@@ -1,101 +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.
- *
- * 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 script file is executed by script engine at the construction
- * of the every new Global object. The functions here assume global variables
- * "context" of type javax.script.ScriptContext and "engine" of the type
- * jdk.nashorn.api.scripting.NashornScriptEngine.
- **/
-
-Object.defineProperty(this, "__noSuchProperty__", {
- configurable: true,
- enumerable: false,
- writable: true,
- value: function (name) {
- 'use strict';
- return engine.__noSuchProperty__(this, context, name);
- }
-});
-
-function print() {
- var writer = context != null? context.writer : engine.context.writer;
- if (! (writer instanceof java.io.PrintWriter)) {
- writer = new java.io.PrintWriter(writer);
- }
-
- var buf = new java.lang.StringBuilder();
- for (var i = 0; i < arguments.length; i++) {
- if (i != 0) {
- buf.append(' ');
- }
- buf.append(String(arguments[i]));
- }
- writer.println(buf.toString());
-}
-
-/**
- * This is C-like printf
- *
- * @param format string to format the rest of the print items
- * @param args variadic argument list
- */
-Object.defineProperty(this, "printf", {
- configurable: true,
- enumerable: false,
- writable: true,
- value: function (format, args/*, more args*/) {
- print(sprintf.apply(this, arguments));
- }
-});
-
-/**
- * This is C-like sprintf
- *
- * @param format string to format the rest of the print items
- * @param args variadic argument list
- */
-Object.defineProperty(this, "sprintf", {
- configurable: true,
- enumerable: false,
- writable: true,
- value: function (format, args/*, more args*/) {
- var len = arguments.length - 1;
- var array = [];
-
- if (len < 0) {
- return "";
- }
-
- for (var i = 0; i < len; i++) {
- if (arguments[i+1] instanceof Date) {
- array[i] = arguments[i+1].getTime();
- } else {
- array[i] = arguments[i+1];
- }
- }
-
- array = Java.to(array);
- return Packages.jdk.nashorn.api.scripting.ScriptUtils.format(format, array);
- }
-});
diff --git a/src/jdk/nashorn/internal/IntDeque.java b/src/jdk/nashorn/internal/IntDeque.java
new file mode 100644
index 00000000..477afcf9
--- /dev/null
+++ b/src/jdk/nashorn/internal/IntDeque.java
@@ -0,0 +1,87 @@
+/*
+ * 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;
+
+/**
+ * Small helper class for fast int deques
+ */
+public class IntDeque {
+ private int[] deque = new int[16];
+ private int nextFree = 0;
+
+ /**
+ * Push an int value
+ * @param value value
+ */
+ public void push(final int value) {
+ if (nextFree == deque.length) {
+ final int[] newDeque = new int[nextFree * 2];
+ System.arraycopy(deque, 0, newDeque, 0, nextFree);
+ deque = newDeque;
+ }
+ deque[nextFree++] = value;
+ }
+
+ /**
+ * Pop an int value
+ * @return value
+ */
+ public int pop() {
+ return deque[--nextFree];
+ }
+
+ /**
+ * Peek
+ * @return top value
+ */
+ public int peek() {
+ return deque[nextFree - 1];
+ }
+
+ /**
+ * Get the value of the top element and increment it.
+ * @return top value
+ */
+ public int getAndIncrement() {
+ return deque[nextFree - 1]++;
+ }
+
+ /**
+ * Decrement the value of the top element and return it.
+ * @return decremented top value
+ */
+ public int decrementAndGet() {
+ return --deque[nextFree - 1];
+ }
+
+ /**
+ * Check if deque is empty
+ * @return true if empty
+ */
+ public boolean isEmpty() {
+ return nextFree == 0;
+ }
+}
diff --git a/src/jdk/nashorn/internal/codegen/ApplySpecialization.java b/src/jdk/nashorn/internal/codegen/ApplySpecialization.java
new file mode 100644
index 00000000..a5fbfdf9
--- /dev/null
+++ b/src/jdk/nashorn/internal/codegen/ApplySpecialization.java
@@ -0,0 +1,318 @@
+/*
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.codegen;
+
+import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS_VAR;
+import static jdk.nashorn.internal.codegen.CompilerConstants.EXPLODED_ARGUMENT_PREFIX;
+
+import java.lang.invoke.MethodType;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import jdk.nashorn.internal.ir.AccessNode;
+import jdk.nashorn.internal.ir.CallNode;
+import jdk.nashorn.internal.ir.Expression;
+import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.ir.IdentNode;
+import jdk.nashorn.internal.ir.LexicalContext;
+import jdk.nashorn.internal.ir.Node;
+import jdk.nashorn.internal.ir.visitor.NodeVisitor;
+import jdk.nashorn.internal.objects.Global;
+import jdk.nashorn.internal.runtime.Context;
+import jdk.nashorn.internal.runtime.logging.DebugLogger;
+import jdk.nashorn.internal.runtime.logging.Loggable;
+import jdk.nashorn.internal.runtime.logging.Logger;
+import jdk.nashorn.internal.runtime.options.Options;
+
+/**
+ * An optimization that attempts to turn applies into calls. This pattern
+ * is very common for fake class instance creation, and apply
+ * introduces expensive args collection and boxing
+ *
+ * <pre>
+ * var Class = {
+ * create: function() {
+ * return function() { //vararg
+ * this.initialize.apply(this, arguments);
+ * }
+ * }
+ * };
+ *
+ * Color = Class.create();
+ *
+ * Color.prototype = {
+ * red: 0, green: 0, blue: 0,
+ * initialize: function(r,g,b) {
+ * this.red = r;
+ * this.green = g;
+ * this.blue = b;
+ * }
+ * }
+ *
+ * new Color(17, 47, 11);
+ * </pre>
+ */
+
+@Logger(name="apply2call")
+public final class ApplySpecialization extends NodeVisitor<LexicalContext> implements Loggable {
+
+ private static final boolean USE_APPLY2CALL = Options.getBooleanProperty("nashorn.apply2call", true);
+
+ private final DebugLogger log;
+
+ private final Compiler compiler;
+
+ private final Set<Integer> changed = new HashSet<>();
+
+ private final Deque<List<IdentNode>> explodedArguments = new ArrayDeque<>();
+
+ private static final String ARGUMENTS = ARGUMENTS_VAR.symbolName();
+
+ /**
+ * Apply specialization optimization. Try to explode arguments and call
+ * applies as calls if they just pass on the "arguments" array and
+ * "arguments" doesn't escape.
+ *
+ * @param compiler compiler
+ */
+ public ApplySpecialization(final Compiler compiler) {
+ super(new LexicalContext());
+ this.compiler = compiler;
+ this.log = initLogger(compiler.getContext());
+ }
+
+ @Override
+ public DebugLogger getLogger() {
+ return log;
+ }
+
+ @Override
+ public DebugLogger initLogger(final Context context) {
+ return context.getLogger(this.getClass());
+ }
+
+ /**
+ * 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) {
+
+ 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
+ }
+
+ private boolean isArguments(final Expression expr) {
+ return expr instanceof IdentNode && ARGUMENTS.equals(((IdentNode)expr).getName());
+ }
+
+ 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()) || ARGUMENTS.equals(identNode.getName()) && !isCurrentArg(identNode)) {
+ throw new UnsupportedOperationException();
+ }
+ 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());
+ }
+ stack.push(callArgs);
+ return true;
+ }
+
+ @Override
+ public Node leaveCallNode(final CallNode callNode) {
+ stack.pop();
+ return callNode;
+ }
+ });
+ } catch (final UnsupportedOperationException e) {
+ log.fine("'arguments' escapes, is not used in standard call dispatch, or is reassigned in '" + functionNode.getName() + "'. Aborting");
+ return true; //bad
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean enterCallNode(final CallNode callNode) {
+ return !explodedArguments.isEmpty();
+ }
+
+ @Override
+ public Node leaveCallNode(final CallNode callNode) {
+ //apply needs to be a global symbol or we don't allow it
+
+ final List<IdentNode> newParams = explodedArguments.peek();
+ if (isApply(callNode)) {
+ final List<Expression> newArgs = new ArrayList<>();
+ for (final Expression arg : callNode.getArgs()) {
+ if (arg instanceof IdentNode && ARGUMENTS.equals(((IdentNode)arg).getName())) {
+ newArgs.addAll(newParams);
+ } else {
+ newArgs.add(arg);
+ }
+ }
+
+ changed.add(lc.getCurrentFunction().getId());
+
+ final CallNode newCallNode = callNode.setArgs(newArgs).setIsApplyToCall();
+
+ log.fine("Transformed ",
+ callNode,
+ " from apply to call => ",
+ newCallNode,
+ " in ",
+ DebugLogger.quote(lc.getCurrentFunction().getName()));
+
+ return newCallNode;
+ }
+
+ return callNode;
+ }
+
+ private boolean pushExplodedArgs(final FunctionNode functionNode) {
+ int start = 0;
+
+ final MethodType actualCallSiteType = compiler.getCallSiteType(functionNode);
+ if (actualCallSiteType == null) {
+ return false;
+ }
+ assert actualCallSiteType.parameterType(actualCallSiteType.parameterCount() - 1) != Object[].class : "error vararg callsite passed to apply2call " + functionNode.getName() + " " + actualCallSiteType;
+
+ final TypeMap ptm = compiler.getTypeMap();
+ if (ptm.needsCallee()) {
+ start++;
+ }
+
+ start++; //we always uses this
+
+ final List<IdentNode> params = functionNode.getParameters();
+ final List<IdentNode> newParams = new ArrayList<>();
+ final long to = Math.max(params.size(), actualCallSiteType.parameterCount() - start);
+ for (int i = 0; i < to; i++) {
+ if (i >= params.size()) {
+ newParams.add(new IdentNode(functionNode.getToken(), functionNode.getFinish(), EXPLODED_ARGUMENT_PREFIX.symbolName() + (i)));
+ } else {
+ newParams.add(params.get(i));
+ }
+ }
+
+ explodedArguments.push(newParams);
+ return true;
+ }
+
+ @Override
+ public boolean enterFunctionNode(final FunctionNode functionNode) {
+ if (!USE_APPLY2CALL) {
+ return false;
+ }
+
+ if (!Global.instance().isSpecialNameValid("apply")) {
+ log.fine("Apply transform disabled: apply/call overridden");
+ assert !Global.instance().isSpecialNameValid("call") : "call and apply should have the same SwitchPoint";
+ return false;
+ }
+
+ if (!compiler.isOnDemandCompilation()) {
+ return false;
+ }
+
+ if (functionNode.hasEval()) {
+ return false;
+ }
+
+ if (argumentsEscape(functionNode)) {
+ return false;
+ }
+
+ return pushExplodedArgs(functionNode);
+ }
+
+ /**
+ * Try to do the apply to call transformation
+ * @return true if successful, false otherwise
+ */
+ @Override
+ public Node leaveFunctionNode(final FunctionNode functionNode0) {
+ FunctionNode newFunctionNode = functionNode0;
+ final String functionName = newFunctionNode.getName();
+
+ if (changed.contains(newFunctionNode.getId())) {
+ newFunctionNode = newFunctionNode.clearFlag(lc, FunctionNode.USES_ARGUMENTS).
+ setFlag(lc, FunctionNode.HAS_APPLY_TO_CALL_SPECIALIZATION).
+ setParameters(lc, explodedArguments.peek());
+
+ if (log.isEnabled()) {
+ log.info("Successfully specialized apply to call in '",
+ functionName,
+ " params=",
+ explodedArguments.peek(),
+ "' id=",
+ newFunctionNode.getId(),
+ " source=",
+ newFunctionNode.getSource().getURL());
+ }
+ }
+
+ explodedArguments.pop();
+
+ return newFunctionNode;
+ }
+
+ private static boolean isApply(final CallNode callNode) {
+ final Expression f = callNode.getFunction();
+ return f instanceof AccessNode && "apply".equals(((AccessNode)f).getProperty());
+ }
+
+}
diff --git a/src/jdk/nashorn/internal/codegen/AssignSymbols.java b/src/jdk/nashorn/internal/codegen/AssignSymbols.java
new file mode 100644
index 00000000..91a022d9
--- /dev/null
+++ b/src/jdk/nashorn/internal/codegen/AssignSymbols.java
@@ -0,0 +1,958 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.codegen;
+
+import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
+import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS_VAR;
+import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
+import static jdk.nashorn.internal.codegen.CompilerConstants.EXCEPTION_PREFIX;
+import static jdk.nashorn.internal.codegen.CompilerConstants.ITERATOR_PREFIX;
+import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
+import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
+import static jdk.nashorn.internal.codegen.CompilerConstants.SWITCH_TAG_PREFIX;
+import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
+import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
+import static jdk.nashorn.internal.ir.Symbol.HAS_OBJECT_VALUE;
+import static jdk.nashorn.internal.ir.Symbol.IS_FUNCTION_SELF;
+import static jdk.nashorn.internal.ir.Symbol.IS_GLOBAL;
+import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL;
+import static jdk.nashorn.internal.ir.Symbol.IS_LET;
+import static jdk.nashorn.internal.ir.Symbol.IS_PARAM;
+import static jdk.nashorn.internal.ir.Symbol.IS_PROGRAM_LEVEL;
+import static jdk.nashorn.internal.ir.Symbol.IS_SCOPE;
+import static jdk.nashorn.internal.ir.Symbol.IS_THIS;
+import static jdk.nashorn.internal.ir.Symbol.IS_VAR;
+import static jdk.nashorn.internal.ir.Symbol.KINDMASK;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Set;
+import jdk.nashorn.internal.ir.AccessNode;
+import jdk.nashorn.internal.ir.BinaryNode;
+import jdk.nashorn.internal.ir.Block;
+import jdk.nashorn.internal.ir.CatchNode;
+import jdk.nashorn.internal.ir.Expression;
+import jdk.nashorn.internal.ir.ForNode;
+import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
+import jdk.nashorn.internal.ir.IdentNode;
+import jdk.nashorn.internal.ir.IndexNode;
+import jdk.nashorn.internal.ir.LexicalContext;
+import jdk.nashorn.internal.ir.LexicalContextNode;
+import jdk.nashorn.internal.ir.LiteralNode;
+import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
+import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit;
+import jdk.nashorn.internal.ir.Node;
+import jdk.nashorn.internal.ir.RuntimeNode;
+import jdk.nashorn.internal.ir.RuntimeNode.Request;
+import jdk.nashorn.internal.ir.SplitNode;
+import jdk.nashorn.internal.ir.Statement;
+import jdk.nashorn.internal.ir.SwitchNode;
+import jdk.nashorn.internal.ir.Symbol;
+import jdk.nashorn.internal.ir.TryNode;
+import jdk.nashorn.internal.ir.UnaryNode;
+import jdk.nashorn.internal.ir.VarNode;
+import jdk.nashorn.internal.ir.WithNode;
+import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
+import jdk.nashorn.internal.ir.visitor.NodeVisitor;
+import jdk.nashorn.internal.runtime.Context;
+import jdk.nashorn.internal.runtime.Property;
+import jdk.nashorn.internal.runtime.PropertyMap;
+import jdk.nashorn.internal.runtime.logging.DebugLogger;
+import jdk.nashorn.internal.runtime.logging.Loggable;
+import jdk.nashorn.internal.runtime.logging.Logger;
+
+/**
+ * This visitor assigns symbols to identifiers denoting variables. It does few more minor calculations that are only
+ * possible after symbols have been assigned; such is the transformation of "delete" and "typeof" operators into runtime
+ * nodes and counting of number of properties assigned to "this" in constructor functions. This visitor is also notable
+ * for what it doesn't do, most significantly it does no type calculations as in JavaScript variables can change types
+ * during runtime and as such symbols don't have types. Calculation of expression types is performed by a separate
+ * visitor.
+ */
+@Logger(name="symbols")
+final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements Loggable {
+ private final DebugLogger log;
+ private final boolean debug;
+
+ private static boolean isParamOrVar(final IdentNode identNode) {
+ final Symbol symbol = identNode.getSymbol();
+ return symbol.isParam() || symbol.isVar();
+ }
+
+ private static String name(final Node node) {
+ final String cn = node.getClass().getName();
+ final int lastDot = cn.lastIndexOf('.');
+ if (lastDot == -1) {
+ return cn;
+ }
+ return cn.substring(lastDot + 1);
+ }
+
+ /**
+ * Checks if various symbols that were provisionally marked as needing a slot ended up unused, and marks them as not
+ * needing a slot after all.
+ * @param functionNode the function node
+ * @return the passed in node, for easy chaining
+ */
+ private static FunctionNode removeUnusedSlots(final FunctionNode functionNode) {
+ if (!functionNode.needsCallee()) {
+ functionNode.compilerConstant(CALLEE).setNeedsSlot(false);
+ }
+ if (!(functionNode.hasScopeBlock() || functionNode.needsParentScope())) {
+ functionNode.compilerConstant(SCOPE).setNeedsSlot(false);
+ }
+ if (!functionNode.usesReturnSymbol()) {
+ functionNode.compilerConstant(RETURN).setNeedsSlot(false);
+ }
+ // Named function expressions that end up not referencing themselves won't need a local slot for the self symbol.
+ if(!functionNode.isDeclared() && !functionNode.usesSelfSymbol() && !functionNode.isAnonymous()) {
+ final Symbol selfSymbol = functionNode.getBody().getExistingSymbol(functionNode.getIdent().getName());
+ if(selfSymbol != null) {
+ if(selfSymbol.isFunctionSelf()) {
+ selfSymbol.setNeedsSlot(false);
+ selfSymbol.clearFlag(Symbol.IS_VAR);
+ }
+ } else {
+ assert functionNode.isProgram();
+ }
+ }
+ return functionNode;
+ }
+
+ private final Deque<Set<String>> thisProperties = new ArrayDeque<>();
+ private final Map<String, Symbol> globalSymbols = new HashMap<>(); //reuse the same global symbol
+ private final Compiler compiler;
+
+ public AssignSymbols(final Compiler compiler) {
+ super(new LexicalContext());
+ this.compiler = compiler;
+ this.log = initLogger(compiler.getContext());
+ this.debug = log.isEnabled();
+ }
+
+ @Override
+ public DebugLogger getLogger() {
+ return log;
+ }
+
+ @Override
+ public DebugLogger initLogger(final Context context) {
+ return context.getLogger(this.getClass());
+ }
+
+ /**
+ * Define symbols for all variable declarations at the top of the function scope. This way we can get around
+ * problems like
+ *
+ * while (true) {
+ * break;
+ * if (true) {
+ * var s;
+ * }
+ * }
+ *
+ * to an arbitrary nesting depth.
+ *
+ * see NASHORN-73
+ *
+ * @param functionNode the FunctionNode we are entering
+ * @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 function declarations (which are taken care
+ // in a separate step above) and "var" declarations in for loop initializers.
+ //
+ body.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
+ @Override
+ public boolean enterFunctionNode(final FunctionNode nestedFn) {
+ // Don't descend into nested functions
+ return false;
+ }
+
+ @Override
+ public Node leaveVarNode(final VarNode varNode) {
+ if (varNode.isStatement()) {
+ final IdentNode ident = varNode.getName();
+ final Symbol symbol = defineSymbol(body, ident.getName(), IS_VAR);
+ functionNode.addDeclaredSymbol(symbol);
+ if (varNode.isFunctionDeclaration()) {
+ symbol.setIsFunctionDeclaration();
+ }
+ return varNode.setName(ident.setSymbol(symbol));
+ }
+ return varNode;
+ }
+ });
+ }
+
+ private IdentNode compilerConstantIdentifier(final CompilerConstants cc) {
+ return createImplicitIdentifier(cc.symbolName()).setSymbol(lc.getCurrentFunction().compilerConstant(cc));
+ }
+
+ /**
+ * Creates an ident node for an implicit identifier within the function (one not declared in the script source
+ * code). These identifiers are defined with function's token and finish.
+ * @param name the name of the identifier
+ * @return an ident node representing the implicit identifier.
+ */
+ private IdentNode createImplicitIdentifier(final String name) {
+ final FunctionNode fn = lc.getCurrentFunction();
+ return new IdentNode(fn.getToken(), fn.getFinish(), name);
+ }
+
+ private Symbol createSymbol(final String name, final int flags) {
+ if ((flags & Symbol.KINDMASK) == IS_GLOBAL) {
+ //reuse global symbols so they can be hashed
+ Symbol global = globalSymbols.get(name);
+ if (global == null) {
+ global = new Symbol(name, flags);
+ globalSymbols.put(name, global);
+ }
+ return global;
+ }
+ return new Symbol(name, flags);
+ }
+
+ /**
+ * Creates a synthetic initializer for a variable (a var statement that doesn't occur in the source code). Typically
+ * used to create assignmnent of {@code :callee} to the function name symbol in self-referential function
+ * expressions as well as for assignment of {@code :arguments} to {@code arguments}.
+ *
+ * @param name the ident node identifying the variable to initialize
+ * @param initConstant the compiler constant it is initialized to
+ * @param fn the function node the assignment is for
+ * @return a var node with the appropriate assignment
+ */
+ private VarNode createSyntheticInitializer(final IdentNode name, final CompilerConstants initConstant, final FunctionNode fn) {
+ final IdentNode init = compilerConstantIdentifier(initConstant);
+ assert init.getSymbol() != null && init.getSymbol().isBytecodeLocal();
+
+ final VarNode synthVar = new VarNode(fn.getLineNumber(), fn.getToken(), fn.getFinish(), name, init);
+
+ final Symbol nameSymbol = fn.getBody().getExistingSymbol(name.getName());
+ assert nameSymbol != null;
+
+ return (VarNode)synthVar.setName(name.setSymbol(nameSymbol)).accept(this);
+ }
+
+ private FunctionNode createSyntheticInitializers(final FunctionNode functionNode) {
+ final List<VarNode> syntheticInitializers = new ArrayList<>(2);
+
+ // Must visit the new var nodes in the context of the body. We could also just set the new statements into the
+ // block and then revisit the entire block, but that seems to be too much double work.
+ final Block body = functionNode.getBody();
+ lc.push(body);
+ try {
+ if (functionNode.usesSelfSymbol()) {
+ // "var fn = :callee"
+ syntheticInitializers.add(createSyntheticInitializer(functionNode.getIdent(), CALLEE, functionNode));
+ }
+
+ if (functionNode.needsArguments()) {
+ // "var arguments = :arguments"
+ syntheticInitializers.add(createSyntheticInitializer(createImplicitIdentifier(ARGUMENTS_VAR.symbolName()),
+ ARGUMENTS, functionNode));
+ }
+
+ if (syntheticInitializers.isEmpty()) {
+ return functionNode;
+ }
+
+ for(final ListIterator<VarNode> it = syntheticInitializers.listIterator(); it.hasNext();) {
+ it.set((VarNode)it.next().accept(this));
+ }
+ } finally {
+ lc.pop(body);
+ }
+
+ final List<Statement> stmts = body.getStatements();
+ final List<Statement> newStatements = new ArrayList<>(stmts.size() + syntheticInitializers.size());
+ newStatements.addAll(syntheticInitializers);
+ newStatements.addAll(stmts);
+ return functionNode.setBody(lc, body.setStatements(lc, newStatements));
+ }
+
+ private Symbol defineGlobalSymbol(final Block block, final String name) {
+ return defineSymbol(block, name, IS_GLOBAL);
+ }
+
+ /**
+ * Defines a new symbol in the given block.
+ *
+ * @param block the block in which to define the symbol
+ * @param name name of symbol.
+ * @param symbolFlags Symbol flags.
+ *
+ * @return Symbol for given name or null for redefinition.
+ */
+ private Symbol defineSymbol(final Block block, final String name, final int symbolFlags) {
+ int flags = symbolFlags;
+ Symbol symbol = findSymbol(block, name); // Locate symbol.
+ final boolean isGlobal = (flags & KINDMASK) == IS_GLOBAL;
+
+ // Global variables are implicitly always scope variables too.
+ if (isGlobal) {
+ flags |= IS_SCOPE;
+ }
+
+ if (lc.getCurrentFunction().isProgram()) {
+ flags |= IS_PROGRAM_LEVEL;
+ }
+
+ final boolean isParam = (flags & KINDMASK) == IS_PARAM;
+ final boolean isVar = (flags & KINDMASK) == IS_VAR;
+
+ final FunctionNode function = lc.getFunction(block);
+ if (symbol != null) {
+ // Symbol was already defined. Check if it needs to be redefined.
+ if (isParam) {
+ if (!isLocal(function, symbol)) {
+ // Not defined in this function. Create a new definition.
+ symbol = null;
+ } else if (symbol.isParam()) {
+ // Duplicate parameter. Null return will force an error.
+ throw new AssertionError("duplicate parameter");
+ }
+ } else if (isVar) {
+ if ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET) {
+ // Always create a new definition.
+ symbol = null;
+ } else {
+ // Not defined in this function. Create a new definition.
+ if (!isLocal(function, symbol) || symbol.less(IS_VAR)) {
+ symbol = null;
+ }
+ }
+ }
+ }
+
+ if (symbol == null) {
+ // If not found, then create a new one.
+ Block symbolBlock;
+
+ // Determine where to create it.
+ if (isVar && ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET)) {
+ symbolBlock = block; //internal vars are always defined in the block closest to them
+ } else if (isGlobal) {
+ symbolBlock = lc.getOutermostFunction().getBody();
+ } else {
+ symbolBlock = lc.getFunctionBody(function);
+ }
+
+ // Create and add to appropriate block.
+ symbol = createSymbol(name, flags);
+ symbolBlock.putSymbol(lc, symbol);
+
+ if ((flags & IS_SCOPE) == 0) {
+ // Initial assumption; symbol can lose its slot later
+ symbol.setNeedsSlot(true);
+ }
+ } else if (symbol.less(flags)) {
+ symbol.setFlags(flags);
+ }
+
+ return symbol;
+ }
+
+ private <T extends Node> T end(final T node) {
+ return end(node, true);
+ }
+
+ private <T extends Node> T end(final T node, final boolean printNode) {
+ if (debug) {
+ final StringBuilder sb = new StringBuilder();
+
+ sb.append("[LEAVE ").
+ append(name(node)).
+ append("] ").
+ append(printNode ? node.toString() : "").
+ append(" in '").
+ append(lc.getCurrentFunction().getName()).
+ append('\'');
+
+ if (node instanceof IdentNode) {
+ final Symbol symbol = ((IdentNode)node).getSymbol();
+ if (symbol == null) {
+ sb.append(" <NO SYMBOL>");
+ } else {
+ sb.append(" <symbol=").append(symbol).append('>');
+ }
+ }
+
+ log.unindent();
+ log.info(sb);
+ }
+
+ return node;
+ }
+
+ @Override
+ public boolean enterBlock(final Block block) {
+ start(block);
+ block.clearSymbols();
+
+ if (lc.isFunctionBody()) {
+ enterFunctionBody();
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean enterCatchNode(final CatchNode catchNode) {
+ final IdentNode exception = catchNode.getException();
+ final Block block = lc.getCurrentBlock();
+
+ start(catchNode);
+
+ // define block-local exception variable
+ final String exname = exception.getName();
+ // If the name of the exception starts with ":e", this is a synthetic catch block, likely a catch-all. Its
+ // symbol is naturally internal, and should be treated as such.
+ final boolean isInternal = exname.startsWith(EXCEPTION_PREFIX.symbolName());
+ defineSymbol(block, exname, IS_VAR | IS_LET | (isInternal ? IS_INTERNAL : 0) | HAS_OBJECT_VALUE);
+
+ return true;
+ }
+
+ private void enterFunctionBody() {
+ final FunctionNode functionNode = lc.getCurrentFunction();
+ final Block body = lc.getCurrentBlock();
+
+ initFunctionWideVariables(functionNode, body);
+
+ if (functionNode.isProgram()) {
+ initGlobalSymbols(body);
+ } else if (!functionNode.isDeclared() && !functionNode.isAnonymous()) {
+ // It's neither declared nor program - it's a function expression then; assign it a self-symbol unless it's
+ // anonymous.
+ final String name = functionNode.getIdent().getName();
+ assert name != null;
+ assert body.getExistingSymbol(name) == null;
+ defineSymbol(body, name, IS_VAR | IS_FUNCTION_SELF | HAS_OBJECT_VALUE);
+ if(functionNode.allVarsInScope()) { // basically, has deep eval
+ lc.setFlag(functionNode, FunctionNode.USES_SELF_SYMBOL);
+ }
+ }
+
+ acceptDeclarations(functionNode, body);
+ }
+
+ @Override
+ public boolean enterFunctionNode(final FunctionNode functionNode) {
+ // TODO: once we have information on symbols used by nested functions, we can stop descending into nested
+ // functions with on-demand compilation, e.g. add
+ // if(!thisProperties.isEmpty() && env.isOnDemandCompilation()) {
+ // return false;
+ // }
+ start(functionNode, false);
+
+ thisProperties.push(new HashSet<String>());
+
+ //an outermost function in our lexical context that is not a program
+ //is possible - it is a function being compiled lazily
+ if (functionNode.isDeclared()) {
+ final Iterator<Block> blocks = lc.getBlocks();
+ if (blocks.hasNext()) {
+ defineSymbol(blocks.next(), functionNode.getIdent().getName(), IS_VAR);
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean enterVarNode(final VarNode varNode) {
+ start(varNode);
+ defineSymbol(lc.getCurrentBlock(), varNode.getName().getName(), IS_VAR | (lc.getCurrentFunction().isProgram() ? IS_SCOPE : 0));
+ return true;
+ }
+
+ private Symbol exceptionSymbol() {
+ return newObjectInternal(EXCEPTION_PREFIX);
+ }
+
+ /**
+ * This has to run before fix assignment types, store any type specializations for
+ * paramters, then turn then to objects for the generic version of this method
+ *
+ * @param functionNode functionNode
+ */
+ private FunctionNode finalizeParameters(final FunctionNode functionNode) {
+ final List<IdentNode> newParams = new ArrayList<>();
+ final boolean isVarArg = functionNode.isVarArg();
+
+ final Block body = functionNode.getBody();
+ for (final IdentNode param : functionNode.getParameters()) {
+ final Symbol paramSymbol = body.getExistingSymbol(param.getName());
+ assert paramSymbol != null;
+ assert paramSymbol.isParam() : paramSymbol + " " + paramSymbol.getFlags();
+ newParams.add(param.setSymbol(paramSymbol));
+
+ // parameters should not be slots for a function that uses variable arity signature
+ if (isVarArg) {
+ paramSymbol.setNeedsSlot(false);
+ }
+ }
+
+ return functionNode.setParameters(lc, newParams);
+ }
+
+ /**
+ * Search for symbol in the lexical context starting from the given block.
+ * @param name Symbol name.
+ * @return Found symbol or null if not found.
+ */
+ private Symbol findSymbol(final Block block, final String name) {
+ for (final Iterator<Block> blocks = lc.getBlocks(block); blocks.hasNext();) {
+ final Symbol symbol = blocks.next().getExistingSymbol(name);
+ if (symbol != null) {
+ return symbol;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Marks the current function as one using any global symbol. The function and all its parent functions will all be
+ * marked as needing parent scope.
+ * @see FunctionNode#needsParentScope()
+ */
+ private void functionUsesGlobalSymbol() {
+ for (final Iterator<FunctionNode> fns = lc.getFunctions(); fns.hasNext();) {
+ lc.setFlag(fns.next(), FunctionNode.USES_ANCESTOR_SCOPE);
+ }
+ }
+
+ /**
+ * Marks the current function as one using a scoped symbol. The block defining the symbol will be marked as needing
+ * its own scope to hold the variable. If the symbol is defined outside of the current function, it and all
+ * functions up to (but not including) the function containing the defining block will be marked as needing parent
+ * function scope.
+ * @see FunctionNode#needsParentScope()
+ */
+ private void functionUsesScopeSymbol(final Symbol symbol) {
+ final String name = symbol.getName();
+ for (final Iterator<LexicalContextNode> contextNodeIter = lc.getAllNodes(); contextNodeIter.hasNext(); ) {
+ final LexicalContextNode node = contextNodeIter.next();
+ if (node instanceof Block) {
+ final Block block = (Block)node;
+ if (block.getExistingSymbol(name) != null) {
+ assert lc.contains(block);
+ lc.setBlockNeedsScope(block);
+ break;
+ }
+ } else if (node instanceof FunctionNode) {
+ lc.setFlag(node, FunctionNode.USES_ANCESTOR_SCOPE);
+ }
+ }
+ }
+
+ /**
+ * Declares that the current function is using the symbol.
+ * @param symbol the symbol used by the current function.
+ */
+ private void functionUsesSymbol(final Symbol symbol) {
+ assert symbol != null;
+ if (symbol.isScope()) {
+ if (symbol.isGlobal()) {
+ functionUsesGlobalSymbol();
+ } else {
+ functionUsesScopeSymbol(symbol);
+ }
+ } else {
+ assert !symbol.isGlobal(); // Every global is also scope
+ }
+ }
+
+ private void initCompileConstant(final CompilerConstants cc, final Block block, final int flags) {
+ defineSymbol(block, cc.symbolName(), flags).setNeedsSlot(true);
+ }
+
+ private void initFunctionWideVariables(final FunctionNode functionNode, final Block body) {
+ initCompileConstant(CALLEE, body, IS_PARAM | IS_INTERNAL | HAS_OBJECT_VALUE);
+ initCompileConstant(THIS, body, IS_PARAM | IS_THIS | HAS_OBJECT_VALUE);
+
+ if (functionNode.isVarArg()) {
+ initCompileConstant(VARARGS, body, IS_PARAM | IS_INTERNAL | HAS_OBJECT_VALUE);
+ if (functionNode.needsArguments()) {
+ initCompileConstant(ARGUMENTS, body, IS_VAR | IS_INTERNAL | HAS_OBJECT_VALUE);
+ defineSymbol(body, ARGUMENTS_VAR.symbolName(), IS_VAR | HAS_OBJECT_VALUE);
+ }
+ }
+
+ initParameters(functionNode, body);
+ initCompileConstant(SCOPE, body, IS_VAR | IS_INTERNAL | HAS_OBJECT_VALUE);
+ initCompileConstant(RETURN, body, IS_VAR | IS_INTERNAL);
+ }
+
+
+ /**
+ * Move any properties from the global map into the scope of this function (which must be a program function).
+ * @param block the function node body for which to init scope vars
+ */
+ private void initGlobalSymbols(final Block block) {
+ final PropertyMap map = Context.getGlobalMap();
+
+ for (final Property property : map.getProperties()) {
+ final Symbol symbol = defineGlobalSymbol(block, property.getKey());
+ log.info("Added global symbol from property map ", symbol);
+ }
+ }
+
+ /**
+ * Initialize parameters for function node.
+ * @param functionNode the function node
+ */
+ private void initParameters(final FunctionNode functionNode, final Block body) {
+ final boolean isVarArg = functionNode.isVarArg();
+ final boolean scopeParams = functionNode.allVarsInScope() || isVarArg;
+ for (final IdentNode param : functionNode.getParameters()) {
+ final Symbol symbol = defineSymbol(body, param.getName(), IS_PARAM);
+ if(scopeParams) {
+ // NOTE: this "set is scope" is a poor substitute for clear expression of where the symbol is stored.
+ // It will force creation of scopes where they would otherwise not necessarily be needed (functions
+ // using arguments object and other variable arity functions). Tracked by JDK-8038942.
+ symbol.setIsScope();
+ assert symbol.hasSlot();
+ if(isVarArg) {
+ symbol.setNeedsSlot(false);
+ }
+ }
+ }
+ }
+
+ /**
+ * Is the symbol local to (that is, defined in) the specified function?
+ * @param function the function
+ * @param symbol the symbol
+ * @return true if the symbol is defined in the specified function
+ */
+ private boolean isLocal(final FunctionNode function, final Symbol symbol) {
+ final FunctionNode definingFn = lc.getDefiningFunction(symbol);
+ assert definingFn != null;
+ return definingFn == function;
+ }
+
+ @Override
+ public Node leaveASSIGN(final BinaryNode binaryNode) {
+ // If we're assigning a property of the this object ("this.foo = ..."), record it.
+
+ final Expression lhs = binaryNode.lhs();
+ if (lhs instanceof AccessNode) {
+ final AccessNode accessNode = (AccessNode) lhs;
+ final Expression base = accessNode.getBase();
+ if (base instanceof IdentNode) {
+ final Symbol symbol = ((IdentNode)base).getSymbol();
+ if(symbol.isThis()) {
+ thisProperties.peek().add(accessNode.getProperty());
+ }
+ }
+ }
+ return binaryNode;
+ }
+
+ @Override
+ public Node leaveBlock(final Block block) {
+ // It's not necessary to guard the marking of symbols as locals with this "if"condition for correctness, it's
+ // just an optimization -- runtime type calculation is not used when the compilation is not an on-demand
+ // optimistic compilation, so we can skip locals marking then.
+ if (compiler.useOptimisticTypes() && compiler.isOnDemandCompilation()) {
+ for (final Symbol symbol: block.getSymbols()) {
+ if (!symbol.isScope()) {
+ assert symbol.isVar() || symbol.isParam();
+ compiler.declareLocalSymbol(symbol.getName());
+ }
+ }
+ }
+ return block;
+ }
+
+ @Override
+ public Node leaveDELETE(final UnaryNode unaryNode) {
+ final FunctionNode currentFunctionNode = lc.getCurrentFunction();
+ final boolean strictMode = currentFunctionNode.isStrict();
+ final Expression rhs = unaryNode.getExpression();
+ final Expression strictFlagNode = (Expression)LiteralNode.newInstance(unaryNode, strictMode).accept(this);
+
+ Request request = Request.DELETE;
+ final List<Expression> args = new ArrayList<>();
+
+ if (rhs instanceof IdentNode) {
+ final IdentNode ident = (IdentNode)rhs;
+ // If this is a declared variable or a function parameter, delete always fails (except for globals).
+ final String name = ident.getName();
+ final Symbol symbol = ident.getSymbol();
+ final boolean failDelete = strictMode || (!symbol.isScope() && (symbol.isParam() || (symbol.isVar() && !symbol.isProgramLevel())));
+
+ if (failDelete && symbol.isThis()) {
+ return LiteralNode.newInstance(unaryNode, true).accept(this);
+ }
+ final Expression literalNode = (Expression)LiteralNode.newInstance(unaryNode, name).accept(this);
+
+ if (!failDelete) {
+ args.add(compilerConstantIdentifier(SCOPE));
+ }
+ args.add(literalNode);
+ args.add(strictFlagNode);
+
+ if (failDelete) {
+ request = Request.FAIL_DELETE;
+ }
+ } else if (rhs instanceof AccessNode) {
+ final Expression base = ((AccessNode)rhs).getBase();
+ final String property = ((AccessNode)rhs).getProperty();
+
+ args.add(base);
+ args.add((Expression)LiteralNode.newInstance(unaryNode, property).accept(this));
+ args.add(strictFlagNode);
+
+ } else if (rhs instanceof IndexNode) {
+ final IndexNode indexNode = (IndexNode)rhs;
+ final Expression base = indexNode.getBase();
+ final Expression index = indexNode.getIndex();
+
+ args.add(base);
+ args.add(index);
+ args.add(strictFlagNode);
+
+ } else {
+ return LiteralNode.newInstance(unaryNode, true).accept(this);
+ }
+ return new RuntimeNode(unaryNode, request, args).accept(this);
+ }
+
+ @Override
+ public Node leaveForNode(final ForNode forNode) {
+ if (forNode.isForIn()) {
+ forNode.setIterator(newObjectInternal(ITERATOR_PREFIX)); //NASHORN-73
+ }
+
+ return end(forNode);
+ }
+
+ @Override
+ public Node leaveFunctionNode(final FunctionNode functionNode) {
+
+ return markProgramBlock(
+ removeUnusedSlots(
+ createSyntheticInitializers(
+ finalizeParameters(
+ lc.applyTopFlags(functionNode))))
+ .setThisProperties(lc, thisProperties.pop().size())
+ .setState(lc, CompilationState.SYMBOLS_ASSIGNED));
+ }
+
+ @Override
+ public Node leaveIdentNode(final IdentNode identNode) {
+ final String name = identNode.getName();
+
+ if (identNode.isPropertyName()) {
+ return identNode;
+ }
+
+ final Block block = lc.getCurrentBlock();
+
+ Symbol symbol = findSymbol(block, name);
+
+ //If an existing symbol with the name is found, use that otherwise, declare a new one
+ if (symbol != null) {
+ log.info("Existing symbol = ", symbol);
+ if (symbol.isFunctionSelf()) {
+ final FunctionNode functionNode = lc.getDefiningFunction(symbol);
+ assert functionNode != null;
+ assert lc.getFunctionBody(functionNode).getExistingSymbol(CALLEE.symbolName()) != null;
+ lc.setFlag(functionNode, FunctionNode.USES_SELF_SYMBOL);
+ }
+
+ // if symbol is non-local or we're in a with block, we need to put symbol in scope (if it isn't already)
+ maybeForceScope(symbol);
+ } else {
+ log.info("No symbol exists. Declare as global: ", symbol);
+ symbol = defineGlobalSymbol(block, name);
+ Symbol.setSymbolIsScope(lc, symbol);
+ }
+
+ functionUsesSymbol(symbol);
+
+ if (!identNode.isInitializedHere()) {
+ symbol.increaseUseCount();
+ }
+
+ return end(identNode.setSymbol(symbol));
+ }
+
+ @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()) {
+ switchNode.setTag(newObjectInternal(SWITCH_TAG_PREFIX));
+ }
+ return switchNode;
+ }
+
+ @Override
+ public Node leaveTryNode(final TryNode tryNode) {
+ tryNode.setException(exceptionSymbol());
+ if (tryNode.getFinallyBody() != null) {
+ tryNode.setFinallyCatchAll(exceptionSymbol());
+ }
+
+ end(tryNode);
+
+ return tryNode;
+ }
+
+ @Override
+ public Node leaveTYPEOF(final UnaryNode unaryNode) {
+ final Expression rhs = unaryNode.getExpression();
+
+ final List<Expression> args = new ArrayList<>();
+ if (rhs instanceof IdentNode && !isParamOrVar((IdentNode)rhs)) {
+ args.add(compilerConstantIdentifier(SCOPE));
+ args.add((Expression)LiteralNode.newInstance(rhs, ((IdentNode)rhs).getName()).accept(this)); //null
+ } else {
+ args.add(rhs);
+ args.add((Expression)LiteralNode.newInstance(unaryNode).accept(this)); //null, do not reuse token of identifier rhs, it can be e.g. 'this'
+ }
+
+ final Node runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args).accept(this);
+
+ end(unaryNode);
+
+ return runtimeNode;
+ }
+
+ private FunctionNode markProgramBlock(final FunctionNode functionNode) {
+ if (compiler.isOnDemandCompilation() || !functionNode.isProgram()) {
+ return functionNode;
+ }
+
+ assert functionNode.getId() == 1;
+ return functionNode.setBody(lc, functionNode.getBody().setFlag(lc, Block.IS_GLOBAL_SCOPE));
+ }
+
+ /**
+ * If the symbol isn't already a scope symbol, but it needs to be (see {@link #symbolNeedsToBeScope(Symbol)}, it is
+ * promoted to a scope symbol and its block marked as needing a scope.
+ * @param symbol the symbol that might be scoped
+ */
+ private void maybeForceScope(final Symbol symbol) {
+ if (!symbol.isScope() && symbolNeedsToBeScope(symbol)) {
+ Symbol.setSymbolIsScope(lc, symbol);
+ }
+ }
+
+ private Symbol newInternal(final CompilerConstants cc, final int flags) {
+ return defineSymbol(lc.getCurrentBlock(), lc.getCurrentFunction().uniqueName(cc.symbolName()), IS_VAR | IS_INTERNAL | flags); //NASHORN-73
+ }
+
+ private Symbol newObjectInternal(final CompilerConstants cc) {
+ return newInternal(cc, HAS_OBJECT_VALUE);
+ }
+
+ private boolean start(final Node node) {
+ return start(node, true);
+ }
+
+ private boolean start(final Node node, final boolean printNode) {
+ if (debug) {
+ final StringBuilder sb = new StringBuilder();
+
+ sb.append("[ENTER ").
+ append(name(node)).
+ append("] ").
+ append(printNode ? node.toString() : "").
+ append(" in '").
+ append(lc.getCurrentFunction().getName()).
+ append("'");
+ log.info(sb);
+ log.indent();
+ }
+
+ return true;
+ }
+
+ /**
+ * Determines if the symbol has to be a scope symbol. In general terms, it has to be a scope symbol if it can only
+ * be reached from the current block by traversing a function node, a split node, or a with node.
+ * @param symbol the symbol checked for needing to be a scope symbol
+ * @return true if the symbol has to be a scope symbol.
+ */
+ private boolean symbolNeedsToBeScope(final Symbol symbol) {
+ if (symbol.isThis() || symbol.isInternal()) {
+ return false;
+ }
+
+ if (lc.getCurrentFunction().allVarsInScope()) {
+ return true;
+ }
+
+ boolean previousWasBlock = false;
+ for (final Iterator<LexicalContextNode> it = lc.getAllNodes(); it.hasNext();) {
+ final LexicalContextNode node = it.next();
+ if (node instanceof FunctionNode || node instanceof SplitNode || isSplitArray(node)) {
+ // We reached the function boundary or a splitting boundary without seeing a definition for the symbol.
+ // It needs to be in scope.
+ return true;
+ } else if (node instanceof WithNode) {
+ if (previousWasBlock) {
+ // We reached a WithNode; the symbol must be scoped. Note that if the WithNode was not immediately
+ // preceded by a block, this means we're currently processing its expression, not its body,
+ // therefore it doesn't count.
+ return true;
+ }
+ previousWasBlock = false;
+ } else if (node instanceof Block) {
+ if (((Block)node).getExistingSymbol(symbol.getName()) == symbol) {
+ // We reached the block that defines the symbol without reaching either the function boundary, or a
+ // WithNode. The symbol need not be scoped.
+ return false;
+ }
+ previousWasBlock = true;
+ } else {
+ previousWasBlock = false;
+ }
+ }
+ throw new AssertionError();
+ }
+
+ private static boolean isSplitArray(final LexicalContextNode expr) {
+ if(!(expr instanceof ArrayLiteralNode)) {
+ return false;
+ }
+ final List<ArrayUnit> units = ((ArrayLiteralNode)expr).getUnits();
+ return !(units == null || units.isEmpty());
+ }
+}
diff --git a/src/jdk/nashorn/internal/codegen/Attr.java b/src/jdk/nashorn/internal/codegen/Attr.java
deleted file mode 100644
index f92e0649..00000000
--- a/src/jdk/nashorn/internal/codegen/Attr.java
+++ /dev/null
@@ -1,1947 +0,0 @@
-/*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package jdk.nashorn.internal.codegen;
-
-import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
-import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS_VAR;
-import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
-import static jdk.nashorn.internal.codegen.CompilerConstants.EXCEPTION_PREFIX;
-import static jdk.nashorn.internal.codegen.CompilerConstants.ITERATOR_PREFIX;
-import static jdk.nashorn.internal.codegen.CompilerConstants.LITERAL_PREFIX;
-import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
-import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
-import static jdk.nashorn.internal.codegen.CompilerConstants.SWITCH_TAG_PREFIX;
-import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
-import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
-import static jdk.nashorn.internal.ir.Symbol.IS_ALWAYS_DEFINED;
-import static jdk.nashorn.internal.ir.Symbol.IS_CONSTANT;
-import static jdk.nashorn.internal.ir.Symbol.IS_FUNCTION_SELF;
-import static jdk.nashorn.internal.ir.Symbol.IS_GLOBAL;
-import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL;
-import static jdk.nashorn.internal.ir.Symbol.IS_LET;
-import static jdk.nashorn.internal.ir.Symbol.IS_PARAM;
-import static jdk.nashorn.internal.ir.Symbol.IS_SCOPE;
-import static jdk.nashorn.internal.ir.Symbol.IS_THIS;
-import static jdk.nashorn.internal.ir.Symbol.IS_VAR;
-import static jdk.nashorn.internal.ir.Symbol.KINDMASK;
-
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Deque;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
-import jdk.nashorn.internal.codegen.types.Type;
-import jdk.nashorn.internal.ir.AccessNode;
-import jdk.nashorn.internal.ir.BinaryNode;
-import jdk.nashorn.internal.ir.Block;
-import jdk.nashorn.internal.ir.CallNode;
-import jdk.nashorn.internal.ir.CaseNode;
-import jdk.nashorn.internal.ir.CatchNode;
-import jdk.nashorn.internal.ir.Expression;
-import jdk.nashorn.internal.ir.ForNode;
-import jdk.nashorn.internal.ir.FunctionNode;
-import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
-import jdk.nashorn.internal.ir.IdentNode;
-import jdk.nashorn.internal.ir.IndexNode;
-import jdk.nashorn.internal.ir.LexicalContext;
-import jdk.nashorn.internal.ir.LexicalContextNode;
-import jdk.nashorn.internal.ir.LiteralNode;
-import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
-import jdk.nashorn.internal.ir.Node;
-import jdk.nashorn.internal.ir.ObjectNode;
-import jdk.nashorn.internal.ir.ReturnNode;
-import jdk.nashorn.internal.ir.RuntimeNode;
-import jdk.nashorn.internal.ir.RuntimeNode.Request;
-import jdk.nashorn.internal.ir.Statement;
-import jdk.nashorn.internal.ir.SwitchNode;
-import jdk.nashorn.internal.ir.Symbol;
-import jdk.nashorn.internal.ir.TemporarySymbols;
-import jdk.nashorn.internal.ir.TernaryNode;
-import jdk.nashorn.internal.ir.TryNode;
-import jdk.nashorn.internal.ir.UnaryNode;
-import jdk.nashorn.internal.ir.VarNode;
-import jdk.nashorn.internal.ir.WithNode;
-import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
-import jdk.nashorn.internal.ir.visitor.NodeVisitor;
-import jdk.nashorn.internal.parser.TokenType;
-import jdk.nashorn.internal.runtime.Context;
-import jdk.nashorn.internal.runtime.Debug;
-import jdk.nashorn.internal.runtime.DebugLogger;
-import jdk.nashorn.internal.runtime.JSType;
-import jdk.nashorn.internal.runtime.Property;
-import jdk.nashorn.internal.runtime.PropertyMap;
-
-/**
- * This is the attribution pass of the code generator. Attr takes Lowered IR,
- * that is, IR where control flow has been computed and high level to low level
- * substitions for operations have been performed.
- *
- * After Attr, every symbol will have a conservative correct type.
- *
- * Any expression that requires temporary storage as part of computation will
- * also be detected here and give a temporary symbol
- *
- * Types can be narrowed after Attr by Access Specialization in FinalizeTypes,
- * but in general, this is where the main symbol type information is
- * computed.
- */
-
-final class Attr extends NodeOperatorVisitor<LexicalContext> {
-
- /**
- * Local definitions in current block (to discriminate from function
- * declarations always defined in the function scope. This is for
- * "can be undefined" analysis.
- */
- private final Deque<Set<String>> localDefs;
-
- /**
- * Local definitions in current block to guard against cases like
- * NASHORN-467 when things can be undefined as they are used before
- * their local var definition. *sigh* JavaScript...
- */
- private final Deque<Set<String>> localUses;
-
- private final Deque<Type> returnTypes;
-
- private int catchNestingLevel;
-
- private static final DebugLogger LOG = new DebugLogger("attr");
- private static final boolean DEBUG = LOG.isEnabled();
-
- private final TemporarySymbols temporarySymbols;
-
- /**
- * Constructor.
- */
- Attr(final TemporarySymbols temporarySymbols) {
- super(new LexicalContext());
- this.temporarySymbols = temporarySymbols;
- this.localDefs = new ArrayDeque<>();
- this.localUses = new ArrayDeque<>();
- this.returnTypes = new ArrayDeque<>();
- }
-
- @Override
- protected boolean enterDefault(final Node node) {
- return start(node);
- }
-
- @Override
- protected Node leaveDefault(final Node node) {
- return end(node);
- }
-
- @Override
- public Node leaveAccessNode(final AccessNode accessNode) {
- //While Object type is assigned here, Access Specialization in FinalizeTypes may narrow this, that
- //is why we can't set the access node base to be an object here, that will ruin access specialization
- //for example for a.x | 17.
- return end(ensureSymbol(Type.OBJECT, accessNode));
- }
-
- private void initFunctionWideVariables(final FunctionNode functionNode, final Block body) {
- initCompileConstant(CALLEE, body, IS_PARAM | IS_INTERNAL);
- initCompileConstant(THIS, body, IS_PARAM | IS_THIS, Type.OBJECT);
-
- if (functionNode.isVarArg()) {
- initCompileConstant(VARARGS, body, IS_PARAM | IS_INTERNAL);
- if (functionNode.needsArguments()) {
- initCompileConstant(ARGUMENTS, body, IS_VAR | IS_INTERNAL | IS_ALWAYS_DEFINED);
- final String argumentsName = ARGUMENTS_VAR.symbolName();
- newType(defineSymbol(body, argumentsName, IS_VAR | IS_ALWAYS_DEFINED), Type.typeFor(ARGUMENTS_VAR.type()));
- addLocalDef(argumentsName);
- }
- }
-
- initParameters(functionNode, body);
- initCompileConstant(SCOPE, body, IS_VAR | IS_INTERNAL | IS_ALWAYS_DEFINED);
- initCompileConstant(RETURN, body, IS_VAR | IS_INTERNAL | IS_ALWAYS_DEFINED, Type.OBJECT);
- }
-
-
- /**
- * This pushes all declarations (except for non-statements, i.e. for
- * node temporaries) to the top of the function scope. This way we can
- * get around problems like
- *
- * while (true) {
- * break;
- * if (true) {
- * var s;
- * }
- * }
- *
- * to an arbitrary nesting depth.
- *
- * see NASHORN-73
- *
- * @param functionNode the FunctionNode we are entering
- * @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 function declarations (which are taken care
- // in a separate step above) and "var" declarations in for loop initializers.
- //
- // It also handles the case that a variable can be undefined, e.g
- // if (cond) {
- // x = x.y;
- // }
- // var x = 17;
- //
- // by making sure that no identifier has been found earlier in the body than the
- // declaration - if such is the case the identifier is flagged as caBeUndefined to
- // be safe if it turns into a local var. Otherwise corrupt bytecode results
-
- body.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
- private final Set<String> uses = new HashSet<>();
- private final Set<String> canBeUndefined = new HashSet<>();
-
- @Override
- public boolean enterFunctionNode(final FunctionNode nestedFn) {
- return false;
- }
-
- @Override
- public Node leaveIdentNode(final IdentNode identNode) {
- uses.add(identNode.getName());
- return identNode;
- }
-
- @Override
- public boolean enterVarNode(final VarNode varNode) {
- final String name = varNode.getName().getName();
- //if this is used before the var node, the var node symbol needs to be tagged as can be undefined
- if (uses.contains(name)) {
- canBeUndefined.add(name);
- }
-
- // all uses of the declared varnode inside the var node are potentially undefined
- // however this is a bit conservative as e.g. var x = 17; var x = 1 + x; does work
- if (!varNode.isFunctionDeclaration() && varNode.getInit() != null) {
- varNode.getInit().accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
- @Override
- public boolean enterIdentNode(final IdentNode identNode) {
- if (name.equals(identNode.getName())) {
- canBeUndefined.add(name);
- }
- return false;
- }
- });
- }
-
- return true;
- }
-
- @Override
- public Node leaveVarNode(final VarNode varNode) {
- // any declared symbols that aren't visited need to be typed as well, hence the list
- if (varNode.isStatement()) {
- final IdentNode ident = varNode.getName();
- final Symbol symbol = defineSymbol(body, ident.getName(), IS_VAR);
- if (canBeUndefined.contains(ident.getName())) {
- symbol.setType(Type.OBJECT);
- symbol.setCanBeUndefined();
- }
- functionNode.addDeclaredSymbol(symbol);
- if (varNode.isFunctionDeclaration()) {
- newType(symbol, FunctionNode.FUNCTION_TYPE);
- symbol.setIsFunctionDeclaration();
- }
- return varNode.setName((IdentNode)ident.setSymbol(lc, symbol));
- }
-
- return varNode;
- }
- });
- }
-
- private void enterFunctionBody() {
-
- final FunctionNode functionNode = lc.getCurrentFunction();
- final Block body = lc.getCurrentBlock();
-
- initFunctionWideVariables(functionNode, body);
-
- if (functionNode.isProgram()) {
- initFromPropertyMap(body);
- } else if (!functionNode.isDeclared()) {
- // It's neither declared nor program - it's a function expression then; assign it a self-symbol.
- assert functionNode.getSymbol() == null;
-
- final boolean anonymous = functionNode.isAnonymous();
- final String name = anonymous ? null : functionNode.getIdent().getName();
- if (!(anonymous || body.getExistingSymbol(name) != null)) {
- assert !anonymous && name != null;
- newType(defineSymbol(body, name, IS_VAR | IS_FUNCTION_SELF), Type.OBJECT);
- }
- }
-
- acceptDeclarations(functionNode, body);
- }
-
- @Override
- public boolean enterBlock(final Block block) {
- start(block);
- //ensure that we don't use information from a previous compile. This is very ugly TODO
- //the symbols in the block should really be stateless
- block.clearSymbols();
-
- if (lc.isFunctionBody()) {
- enterFunctionBody();
- }
- pushLocalsBlock();
-
- return true;
- }
-
- @Override
- public Node leaveBlock(final Block block) {
- popLocals();
- return end(block);
- }
-
- @Override
- public boolean enterCallNode(final CallNode callNode) {
- return start(callNode);
- }
-
- @Override
- public Node leaveCallNode(final CallNode callNode) {
- return end(ensureSymbol(callNode.getType(), callNode));
- }
-
- @Override
- public boolean enterCatchNode(final CatchNode catchNode) {
- final IdentNode exception = catchNode.getException();
- final Block block = lc.getCurrentBlock();
-
- start(catchNode);
- catchNestingLevel++;
-
- // define block-local exception variable
- final String exname = exception.getName();
- final Symbol def = defineSymbol(block, exname, IS_VAR | IS_LET | IS_ALWAYS_DEFINED);
- newType(def, Type.OBJECT); //we can catch anything, not just ecma exceptions
-
- addLocalDef(exname);
-
- return true;
- }
-
- @Override
- public Node leaveCatchNode(final CatchNode catchNode) {
- final IdentNode exception = catchNode.getException();
- final Block block = lc.getCurrentBlock();
- final Symbol symbol = findSymbol(block, exception.getName());
-
- catchNestingLevel--;
-
- assert symbol != null;
- return end(catchNode.setException((IdentNode)exception.setSymbol(lc, symbol)));
- }
-
- /**
- * Declare the definition of a new symbol.
- *
- * @param name Name of symbol.
- * @param symbolFlags Symbol flags.
- *
- * @return Symbol for given name or null for redefinition.
- */
- private Symbol defineSymbol(final Block block, final String name, final int symbolFlags) {
- int flags = symbolFlags;
- Symbol symbol = findSymbol(block, name); // Locate symbol.
- final boolean isGlobal = (flags & KINDMASK) == IS_GLOBAL;
-
- if (isGlobal) {
- flags |= IS_SCOPE;
- }
-
- final FunctionNode function = lc.getFunction(block);
- if (symbol != null) {
- // Symbol was already defined. Check if it needs to be redefined.
- if ((flags & KINDMASK) == IS_PARAM) {
- if (!isLocal(function, symbol)) {
- // Not defined in this function. Create a new definition.
- symbol = null;
- } else if (symbol.isParam()) {
- // Duplicate parameter. Null return will force an error.
- assert false : "duplicate parameter";
- return null;
- }
- } else if ((flags & KINDMASK) == IS_VAR) {
- if ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET) {
- // Always create a new definition.
- symbol = null;
- } else {
- // Not defined in this function. Create a new definition.
- if (!isLocal(function, symbol) || symbol.less(IS_VAR)) {
- symbol = null;
- }
- }
- }
- }
-
- if (symbol == null) {
- // If not found, then create a new one.
- Block symbolBlock;
-
- // Determine where to create it.
- if ((flags & Symbol.KINDMASK) == IS_VAR && ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET)) {
- symbolBlock = block; //internal vars are always defined in the block closest to them
- } else if (isGlobal) {
- symbolBlock = lc.getOutermostFunction().getBody();
- } else {
- symbolBlock = lc.getFunctionBody(function);
- }
-
- // Create and add to appropriate block.
- symbol = new Symbol(name, flags);
- symbolBlock.putSymbol(lc, symbol);
-
- if ((flags & Symbol.KINDMASK) != IS_GLOBAL) {
- symbol.setNeedsSlot(true);
- }
- } else if (symbol.less(flags)) {
- symbol.setFlags(flags);
- }
-
- return symbol;
- }
-
- @Override
- public boolean enterFunctionNode(final FunctionNode functionNode) {
- start(functionNode, false);
-
- if (functionNode.isLazy()) {
- return false;
- }
-
- //an outermost function in our lexical context that is not a program (runScript)
- //is possible - it is a function being compiled lazily
- if (functionNode.isDeclared()) {
- final Iterator<Block> blocks = lc.getBlocks();
- if (blocks.hasNext()) {
- defineSymbol(blocks.next(), functionNode.getIdent().getName(), IS_VAR);
- }
- }
-
- returnTypes.push(functionNode.getReturnType());
- pushLocalsFunction();
-
- return true;
- }
-
- @Override
- public Node leaveFunctionNode(final FunctionNode functionNode) {
- FunctionNode newFunctionNode = functionNode;
-
- final Block body = newFunctionNode.getBody();
-
- //look for this function in the parent block
- if (functionNode.isDeclared()) {
- final Iterator<Block> blocks = lc.getBlocks();
- if (blocks.hasNext()) {
- newFunctionNode = (FunctionNode)newFunctionNode.setSymbol(lc, findSymbol(blocks.next(), functionNode.getIdent().getName()));
- }
- } else if (!functionNode.isProgram()) {
- final boolean anonymous = functionNode.isAnonymous();
- final String name = anonymous ? null : functionNode.getIdent().getName();
- if (anonymous || body.getExistingSymbol(name) != null) {
- newFunctionNode = (FunctionNode)ensureSymbol(FunctionNode.FUNCTION_TYPE, newFunctionNode);
- } else {
- assert name != null;
- final Symbol self = body.getExistingSymbol(name);
- assert self != null && self.isFunctionSelf();
- newFunctionNode = (FunctionNode)newFunctionNode.setSymbol(lc, body.getExistingSymbol(name));
- }
- }
-
- //unknown parameters are promoted to object type.
- if (newFunctionNode.hasLazyChildren()) {
- //the final body has already been assigned as we have left the function node block body by now
- objectifySymbols(body);
- }
- newFunctionNode = finalizeParameters(newFunctionNode);
- newFunctionNode = finalizeTypes(newFunctionNode);
- for (final Symbol symbol : newFunctionNode.getDeclaredSymbols()) {
- if (symbol.getSymbolType().isUnknown()) {
- symbol.setType(Type.OBJECT);
- symbol.setCanBeUndefined();
- }
- }
-
- List<VarNode> syntheticInitializers = null;
-
- if (body.getFlag(Block.NEEDS_SELF_SYMBOL)) {
- syntheticInitializers = new ArrayList<>(2);
- LOG.info("Accepting self symbol init for ", newFunctionNode.getName());
- // "var fn = :callee"
- syntheticInitializers.add(createSyntheticInitializer(newFunctionNode.getIdent(), CALLEE, newFunctionNode));
- }
-
- if (newFunctionNode.needsArguments()) {
- if (syntheticInitializers == null) {
- syntheticInitializers = new ArrayList<>(1);
- }
- // "var arguments = :arguments"
- syntheticInitializers.add(createSyntheticInitializer(createImplicitIdentifier(ARGUMENTS_VAR.symbolName()),
- ARGUMENTS, newFunctionNode));
- }
-
- if (syntheticInitializers != null) {
- final List<Statement> stmts = newFunctionNode.getBody().getStatements();
- final List<Statement> newStatements = new ArrayList<>(stmts.size() + syntheticInitializers.size());
- newStatements.addAll(syntheticInitializers);
- newStatements.addAll(stmts);
- newFunctionNode = newFunctionNode.setBody(lc, newFunctionNode.getBody().setStatements(lc, newStatements));
- }
-
- if (returnTypes.peek().isUnknown()) {
- LOG.info("Unknown return type promoted to object");
- newFunctionNode = newFunctionNode.setReturnType(lc, Type.OBJECT);
- }
- final Type returnType = returnTypes.pop();
- newFunctionNode = newFunctionNode.setReturnType(lc, returnType.isUnknown() ? Type.OBJECT : returnType);
- newFunctionNode = newFunctionNode.setState(lc, CompilationState.ATTR);
-
- popLocals();
-
- end(newFunctionNode, false);
-
- return newFunctionNode;
- }
-
- /**
- * Creates a synthetic initializer for a variable (a var statement that doesn't occur in the source code). Typically
- * used to create assignmnent of {@code :callee} to the function name symbol in self-referential function
- * expressions as well as for assignment of {@code :arguments} to {@code arguments}.
- *
- * @param name the ident node identifying the variable to initialize
- * @param initConstant the compiler constant it is initialized to
- * @param fn the function node the assignment is for
- * @return a var node with the appropriate assignment
- */
- private VarNode createSyntheticInitializer(final IdentNode name, final CompilerConstants initConstant, final FunctionNode fn) {
- final IdentNode init = compilerConstant(initConstant);
- assert init.getSymbol() != null && init.getSymbol().hasSlot();
-
- final VarNode synthVar = new VarNode(fn.getLineNumber(), fn.getToken(), fn.getFinish(), name, init);
-
- final Symbol nameSymbol = fn.getBody().getExistingSymbol(name.getName());
- assert nameSymbol != null;
-
- return synthVar.setName((IdentNode)name.setSymbol(lc, nameSymbol));
- }
-
- @Override
- public Node leaveIdentNode(final IdentNode identNode) {
- final String name = identNode.getName();
-
- if (identNode.isPropertyName()) {
- // assign a pseudo symbol to property name
- final Symbol pseudoSymbol = pseudoSymbol(name);
- LOG.info("IdentNode is property name -> assigning pseudo symbol ", pseudoSymbol);
- LOG.unindent();
- return end(identNode.setSymbol(lc, pseudoSymbol));
- }
-
- final Block block = lc.getCurrentBlock();
-
- Symbol symbol = findSymbol(block, name);
-
- //If an existing symbol with the name is found, use that otherwise, declare a new one
- if (symbol != null) {
- LOG.info("Existing symbol = ", symbol);
- if (symbol.isFunctionSelf()) {
- final FunctionNode functionNode = lc.getDefiningFunction(symbol);
- assert functionNode != null;
- assert lc.getFunctionBody(functionNode).getExistingSymbol(CALLEE.symbolName()) != null;
- lc.setFlag(functionNode.getBody(), Block.NEEDS_SELF_SYMBOL);
- newType(symbol, FunctionNode.FUNCTION_TYPE);
- } else if (!identNode.isInitializedHere()) {
- /*
- * See NASHORN-448, JDK-8016235
- *
- * Here is a use outside the local def scope
- * the inCatch check is a conservative approach to handle things that might have only been
- * defined in the try block, but with variable declarations, which due to JavaScript rules
- * have to be lifted up into the function scope outside the try block anyway, but as the
- * flow can fault at almost any place in the try block and get us to the catch block, all we
- * know is that we have a declaration, not a definition. This can be made better and less
- * conservative once we superimpose a CFG onto the AST.
- */
- if (!isLocalDef(name) || inCatch()) {
- newType(symbol, Type.OBJECT);
- symbol.setCanBeUndefined();
- }
- }
-
- // if symbol is non-local or we're in a with block, we need to put symbol in scope (if it isn't already)
- maybeForceScope(symbol);
- } else {
- LOG.info("No symbol exists. Declare undefined: ", symbol);
- symbol = defineSymbol(block, name, IS_GLOBAL);
- // we have never seen this before, it can be undefined
- newType(symbol, Type.OBJECT); // TODO unknown -we have explicit casts anyway?
- symbol.setCanBeUndefined();
- Symbol.setSymbolIsScope(lc, symbol);
- }
-
- setBlockScope(name, symbol);
-
- if (!identNode.isInitializedHere()) {
- symbol.increaseUseCount();
- }
- addLocalUse(identNode.getName());
-
- return end(identNode.setSymbol(lc, symbol));
- }
-
- private boolean inCatch() {
- return catchNestingLevel > 0;
- }
-
- /**
- * If the symbol isn't already a scope symbol, and it is either not local to the current function, or it is being
- * referenced from within a with block, we force it to be a scope symbol.
- * @param symbol the symbol that might be scoped
- */
- private void maybeForceScope(final Symbol symbol) {
- if (!symbol.isScope() && symbolNeedsToBeScope(symbol)) {
- Symbol.setSymbolIsScope(lc, symbol);
- }
- }
-
- private boolean symbolNeedsToBeScope(final Symbol symbol) {
- if (symbol.isThis() || symbol.isInternal()) {
- return false;
- }
- boolean previousWasBlock = false;
- for (final Iterator<LexicalContextNode> it = lc.getAllNodes(); it.hasNext();) {
- final LexicalContextNode node = it.next();
- if (node instanceof FunctionNode) {
- // We reached the function boundary without seeing a definition for the symbol - it needs to be in
- // scope.
- return true;
- } else if (node instanceof WithNode) {
- if (previousWasBlock) {
- // We reached a WithNode; the symbol must be scoped. Note that if the WithNode was not immediately
- // preceded by a block, this means we're currently processing its expression, not its body,
- // therefore it doesn't count.
- return true;
- }
- previousWasBlock = false;
- } else if (node instanceof Block) {
- if (((Block)node).getExistingSymbol(symbol.getName()) == symbol) {
- // We reached the block that defines the symbol without reaching either the function boundary, or a
- // WithNode. The symbol need not be scoped.
- return false;
- }
- previousWasBlock = true;
- } else {
- previousWasBlock = false;
- }
- }
- throw new AssertionError();
- }
-
- private void setBlockScope(final String name, final Symbol symbol) {
- assert symbol != null;
- if (symbol.isGlobal()) {
- setUsesGlobalSymbol();
- return;
- }
-
- if (symbol.isScope()) {
- Block scopeBlock = null;
- for (final Iterator<LexicalContextNode> contextNodeIter = lc.getAllNodes(); contextNodeIter.hasNext(); ) {
- final LexicalContextNode node = contextNodeIter.next();
- if (node instanceof Block) {
- if (((Block)node).getExistingSymbol(name) != null) {
- scopeBlock = (Block)node;
- break;
- }
- } else if (node instanceof FunctionNode) {
- lc.setFlag(node, FunctionNode.USES_ANCESTOR_SCOPE);
- }
- }
-
- if (scopeBlock != null) {
- assert lc.contains(scopeBlock);
- lc.setBlockNeedsScope(scopeBlock);
- }
- }
- }
-
- /**
- * Marks the current function as one using any global symbol. The function and all its parent functions will all be
- * marked as needing parent scope.
- * @see #needsParentScope()
- */
- private void setUsesGlobalSymbol() {
- for (final Iterator<FunctionNode> fns = lc.getFunctions(); fns.hasNext();) {
- lc.setFlag(fns.next(), FunctionNode.USES_ANCESTOR_SCOPE);
- }
- }
-
- /**
- * Search for symbol in the lexical context starting from the given block.
- * @param name Symbol name.
- * @return Found symbol or null if not found.
- */
- private Symbol findSymbol(final Block block, final String name) {
- // Search up block chain to locate symbol.
-
- for (final Iterator<Block> blocks = lc.getBlocks(block); blocks.hasNext();) {
- // Find name.
- final Symbol symbol = blocks.next().getExistingSymbol(name);
- // If found then we are good.
- if (symbol != null) {
- return symbol;
- }
- }
- return null;
- }
-
- @Override
- public Node leaveIndexNode(final IndexNode indexNode) {
- return end(ensureSymbol(Type.OBJECT, indexNode));
- }
-
- @SuppressWarnings("rawtypes")
- @Override
- public Node leaveLiteralNode(final LiteralNode literalNode) {
- assert !literalNode.isTokenType(TokenType.THIS) : "tokentype for " + literalNode + " is this"; //guard against old dead code case. literal nodes should never inherit tokens
- assert literalNode instanceof ArrayLiteralNode || !(literalNode.getValue() instanceof Node) : "literals with Node values not supported";
- final Symbol symbol = new Symbol(lc.getCurrentFunction().uniqueName(LITERAL_PREFIX.symbolName()), IS_CONSTANT, literalNode.getType());
- if (literalNode instanceof ArrayLiteralNode) {
- ((ArrayLiteralNode)literalNode).analyze();
- }
- return end(literalNode.setSymbol(lc, symbol));
- }
-
- @Override
- public boolean enterObjectNode(final ObjectNode objectNode) {
- return start(objectNode);
- }
-
- @Override
- public Node leaveObjectNode(final ObjectNode objectNode) {
- return end(ensureSymbol(Type.OBJECT, objectNode));
- }
-
- @Override
- public Node leaveReturnNode(final ReturnNode returnNode) {
- final Expression expr = returnNode.getExpression();
- final Type returnType;
-
- if (expr != null) {
- //we can't do parameter specialization if we return something that hasn't been typed yet
- final Symbol symbol = expr.getSymbol();
- if (expr.getType().isUnknown() && symbol.isParam()) {
- symbol.setType(Type.OBJECT);
- }
-
- returnType = widestReturnType(returnTypes.pop(), symbol.getSymbolType());
- } else {
- returnType = Type.OBJECT; //undefined
- }
- LOG.info("Returntype is now ", returnType);
- returnTypes.push(returnType);
-
- end(returnNode);
-
- return returnNode;
- }
-
- @Override
- public Node leaveSwitchNode(final SwitchNode switchNode) {
- Type type = Type.UNKNOWN;
-
- final List<CaseNode> newCases = new ArrayList<>();
- for (final CaseNode caseNode : switchNode.getCases()) {
- final Node test = caseNode.getTest();
-
- CaseNode newCaseNode = caseNode;
- if (test != null) {
- if (test instanceof LiteralNode) {
- //go down to integers if we can
- final LiteralNode<?> lit = (LiteralNode<?>)test;
- if (lit.isNumeric() && !(lit.getValue() instanceof Integer)) {
- if (JSType.isRepresentableAsInt(lit.getNumber())) {
- newCaseNode = caseNode.setTest((Expression)LiteralNode.newInstance(lit, lit.getInt32()).accept(this));
- }
- }
- } else {
- // the "all integer" case that CodeGenerator optimizes for currently assumes literals only
- type = Type.OBJECT;
- }
-
- final Type newCaseType = newCaseNode.getTest().getType();
- if (newCaseType.isBoolean()) {
- type = Type.OBJECT; //booleans and integers aren't assignment compatible
- } else {
- type = Type.widest(type, newCaseType);
- }
- }
-
- newCases.add(newCaseNode);
- }
-
- //only optimize for all integers
- if (!type.isInteger()) {
- type = Type.OBJECT;
- }
-
- switchNode.setTag(newInternal(lc.getCurrentFunction().uniqueName(SWITCH_TAG_PREFIX.symbolName()), type));
-
- end(switchNode);
-
- return switchNode.setCases(lc, newCases);
- }
-
- @Override
- public Node leaveTryNode(final TryNode tryNode) {
- tryNode.setException(exceptionSymbol());
-
- if (tryNode.getFinallyBody() != null) {
- tryNode.setFinallyCatchAll(exceptionSymbol());
- }
-
- end(tryNode);
-
- return tryNode;
- }
-
- @Override
- public boolean enterVarNode(final VarNode varNode) {
- start(varNode);
-
- final IdentNode ident = varNode.getName();
- final String name = ident.getName();
-
- final Symbol symbol = defineSymbol(lc.getCurrentBlock(), name, IS_VAR);
- assert symbol != null;
-
- // NASHORN-467 - use before definition of vars - conservative
- if (isLocalUse(ident.getName())) {
- newType(symbol, Type.OBJECT);
- symbol.setCanBeUndefined();
- }
-
- return true;
- }
-
- @Override
- public Node leaveVarNode(final VarNode varNode) {
- final Expression init = varNode.getInit();
- final IdentNode ident = varNode.getName();
- final String name = ident.getName();
-
- final Symbol symbol = findSymbol(lc.getCurrentBlock(), name);
- assert ident.getSymbol() == symbol;
-
- if (init == null) {
- // var x; with no init will be treated like a use of x by
- // leaveIdentNode unless we remove the name from the localdef list.
- removeLocalDef(name);
- return end(varNode);
- }
-
- addLocalDef(name);
-
- assert symbol != null;
-
- final IdentNode newIdent = (IdentNode)ident.setSymbol(lc, symbol);
-
- final VarNode newVarNode = varNode.setName(newIdent);
-
- final boolean isScript = lc.getDefiningFunction(symbol).isProgram(); //see NASHORN-56
- if ((init.getType().isNumeric() || init.getType().isBoolean()) && !isScript) {
- // Forbid integers as local vars for now as we have no way to treat them as undefined
- newType(symbol, init.getType());
- } else {
- newType(symbol, Type.OBJECT);
- }
-
- assert newVarNode.getName().hasType() : newVarNode + " has no type";
-
- return end(newVarNode);
- }
-
- @Override
- public Node leaveADD(final UnaryNode unaryNode) {
- return end(ensureSymbol(arithType(), unaryNode));
- }
-
- @Override
- public Node leaveBIT_NOT(final UnaryNode unaryNode) {
- return end(ensureSymbol(Type.INT, unaryNode));
- }
-
- @Override
- public Node leaveDECINC(final UnaryNode unaryNode) {
- // @see assignOffset
- final Type type = arithType();
- newType(unaryNode.rhs().getSymbol(), type);
- return end(ensureSymbol(type, unaryNode));
- }
-
- @Override
- public Node leaveDELETE(final UnaryNode unaryNode) {
- final FunctionNode currentFunctionNode = lc.getCurrentFunction();
- final boolean strictMode = currentFunctionNode.isStrict();
- final Expression rhs = unaryNode.rhs();
- final Expression strictFlagNode = (Expression)LiteralNode.newInstance(unaryNode, strictMode).accept(this);
-
- Request request = Request.DELETE;
- final List<Expression> args = new ArrayList<>();
-
- if (rhs instanceof IdentNode) {
- // If this is a declared variable or a function parameter, delete always fails (except for globals).
- final String name = ((IdentNode)rhs).getName();
-
- final boolean failDelete = strictMode || rhs.getSymbol().isParam() || (rhs.getSymbol().isVar() && !isProgramLevelSymbol(name));
-
- if (failDelete && rhs.getSymbol().isThis()) {
- return LiteralNode.newInstance(unaryNode, true).accept(this);
- }
- final Expression literalNode = (Expression)LiteralNode.newInstance(unaryNode, name).accept(this);
-
- if (!failDelete) {
- args.add(compilerConstant(SCOPE));
- }
- args.add(literalNode);
- args.add(strictFlagNode);
-
- if (failDelete) {
- request = Request.FAIL_DELETE;
- }
- } else if (rhs instanceof AccessNode) {
- final Expression base = ((AccessNode)rhs).getBase();
- final IdentNode property = ((AccessNode)rhs).getProperty();
-
- args.add(base);
- args.add((Expression)LiteralNode.newInstance(unaryNode, property.getName()).accept(this));
- args.add(strictFlagNode);
-
- } else if (rhs instanceof IndexNode) {
- final IndexNode indexNode = (IndexNode)rhs;
- final Expression base = indexNode.getBase();
- final Expression index = indexNode.getIndex();
-
- args.add(base);
- args.add(index);
- args.add(strictFlagNode);
-
- } else {
- return LiteralNode.newInstance(unaryNode, true).accept(this);
- }
-
- final RuntimeNode runtimeNode = new RuntimeNode(unaryNode, request, args);
- assert runtimeNode.getSymbol() == unaryNode.getSymbol(); //unary parent constructor should do this
-
- return leaveRuntimeNode(runtimeNode);
- }
-
- /**
- * Is the symbol denoted by the specified name in the current lexical context defined in the program level
- * @param name the name of the symbol
- * @return true if the symbol denoted by the specified name in the current lexical context defined in the program level.
- */
- private boolean isProgramLevelSymbol(final String name) {
- for(final Iterator<Block> it = lc.getBlocks(); it.hasNext();) {
- final Block next = it.next();
- if(next.getExistingSymbol(name) != null) {
- return next == lc.getFunctionBody(lc.getOutermostFunction());
- }
- }
- throw new AssertionError("Couldn't find symbol " + name + " in the context");
- }
-
- @Override
- public Node leaveNEW(final UnaryNode unaryNode) {
- return end(ensureSymbol(Type.OBJECT, unaryNode.setRHS(((CallNode)unaryNode.rhs()).setIsNew())));
- }
-
- @Override
- public Node leaveNOT(final UnaryNode unaryNode) {
- return end(ensureSymbol(Type.BOOLEAN, unaryNode));
- }
-
- private IdentNode compilerConstant(final CompilerConstants cc) {
- return (IdentNode)createImplicitIdentifier(cc.symbolName()).setSymbol(lc, lc.getCurrentFunction().compilerConstant(cc));
- }
-
- /**
- * Creates an ident node for an implicit identifier within the function (one not declared in the script source
- * code). These identifiers are defined with function's token and finish.
- * @param name the name of the identifier
- * @return an ident node representing the implicit identifier.
- */
- private IdentNode createImplicitIdentifier(final String name) {
- final FunctionNode fn = lc.getCurrentFunction();
- return new IdentNode(fn.getToken(), fn.getFinish(), name);
- }
-
- @Override
- public Node leaveTYPEOF(final UnaryNode unaryNode) {
- final Expression rhs = unaryNode.rhs();
-
- final List<Expression> args = new ArrayList<>();
- if (rhs instanceof IdentNode && !rhs.getSymbol().isParam() && !rhs.getSymbol().isVar()) {
- args.add(compilerConstant(SCOPE));
- args.add((Expression)LiteralNode.newInstance(rhs, ((IdentNode)rhs).getName()).accept(this)); //null
- } else {
- args.add(rhs);
- args.add((Expression)LiteralNode.newInstance(unaryNode).accept(this)); //null, do not reuse token of identifier rhs, it can be e.g. 'this'
- }
-
- RuntimeNode runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args);
- assert runtimeNode.getSymbol() == unaryNode.getSymbol();
-
- runtimeNode = (RuntimeNode)leaveRuntimeNode(runtimeNode);
-
- end(unaryNode);
-
- return runtimeNode;
- }
-
- @Override
- public Node leaveRuntimeNode(final RuntimeNode runtimeNode) {
- return end(ensureSymbol(runtimeNode.getRequest().getReturnType(), runtimeNode));
- }
-
- @Override
- public Node leaveSUB(final UnaryNode unaryNode) {
- return end(ensureSymbol(arithType(), unaryNode));
- }
-
- @Override
- public Node leaveVOID(final UnaryNode unaryNode) {
- return end(ensureSymbol(Type.OBJECT, unaryNode));
- }
-
- /**
- * Add is a special binary, as it works not only on arithmetic, but for
- * strings etc as well.
- */
- @Override
- public Node leaveADD(final BinaryNode binaryNode) {
- final Expression lhs = binaryNode.lhs();
- final Expression rhs = binaryNode.rhs();
-
- ensureTypeNotUnknown(lhs);
- ensureTypeNotUnknown(rhs);
- //even if we are adding two known types, this can overflow. i.e.
- //int and number -> number.
- //int and int are also number though.
- //something and object is object
- return end(ensureSymbol(Type.widest(arithType(), Type.widest(lhs.getType(), rhs.getType())), binaryNode));
- }
-
- @Override
- public Node leaveAND(final BinaryNode binaryNode) {
- return end(ensureSymbol(Type.OBJECT, binaryNode));
- }
-
- /**
- * This is a helper called before an assignment.
- * @param binaryNode assignment node
- */
- private boolean enterAssignmentNode(final BinaryNode binaryNode) {
- start(binaryNode);
-
- return true;
- }
-
-
- /**
- * This assign helper is called after an assignment, when all children of
- * the assign has been processed. It fixes the types and recursively makes
- * sure that everyhing has slots that should have them in the chain.
- *
- * @param binaryNode assignment node
- */
- private Node leaveAssignmentNode(final BinaryNode binaryNode) {
- final Expression lhs = binaryNode.lhs();
- final Expression rhs = binaryNode.rhs();
- final Type type;
-
- if (lhs instanceof IdentNode) {
- final Block block = lc.getCurrentBlock();
- final IdentNode ident = (IdentNode)lhs;
- final String name = ident.getName();
- final Symbol symbol = findSymbol(block, name);
-
- if (symbol == null) {
- defineSymbol(block, name, IS_GLOBAL);
- } else {
- maybeForceScope(symbol);
- }
-
- addLocalDef(name);
- }
-
- if (rhs.getType().isNumeric()) {
- type = Type.widest(lhs.getType(), rhs.getType());
- } else {
- type = Type.OBJECT; //force lhs to be an object if not numeric assignment, e.g. strings too.
- }
-
- newType(lhs.getSymbol(), type);
- return end(ensureSymbol(type, binaryNode));
- }
-
- private boolean isLocal(final FunctionNode function, final Symbol symbol) {
- final FunctionNode definingFn = lc.getDefiningFunction(symbol);
- // Temp symbols are not assigned to a block, so their defining fn is null; those can be assumed local
- return definingFn == null || definingFn == function;
- }
-
- @Override
- public boolean enterASSIGN(final BinaryNode binaryNode) {
- return enterAssignmentNode(binaryNode);
- }
-
- @Override
- public Node leaveASSIGN(final BinaryNode binaryNode) {
- return leaveAssignmentNode(binaryNode);
- }
-
- @Override
- public boolean enterASSIGN_ADD(final BinaryNode binaryNode) {
- return enterAssignmentNode(binaryNode);
- }
-
- @Override
- public Node leaveASSIGN_ADD(final BinaryNode binaryNode) {
- final Expression lhs = binaryNode.lhs();
- final Expression rhs = binaryNode.rhs();
-
- final Type widest = Type.widest(lhs.getType(), rhs.getType());
- //Type.NUMBER if we can't prove that the add doesn't overflow. todo
- return leaveSelfModifyingAssignmentNode(binaryNode, widest.isNumeric() ? Type.NUMBER : Type.OBJECT);
- }
-
- @Override
- public boolean enterASSIGN_BIT_AND(final BinaryNode binaryNode) {
- return enterAssignmentNode(binaryNode);
- }
-
- @Override
- public Node leaveASSIGN_BIT_AND(final BinaryNode binaryNode) {
- return leaveSelfModifyingAssignmentNode(binaryNode);
- }
-
- @Override
- public boolean enterASSIGN_BIT_OR(final BinaryNode binaryNode) {
- return enterAssignmentNode(binaryNode);
- }
-
- @Override
- public Node leaveASSIGN_BIT_OR(final BinaryNode binaryNode) {
- return leaveSelfModifyingAssignmentNode(binaryNode);
- }
-
- @Override
- public boolean enterASSIGN_BIT_XOR(final BinaryNode binaryNode) {
- return enterAssignmentNode(binaryNode);
- }
-
- @Override
- public Node leaveASSIGN_BIT_XOR(final BinaryNode binaryNode) {
- return leaveSelfModifyingAssignmentNode(binaryNode);
- }
-
- @Override
- public boolean enterASSIGN_DIV(final BinaryNode binaryNode) {
- return enterAssignmentNode(binaryNode);
- }
-
- @Override
- public Node leaveASSIGN_DIV(final BinaryNode binaryNode) {
- return leaveSelfModifyingAssignmentNode(binaryNode);
- }
-
- @Override
- public boolean enterASSIGN_MOD(final BinaryNode binaryNode) {
- return enterAssignmentNode(binaryNode);
- }
-
- @Override
- public Node leaveASSIGN_MOD(final BinaryNode binaryNode) {
- return leaveSelfModifyingAssignmentNode(binaryNode);
- }
-
- @Override
- public boolean enterASSIGN_MUL(final BinaryNode binaryNode) {
- return enterAssignmentNode(binaryNode);
- }
-
- @Override
- public Node leaveASSIGN_MUL(final BinaryNode binaryNode) {
- return leaveSelfModifyingAssignmentNode(binaryNode);
- }
-
- @Override
- public boolean enterASSIGN_SAR(final BinaryNode binaryNode) {
- return enterAssignmentNode(binaryNode);
- }
-
- @Override
- public Node leaveASSIGN_SAR(final BinaryNode binaryNode) {
- return leaveSelfModifyingAssignmentNode(binaryNode);
- }
-
- @Override
- public boolean enterASSIGN_SHL(final BinaryNode binaryNode) {
- return enterAssignmentNode(binaryNode);
- }
-
- @Override
- public Node leaveASSIGN_SHL(final BinaryNode binaryNode) {
- return leaveSelfModifyingAssignmentNode(binaryNode);
- }
-
- @Override
- public boolean enterASSIGN_SHR(final BinaryNode binaryNode) {
- return enterAssignmentNode(binaryNode);
- }
-
- @Override
- public Node leaveASSIGN_SHR(final BinaryNode binaryNode) {
- return leaveSelfModifyingAssignmentNode(binaryNode);
- }
-
- @Override
- public boolean enterASSIGN_SUB(final BinaryNode binaryNode) {
- return enterAssignmentNode(binaryNode);
- }
-
- @Override
- public Node leaveASSIGN_SUB(final BinaryNode binaryNode) {
- return leaveSelfModifyingAssignmentNode(binaryNode);
- }
-
- @Override
- public Node leaveBIT_AND(final BinaryNode binaryNode) {
- return end(coerce(binaryNode, Type.INT));
- }
-
- @Override
- public Node leaveBIT_OR(final BinaryNode binaryNode) {
- return end(coerce(binaryNode, Type.INT));
- }
-
- @Override
- public Node leaveBIT_XOR(final BinaryNode binaryNode) {
- return end(coerce(binaryNode, Type.INT));
- }
-
- @Override
- public Node leaveCOMMARIGHT(final BinaryNode binaryNode) {
- return leaveComma(binaryNode, binaryNode.rhs());
- }
-
- @Override
- public Node leaveCOMMALEFT(final BinaryNode binaryNode) {
- return leaveComma(binaryNode, binaryNode.lhs());
- }
-
- private Node leaveComma(final BinaryNode commaNode, final Expression effectiveExpr) {
- ensureTypeNotUnknown(effectiveExpr);
- return end(ensureSymbol(effectiveExpr.getType(), commaNode));
- }
-
- @Override
- public Node leaveDIV(final BinaryNode binaryNode) {
- return leaveBinaryArithmetic(binaryNode);
- }
-
- private Node leaveCmp(final BinaryNode binaryNode) {
- ensureTypeNotUnknown(binaryNode.lhs());
- ensureTypeNotUnknown(binaryNode.rhs());
- final Type widest = Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType());
- ensureSymbol(widest, binaryNode.lhs());
- ensureSymbol(widest, binaryNode.rhs());
- return end(ensureSymbol(Type.BOOLEAN, binaryNode));
- }
-
- private Node coerce(final BinaryNode binaryNode, final Type operandType, final Type destType) {
- // TODO we currently don't support changing inferred type based on uses, only on
- // definitions. we would need some additional logic. We probably want to do that
- // in the future, if e.g. a specialized method gets parameter that is only used
- // as, say, an int : function(x) { return x & 4711 }, and x is not defined in
- // the function. to make this work, uncomment the following two type inferences
- // and debug.
- //newType(binaryNode.lhs().getSymbol(), operandType);
- //newType(binaryNode.rhs().getSymbol(), operandType);
- return ensureSymbol(destType, binaryNode);
- }
-
- private Node coerce(final BinaryNode binaryNode, final Type type) {
- return coerce(binaryNode, type, type);
- }
-
- //leave a binary node and inherit the widest type of lhs , rhs
- private Node leaveBinaryArithmetic(final BinaryNode binaryNode) {
- assert !Compiler.shouldUseIntegerArithmetic();
- return end(coerce(binaryNode, Type.NUMBER));
- }
-
- @Override
- public Node leaveEQ(final BinaryNode binaryNode) {
- return leaveCmp(binaryNode);
- }
-
- @Override
- public Node leaveEQ_STRICT(final BinaryNode binaryNode) {
- return leaveCmp(binaryNode);
- }
-
- @Override
- public Node leaveGE(final BinaryNode binaryNode) {
- return leaveCmp(binaryNode);
- }
-
- @Override
- public Node leaveGT(final BinaryNode binaryNode) {
- return leaveCmp(binaryNode);
- }
-
- @Override
- public Node leaveIN(final BinaryNode binaryNode) {
- return leaveBinaryRuntimeOperator(binaryNode, Request.IN);
- }
-
- @Override
- public Node leaveINSTANCEOF(final BinaryNode binaryNode) {
- return leaveBinaryRuntimeOperator(binaryNode, Request.INSTANCEOF);
- }
-
- private Node leaveBinaryRuntimeOperator(final BinaryNode binaryNode, final Request request) {
- try {
- // Don't do a full RuntimeNode.accept, as we don't want to double-visit the binary node operands
- return leaveRuntimeNode(new RuntimeNode(binaryNode, request));
- } finally {
- end(binaryNode);
- }
- }
-
- @Override
- public Node leaveLE(final BinaryNode binaryNode) {
- return leaveCmp(binaryNode);
- }
-
- @Override
- public Node leaveLT(final BinaryNode binaryNode) {
- return leaveCmp(binaryNode);
- }
-
- @Override
- public Node leaveMOD(final BinaryNode binaryNode) {
- return leaveBinaryArithmetic(binaryNode);
- }
-
- @Override
- public Node leaveMUL(final BinaryNode binaryNode) {
- return leaveBinaryArithmetic(binaryNode);
- }
-
- @Override
- public Node leaveNE(final BinaryNode binaryNode) {
- return leaveCmp(binaryNode);
- }
-
- @Override
- public Node leaveNE_STRICT(final BinaryNode binaryNode) {
- return leaveCmp(binaryNode);
- }
-
- @Override
- public Node leaveOR(final BinaryNode binaryNode) {
- return end(ensureSymbol(Type.OBJECT, binaryNode));
- }
-
- @Override
- public Node leaveSAR(final BinaryNode binaryNode) {
- return end(coerce(binaryNode, Type.INT));
- }
-
- @Override
- public Node leaveSHL(final BinaryNode binaryNode) {
- return end(coerce(binaryNode, Type.INT));
- }
-
- @Override
- public Node leaveSHR(final BinaryNode binaryNode) {
- return end(coerce(binaryNode, Type.LONG));
- }
-
- @Override
- public Node leaveSUB(final BinaryNode binaryNode) {
- return leaveBinaryArithmetic(binaryNode);
- }
-
- @Override
- public Node leaveForNode(final ForNode forNode) {
- if (forNode.isForIn()) {
- forNode.setIterator(newInternal(lc.getCurrentFunction().uniqueName(ITERATOR_PREFIX.symbolName()), Type.typeFor(ITERATOR_PREFIX.type()))); //NASHORN-73
- /*
- * Iterators return objects, so we need to widen the scope of the
- * init variable if it, for example, has been assigned double type
- * see NASHORN-50
- */
- newType(forNode.getInit().getSymbol(), Type.OBJECT);
- }
-
- end(forNode);
-
- return forNode;
- }
-
- @Override
- public Node leaveTernaryNode(final TernaryNode ternaryNode) {
- final Expression trueExpr = ternaryNode.getTrueExpression();
- final Expression falseExpr = ternaryNode.getFalseExpression();
-
- ensureTypeNotUnknown(trueExpr);
- ensureTypeNotUnknown(falseExpr);
-
- final Type type = widestReturnType(trueExpr.getType(), falseExpr.getType());
- return end(ensureSymbol(type, ternaryNode));
- }
-
- /**
- * 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. Also, widening a numeric type to an object type must widen to Object proper and not
- * any more specific subclass (e.g. widest of int/long/double and String is Object).
- * @param t1 type 1
- * @param t2 type 2
- * @return wider of t1 and t2, except if one is boolean and the other is neither boolean nor unknown, or if one is
- * numeric and the other is neither numeric nor unknown in which case {@code Type.OBJECT} is returned.
- */
- private static Type widestReturnType(final Type t1, final Type t2) {
- if (t1.isUnknown()) {
- return t2;
- } else if (t2.isUnknown()) {
- return t1;
- } else if (t1.isBoolean() != t2.isBoolean() || t1.isNumeric() != t2.isNumeric()) {
- return Type.OBJECT;
- }
- return Type.widest(t1, t2);
- }
-
- private void initCompileConstant(final CompilerConstants cc, final Block block, final int flags) {
- final Class<?> type = cc.type();
- // Must not call this method for constants with no explicit types; use the one with (..., Type) signature instead.
- assert type != null;
- initCompileConstant(cc, block, flags, Type.typeFor(type));
- }
-
- private void initCompileConstant(final CompilerConstants cc, final Block block, final int flags, final Type type) {
- final Symbol symbol = defineSymbol(block, cc.symbolName(), flags);
- symbol.setTypeOverride(type);
- symbol.setNeedsSlot(true);
- }
-
- /**
- * Initialize parameters for function node. This may require specializing
- * types if a specialization profile is known
- *
- * @param functionNode the function node
- */
- private void initParameters(final FunctionNode functionNode, final Block body) {
- int pos = 0;
- for (final IdentNode param : functionNode.getParameters()) {
- addLocalDef(param.getName());
-
- final Type callSiteParamType = functionNode.getHints().getParameterType(pos);
- int flags = IS_PARAM;
- if (callSiteParamType != null) {
- LOG.info("Param ", param, " has a callsite type ", callSiteParamType, ". Using that.");
- flags |= Symbol.IS_SPECIALIZED_PARAM;
- }
-
- final Symbol paramSymbol = defineSymbol(body, param.getName(), flags);
- assert paramSymbol != null;
-
- newType(paramSymbol, callSiteParamType == null ? Type.UNKNOWN : callSiteParamType);
-
- LOG.info("Initialized param ", pos, "=", paramSymbol);
- pos++;
- }
-
- }
-
- /**
- * This has to run before fix assignment types, store any type specializations for
- * paramters, then turn then to objects for the generic version of this method
- *
- * @param functionNode functionNode
- */
- private FunctionNode finalizeParameters(final FunctionNode functionNode) {
- final List<IdentNode> newParams = new ArrayList<>();
- final boolean isVarArg = functionNode.isVarArg();
- final int nparams = functionNode.getParameters().size();
-
- int specialize = 0;
- int pos = 0;
- for (final IdentNode param : functionNode.getParameters()) {
- final Symbol paramSymbol = functionNode.getBody().getExistingSymbol(param.getName());
- assert paramSymbol != null;
- assert paramSymbol.isParam();
- newParams.add((IdentNode)param.setSymbol(lc, paramSymbol));
-
- assert paramSymbol != null;
- Type type = functionNode.getHints().getParameterType(pos);
- if (type == null) {
- type = Type.OBJECT;
- }
-
- // if we know that a parameter is only used as a certain type throughout
- // this function, we can tell the runtime system that no matter what the
- // call site is, use this information:
- // we also need more than half of the parameters to be specializable
- // for the heuristic to be worth it, and we need more than one use of
- // the parameter to consider it, i.e. function(x) { call(x); } doens't count
- if (paramSymbol.getUseCount() > 1 && !paramSymbol.getSymbolType().isObject()) {
- LOG.finest("Parameter ", param, " could profit from specialization to ", paramSymbol.getSymbolType());
- specialize++;
- }
-
- newType(paramSymbol, Type.widest(type, paramSymbol.getSymbolType()));
-
- // parameters should not be slots for a function that uses variable arity signature
- if (isVarArg) {
- paramSymbol.setNeedsSlot(false);
- }
-
- pos++;
- }
-
- FunctionNode newFunctionNode = functionNode;
-
- if (nparams == 0 || (specialize * 2) < nparams) {
- newFunctionNode = newFunctionNode.clearSnapshot(lc);
- }
-
- return newFunctionNode.setParameters(lc, newParams);
- }
-
- /**
- * Move any properties from a global map into the scope of this method
- * @param block the function node body for which to init scope vars
- */
- private void initFromPropertyMap(final Block block) {
- // For a script, add scope symbols as defined in the property map
-
- final PropertyMap map = Context.getGlobalMap();
-
- for (final Property property : map.getProperties()) {
- final String key = property.getKey();
- final Symbol symbol = defineSymbol(block, key, IS_GLOBAL);
- newType(symbol, Type.OBJECT);
- LOG.info("Added global symbol from property map ", symbol);
- }
- }
-
- private static void ensureTypeNotUnknown(final Expression node) {
-
- final Symbol symbol = node.getSymbol();
-
- LOG.info("Ensure type not unknown for: ", symbol);
-
- /*
- * Note that not just unknowns, but params need to be blown
- * up to objects, because we can have something like
- *
- * function f(a) {
- * var b = ~a; //b and a are inferred to be int
- * return b;
- * }
- *
- * In this case, it would be correct to say that "if you have
- * an int at the callsite, just pass it".
- *
- * However
- *
- * function f(a) {
- * var b = ~a; //b and a are inferred to be int
- * return b == 17; //b is still inferred to be int.
- * }
- *
- * can be called with f("17") and if we assume that b is an
- * int and don't blow it up to an object in the comparison, we
- * are screwed. I hate JavaScript.
- *
- * This check has to be done for any operation that might take
- * objects as parameters, for example +, but not *, which is known
- * to coerce types into doubles
- */
- if (node.getType().isUnknown() || (symbol.isParam() && !symbol.isSpecializedParam())) {
- newType(symbol, Type.OBJECT);
- symbol.setCanBeUndefined();
- }
- }
-
- private static Symbol pseudoSymbol(final String name) {
- return new Symbol(name, 0, Type.OBJECT);
- }
-
- private Symbol exceptionSymbol() {
- return newInternal(lc.getCurrentFunction().uniqueName(EXCEPTION_PREFIX.symbolName()), Type.typeFor(EXCEPTION_PREFIX.type()));
- }
-
- /**
- * Return the type that arithmetic ops should use. Until we have implemented better type
- * analysis (range based) or overflow checks that are fast enough for int arithmetic,
- * this is the number type
- * @return the arithetic type
- */
- private static Type arithType() {
- return Compiler.shouldUseIntegerArithmetic() ? Type.INT : Type.NUMBER;
- }
-
- /**
- * If types have changed, we can have failed to update vars. For example
- *
- * var x = 17; //x is int
- * x = "apa"; //x is object. This will be converted fine
- *
- * @param functionNode
- */
- private FunctionNode finalizeTypes(final FunctionNode functionNode) {
- final Set<Node> changed = new HashSet<>();
- FunctionNode currentFunctionNode = functionNode;
- do {
- changed.clear();
- final FunctionNode newFunctionNode = (FunctionNode)currentFunctionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
-
- private Expression widen(final Expression node, final Type to) {
- if (node instanceof LiteralNode) {
- return node;
- }
- final Type from = node.getType();
- if (!Type.areEquivalent(from, to) && Type.widest(from, to) == to) {
- LOG.fine("Had to post pass widen '", node, "' ", Debug.id(node), " from ", node.getType(), " to ", to);
- Symbol symbol = node.getSymbol();
- if (symbol.isShared() && symbol.wouldChangeType(to)) {
- symbol = temporarySymbols.getTypedTemporarySymbol(to);
- }
- newType(symbol, to);
- final Expression newNode = node.setSymbol(lc, symbol);
- changed.add(newNode);
- return newNode;
- }
- return node;
- }
-
- @Override
- public boolean enterFunctionNode(final FunctionNode node) {
- return !node.isLazy();
- }
-
- //
- // Eg.
- //
- // var d = 17;
- // var e;
- // e = d; //initially typed as int for node type, should retype as double
- // e = object;
- //
- // var d = 17;
- // var e;
- // e -= d; //initially type number, should number remain with a final conversion supplied by Store. ugly, but the computation result of the sub is numeric
- // e = object;
- //
- @SuppressWarnings("fallthrough")
- @Override
- public Node leaveBinaryNode(final BinaryNode binaryNode) {
- final Type widest = Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType());
- BinaryNode newBinaryNode = binaryNode;
-
- if (isAdd(binaryNode)) {
- newBinaryNode = (BinaryNode)widen(newBinaryNode, widest);
- if (newBinaryNode.getType().isObject() && !isAddString(newBinaryNode)) {
- return new RuntimeNode(newBinaryNode, Request.ADD);
- }
- } else if (binaryNode.isComparison()) {
- final Expression lhs = newBinaryNode.lhs();
- final Expression rhs = newBinaryNode.rhs();
-
- Type cmpWidest = Type.widest(lhs.getType(), rhs.getType());
-
- boolean newRuntimeNode = false, finalized = false;
- switch (newBinaryNode.tokenType()) {
- case EQ_STRICT:
- case NE_STRICT:
- if (lhs.getType().isBoolean() != rhs.getType().isBoolean()) {
- newRuntimeNode = true;
- cmpWidest = Type.OBJECT;
- finalized = true;
- }
- //fallthru
- default:
- if (newRuntimeNode || cmpWidest.isObject()) {
- return new RuntimeNode(newBinaryNode, Request.requestFor(binaryNode)).setIsFinal(finalized);
- }
- break;
- }
-
- return newBinaryNode;
- } else {
- if (!binaryNode.isAssignment() || binaryNode.isSelfModifying()) {
- return newBinaryNode;
- }
- checkThisAssignment(binaryNode);
- newBinaryNode = newBinaryNode.setLHS(widen(newBinaryNode.lhs(), widest));
- newBinaryNode = (BinaryNode)widen(newBinaryNode, widest);
- }
-
- return newBinaryNode;
-
- }
-
- private boolean isAdd(final Node node) {
- return node.isTokenType(TokenType.ADD);
- }
-
- /**
- * Determine if the outcome of + operator is a string.
- *
- * @param node Node to test.
- * @return true if a string result.
- */
- private boolean isAddString(final Node node) {
- if (node instanceof BinaryNode && isAdd(node)) {
- final BinaryNode binaryNode = (BinaryNode)node;
- final Node lhs = binaryNode.lhs();
- final Node rhs = binaryNode.rhs();
-
- return isAddString(lhs) || isAddString(rhs);
- }
-
- return node instanceof LiteralNode<?> && ((LiteralNode<?>)node).isString();
- }
-
- private void checkThisAssignment(final BinaryNode binaryNode) {
- if (binaryNode.isAssignment()) {
- if (binaryNode.lhs() instanceof AccessNode) {
- final AccessNode accessNode = (AccessNode) binaryNode.lhs();
-
- if (accessNode.getBase().getSymbol().isThis()) {
- lc.getCurrentFunction().addThisProperty(accessNode.getProperty().getName());
- }
- }
- }
- }
- });
- lc.replace(currentFunctionNode, newFunctionNode);
- currentFunctionNode = newFunctionNode;
- } while (!changed.isEmpty());
-
- return currentFunctionNode;
- }
-
- private Node leaveSelfModifyingAssignmentNode(final BinaryNode binaryNode) {
- return leaveSelfModifyingAssignmentNode(binaryNode, binaryNode.getWidestOperationType());
- }
-
- private Node leaveSelfModifyingAssignmentNode(final BinaryNode binaryNode, final Type destType) {
- //e.g. for -=, Number, no wider, destType (binaryNode.getWidestOperationType()) is the coerce type
- final Expression lhs = binaryNode.lhs();
-
- newType(lhs.getSymbol(), destType); //may not narrow if dest is already wider than destType
-
- return end(ensureSymbol(destType, binaryNode));
- }
-
- private Expression ensureSymbol(final Type type, final Expression expr) {
- LOG.info("New TEMPORARY added to ", lc.getCurrentFunction().getName(), " type=", type);
- return temporarySymbols.ensureSymbol(lc, type, expr);
- }
-
- private Symbol newInternal(final String name, final Type type) {
- final Symbol iter = defineSymbol(lc.getCurrentBlock(), name, IS_VAR | IS_INTERNAL);
- iter.setType(type); // NASHORN-73
- return iter;
- }
-
- private static void newType(final Symbol symbol, final Type type) {
- final Type oldType = symbol.getSymbolType();
- symbol.setType(type);
-
- if (symbol.getSymbolType() != oldType) {
- LOG.info("New TYPE ", type, " for ", symbol," (was ", oldType, ")");
- }
-
- if (symbol.isParam()) {
- symbol.setType(type);
- LOG.info("Param type change ", symbol);
- }
- }
-
- private void pushLocalsFunction() {
- localDefs.push(new HashSet<String>());
- localUses.push(new HashSet<String>());
- }
-
- private void pushLocalsBlock() {
- localDefs.push(new HashSet<>(localDefs.peek()));
- localUses.push(new HashSet<>(localUses.peek()));
- }
-
- private void popLocals() {
- localDefs.pop();
- localUses.pop();
- }
-
- private boolean isLocalDef(final String name) {
- return localDefs.peek().contains(name);
- }
-
- private void addLocalDef(final String name) {
- LOG.info("Adding local def of symbol: '", name, "'");
- localDefs.peek().add(name);
- }
-
- private void removeLocalDef(final String name) {
- LOG.info("Removing local def of symbol: '", name, "'");
- localDefs.peek().remove(name);
- }
-
- private boolean isLocalUse(final String name) {
- return localUses.peek().contains(name);
- }
-
- private void addLocalUse(final String name) {
- LOG.info("Adding local use of symbol: '", name, "'");
- localUses.peek().add(name);
- }
-
- /**
- * Pessimistically promote all symbols in current function node to Object types
- * This is done when the function contains unevaluated black boxes such as
- * lazy sub-function nodes that have not been compiled.
- *
- * @param body body for the function node we are leaving
- */
- private static void objectifySymbols(final Block body) {
- body.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
- private void toObject(final Block block) {
- for (final Symbol symbol : block.getSymbols()) {
- if (!symbol.isTemp()) {
- newType(symbol, Type.OBJECT);
- }
- }
- }
-
- @Override
- public boolean enterBlock(final Block block) {
- toObject(block);
- return true;
- }
-
- @Override
- public boolean enterFunctionNode(final FunctionNode node) {
- return false;
- }
- });
- }
-
- private static String name(final Node node) {
- final String cn = node.getClass().getName();
- final int lastDot = cn.lastIndexOf('.');
- if (lastDot == -1) {
- return cn;
- }
- return cn.substring(lastDot + 1);
- }
-
- private boolean start(final Node node) {
- return start(node, true);
- }
-
- private boolean start(final Node node, final boolean printNode) {
- if (DEBUG) {
- final StringBuilder sb = new StringBuilder();
-
- sb.append("[ENTER ").
- append(name(node)).
- append("] ").
- append(printNode ? node.toString() : "").
- append(" in '").
- append(lc.getCurrentFunction().getName()).
- append("'");
- LOG.info(sb);
- LOG.indent();
- }
-
- return true;
- }
-
- private <T extends Node> T end(final T node) {
- return end(node, true);
- }
-
- private <T extends Node> T end(final T node, final boolean printNode) {
- if(node instanceof Statement) {
- // If we're done with a statement, all temporaries can be reused.
- temporarySymbols.reuse();
- }
- if (DEBUG) {
- final StringBuilder sb = new StringBuilder();
-
- sb.append("[LEAVE ").
- append(name(node)).
- append("] ").
- append(printNode ? node.toString() : "").
- append(" in '").
- append(lc.getCurrentFunction().getName()).
- append('\'');
-
- if (node instanceof Expression) {
- final Symbol symbol = ((Expression)node).getSymbol();
- if (symbol == null) {
- sb.append(" <NO SYMBOL>");
- } else {
- sb.append(" <symbol=").append(symbol).append('>');
- }
- }
-
- LOG.unindent();
- LOG.info(sb);
- }
-
- return node;
- }
-}
diff --git a/src/jdk/nashorn/internal/codegen/BranchOptimizer.java b/src/jdk/nashorn/internal/codegen/BranchOptimizer.java
index ad9bdb07..5c8f9645 100644
--- a/src/jdk/nashorn/internal/codegen/BranchOptimizer.java
+++ b/src/jdk/nashorn/internal/codegen/BranchOptimizer.java
@@ -32,10 +32,10 @@ import static jdk.nashorn.internal.codegen.Condition.LE;
import static jdk.nashorn.internal.codegen.Condition.LT;
import static jdk.nashorn.internal.codegen.Condition.NE;
-import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.Expression;
-import jdk.nashorn.internal.ir.TernaryNode;
+import jdk.nashorn.internal.ir.JoinPredecessorExpression;
+import jdk.nashorn.internal.ir.LocalVariableConversion;
import jdk.nashorn.internal.ir.UnaryNode;
/**
@@ -57,7 +57,7 @@ final class BranchOptimizer {
}
private void branchOptimizer(final UnaryNode unaryNode, final Label label, final boolean state) {
- final Expression rhs = unaryNode.rhs();
+ final Expression rhs = unaryNode.getExpression();
switch (unaryNode.tokenType()) {
case NOT:
@@ -71,13 +71,7 @@ final class BranchOptimizer {
break;
}
- // convert to boolean
- codegen.load(unaryNode, Type.BOOLEAN);
- if (state) {
- method.ifne(label);
- } else {
- method.ifeq(label);
- }
+ loadTestAndJump(unaryNode, label, state);
}
private void branchOptimizer(final BinaryNode binaryNode, final Label label, final boolean state) {
@@ -88,86 +82,97 @@ final class BranchOptimizer {
case AND:
if (state) {
final Label skip = new Label("skip");
- branchOptimizer(lhs, skip, false);
- branchOptimizer(rhs, label, true);
+ optimizeLogicalOperand(lhs, skip, false, false);
+ optimizeLogicalOperand(rhs, label, true, true);
method.label(skip);
} else {
- branchOptimizer(lhs, label, false);
- branchOptimizer(rhs, label, false);
+ optimizeLogicalOperand(lhs, label, false, false);
+ optimizeLogicalOperand(rhs, label, false, true);
}
return;
case OR:
if (state) {
- branchOptimizer(lhs, label, true);
- branchOptimizer(rhs, label, true);
+ optimizeLogicalOperand(lhs, label, true, false);
+ optimizeLogicalOperand(rhs, label, true, true);
} else {
final Label skip = new Label("skip");
- branchOptimizer(lhs, skip, true);
- branchOptimizer(rhs, label, false);
+ optimizeLogicalOperand(lhs, skip, true, false);
+ optimizeLogicalOperand(rhs, label, false, true);
method.label(skip);
}
return;
case EQ:
case EQ_STRICT:
- codegen.loadBinaryOperands(lhs, rhs, Type.widest(lhs.getType(), rhs.getType()));
+ codegen.loadBinaryOperands(binaryNode);
method.conditionalJump(state ? EQ : NE, true, label);
return;
case NE:
case NE_STRICT:
- codegen.loadBinaryOperands(lhs, rhs, Type.widest(lhs.getType(), rhs.getType()));
+ codegen.loadBinaryOperands(binaryNode);
method.conditionalJump(state ? NE : EQ, true, label);
return;
case GE:
- codegen.loadBinaryOperands(lhs, rhs, Type.widest(lhs.getType(), rhs.getType()));
- method.conditionalJump(state ? GE : LT, !state, label);
+ codegen.loadBinaryOperands(binaryNode);
+ method.conditionalJump(state ? GE : LT, false, label);
return;
case GT:
- codegen.loadBinaryOperands(lhs, rhs, Type.widest(lhs.getType(), rhs.getType()));
- method.conditionalJump(state ? GT : LE, !state, label);
+ codegen.loadBinaryOperands(binaryNode);
+ method.conditionalJump(state ? GT : LE, false, label);
return;
case LE:
- codegen.loadBinaryOperands(lhs, rhs, Type.widest(lhs.getType(), rhs.getType()));
- method.conditionalJump(state ? LE : GT, state, label);
+ codegen.loadBinaryOperands(binaryNode);
+ method.conditionalJump(state ? LE : GT, true, label);
return;
case LT:
- codegen.loadBinaryOperands(lhs, rhs, Type.widest(lhs.getType(), rhs.getType()));
- method.conditionalJump(state ? LT : GE, state, label);
+ codegen.loadBinaryOperands(binaryNode);
+ method.conditionalJump(state ? LT : GE, true, label);
return;
default:
break;
}
- codegen.load(binaryNode, Type.BOOLEAN);
- if (state) {
- method.ifne(label);
+ loadTestAndJump(binaryNode, label, state);
+ }
+
+ private void optimizeLogicalOperand(final Expression expr, final Label label, final boolean state, final boolean isRhs) {
+ final JoinPredecessorExpression jpexpr = (JoinPredecessorExpression)expr;
+ if(LocalVariableConversion.hasLiveConversion(jpexpr)) {
+ final Label after = new Label("after");
+ branchOptimizer(jpexpr.getExpression(), after, !state);
+ method.beforeJoinPoint(jpexpr);
+ method._goto(label);
+ method.label(after);
+ if(isRhs) {
+ method.beforeJoinPoint(jpexpr);
+ }
} else {
- method.ifeq(label);
+ branchOptimizer(jpexpr.getExpression(), label, state);
}
}
-
private void branchOptimizer(final Expression node, final Label label, final boolean state) {
- if (!(node instanceof TernaryNode)) {
-
- if (node instanceof BinaryNode) {
- branchOptimizer((BinaryNode)node, label, state);
- return;
- }
+ if (node instanceof BinaryNode) {
+ branchOptimizer((BinaryNode)node, label, state);
+ return;
+ }
- if (node instanceof UnaryNode) {
- branchOptimizer((UnaryNode)node, label, state);
- return;
- }
+ if (node instanceof UnaryNode) {
+ branchOptimizer((UnaryNode)node, label, state);
+ return;
}
- codegen.load(node, Type.BOOLEAN);
+ loadTestAndJump(node, label, state);
+ }
+
+ private void loadTestAndJump(final Expression node, final Label label, final boolean state) {
+ codegen.loadExpressionAsBoolean(node);
if (state) {
method.ifne(label);
} else {
diff --git a/src/jdk/nashorn/internal/codegen/ClassEmitter.java b/src/jdk/nashorn/internal/codegen/ClassEmitter.java
index 717ed356..386effdc 100644
--- a/src/jdk/nashorn/internal/codegen/ClassEmitter.java
+++ b/src/jdk/nashorn/internal/codegen/ClassEmitter.java
@@ -49,24 +49,27 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE;
import static jdk.nashorn.internal.codegen.CompilerConstants.STRICT_MODE;
import static jdk.nashorn.internal.codegen.CompilerConstants.className;
import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor;
-import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor;
+import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
-import java.util.Arrays;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;
-import jdk.internal.org.objectweb.asm.ClassReader;
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.util.TraceClassVisitor;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.SplitNode;
+import jdk.nashorn.internal.ir.debug.NashornClassReader;
+import jdk.nashorn.internal.ir.debug.NashornTextifier;
+import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.PropertyMap;
-import jdk.nashorn.internal.runtime.ScriptEnvironment;
+import jdk.nashorn.internal.runtime.RewriteException;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.Source;
@@ -105,6 +108,8 @@ import jdk.nashorn.internal.runtime.Source;
* @see Compiler
*/
public class ClassEmitter implements Emitter {
+ /** Default flags for class generation - public class */
+ private static final EnumSet<Flag> DEFAULT_METHOD_FLAGS = EnumSet.of(Flag.PUBLIC);
/** Sanity check flag - have we started on a class? */
private boolean classStarted;
@@ -122,10 +127,7 @@ public class ClassEmitter implements Emitter {
protected final ClassWriter cw;
/** The script environment */
- protected final ScriptEnvironment env;
-
- /** Default flags for class generation - oublic class */
- private static final EnumSet<Flag> DEFAULT_METHOD_FLAGS = EnumSet.of(Flag.PUBLIC);
+ protected final Context context;
/** Compile unit class name. */
private String unitClassName;
@@ -140,10 +142,8 @@ public class ClassEmitter implements Emitter {
* @param env script environment
* @param cw ASM classwriter
*/
- private ClassEmitter(final ScriptEnvironment env, final ClassWriter cw) {
- assert env != null;
-
- this.env = env;
+ private ClassEmitter(final Context context, final ClassWriter cw) {
+ this.context = context;
this.cw = cw;
this.methodsStarted = new HashSet<>();
}
@@ -156,8 +156,8 @@ public class ClassEmitter implements Emitter {
* @param superClassName super class name for class
* @param interfaceNames names of interfaces implemented by this class, or null if none
*/
- ClassEmitter(final ScriptEnvironment env, final String className, final String superClassName, final String... interfaceNames) {
- this(env, new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS));
+ ClassEmitter(final Context context, final String className, final String superClassName, final String... interfaceNames) {
+ this(context, new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS));
cw.visit(V1_7, ACC_PUBLIC | ACC_SUPER, className, null, superClassName, interfaceNames);
}
@@ -169,8 +169,8 @@ public class ClassEmitter implements Emitter {
* @param unitClassName Compile unit class name.
* @param strictMode Should we generate this method in strict mode
*/
- ClassEmitter(final ScriptEnvironment env, final String sourceName, final String unitClassName, final boolean strictMode) {
- this(env,
+ ClassEmitter(final Context context, final String sourceName, final String unitClassName, final boolean strictMode) {
+ this(context,
new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS) {
private static final String OBJECT_CLASS = "java/lang/Object";
@@ -196,6 +196,10 @@ public class ClassEmitter implements Emitter {
defineCommonStatics(strictMode);
}
+ Context getContext() {
+ return context;
+ }
+
/**
* Returns the name of the compile unit class name.
* @return the name of the compile unit class name.
@@ -273,51 +277,51 @@ public class ClassEmitter implements Emitter {
}
// $getXXXX$array - get the ith entry from the constants table and cast to XXXX[].
- for (final Class<?> cls : constantMethodNeeded) {
- if (cls.isArray()) {
- defineGetArrayMethod(cls);
+ for (final Class<?> clazz : constantMethodNeeded) {
+ if (clazz.isArray()) {
+ defineGetArrayMethod(clazz);
}
}
}
/**
- * Constructs a primitive specific method for getting the ith entry from the constants table and cast.
- * @param cls Array class.
+ * Constructs a primitive specific method for getting the ith entry from the constants table as an array.
+ * @param clazz Array class.
*/
- private void defineGetArrayMethod(final Class<?> cls) {
+ private void defineGetArrayMethod(final Class<?> clazz) {
assert unitClassName != null;
- final String methodName = getArrayMethodName(cls);
- final MethodEmitter getArrayMethod = method(EnumSet.of(Flag.PRIVATE, Flag.STATIC), methodName, cls, int.class);
+ final String methodName = getArrayMethodName(clazz);
+ final MethodEmitter getArrayMethod = method(EnumSet.of(Flag.PRIVATE, Flag.STATIC), methodName, clazz, int.class);
getArrayMethod.begin();
getArrayMethod.getStatic(unitClassName, CONSTANTS.symbolName(), CONSTANTS.descriptor())
.load(Type.INT, 0)
.arrayload()
- .checkcast(cls)
- .dup()
- .arraylength()
- .invoke(staticCallNoLookup(Arrays.class, "copyOf", cls, cls, int.class))
+ .checkcast(clazz)
+ .invoke(virtualCallNoLookup(clazz, "clone", Object.class))
+ .checkcast(clazz)
._return();
getArrayMethod.end();
}
+
/**
* Generate the name of a get array from constant pool method.
- * @param cls Name of array class.
+ * @param clazz Name of array class.
* @return Method name.
*/
- static String getArrayMethodName(final Class<?> cls) {
- assert cls.isArray();
- return GET_ARRAY_PREFIX.symbolName() + cls.getComponentType().getSimpleName() + GET_ARRAY_SUFFIX.symbolName();
+ static String getArrayMethodName(final Class<?> clazz) {
+ assert clazz.isArray();
+ return GET_ARRAY_PREFIX.symbolName() + clazz.getComponentType().getSimpleName() + GET_ARRAY_SUFFIX.symbolName();
}
/**
* Ensure a get constant method is issued for the class.
- * @param cls Class of constant.
+ * @param clazz Class of constant.
*/
- void needGetConstantMethod(final Class<?> cls) {
- constantMethodNeeded.add(cls);
+ void needGetConstantMethod(final Class<?> clazz) {
+ constantMethodNeeded.add(clazz);
}
/**
@@ -375,16 +379,19 @@ public class ClassEmitter implements Emitter {
static String disassemble(final byte[] bytecode) {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (final PrintWriter pw = new PrintWriter(baos)) {
- new ClassReader(bytecode).accept(new TraceClassVisitor(pw), 0);
+ final NashornClassReader cr = new NashornClassReader(bytecode);
+ final Context ctx = AccessController.doPrivileged(new PrivilegedAction<Context>() {
+ @Override
+ public Context run() {
+ return Context.getContext();
+ }
+ });
+ final TraceClassVisitor tcv = new TraceClassVisitor(null, new NashornTextifier(ctx.getEnv(), cr), pw);
+ cr.accept(tcv, 0);
}
- return new String(baos.toByteArray());
- }
- /**
- * @return env used for class emission
- */
- ScriptEnvironment getEnv() {
- return env;
+ final String str = new String(baos.toByteArray());
+ return str;
}
/**
@@ -474,10 +481,11 @@ public class ClassEmitter implements Emitter {
* @return method emitter to use for weaving this method
*/
MethodEmitter method(final FunctionNode functionNode) {
+ final FunctionSignature signature = new FunctionSignature(functionNode);
final MethodVisitor mv = cw.visitMethod(
ACC_PUBLIC | ACC_STATIC | (functionNode.isVarArg() ? ACC_VARARGS : 0),
functionNode.getName(),
- new FunctionSignature(functionNode).toString(),
+ signature.toString(),
null,
null);
@@ -485,6 +493,24 @@ public class ClassEmitter implements Emitter {
}
/**
+ * Add a new method to the class, representing a rest-of version of the function node
+ *
+ * @param functionNode the function node to generate a method for
+ * @return method emitter to use for weaving this method
+ */
+ MethodEmitter restOfMethod(final FunctionNode functionNode) {
+ final MethodVisitor mv = cw.visitMethod(
+ ACC_PUBLIC | ACC_STATIC,
+ functionNode.getName(),
+ Type.getMethodDescriptor(functionNode.getReturnType().getTypeClass(), RewriteException.class),
+ null,
+ null);
+
+ return new MethodEmitter(this, mv, functionNode);
+ }
+
+
+ /**
* Start generating the <clinit> method in the class
*
* @return method emitter to use for weaving <clinit>
diff --git a/src/jdk/nashorn/internal/codegen/CodeGenerator.java b/src/jdk/nashorn/internal/codegen/CodeGenerator.java
index d299d0a3..0266e5d1 100644
--- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java
+++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java
@@ -29,6 +29,7 @@ import static jdk.nashorn.internal.codegen.ClassEmitter.Flag.PRIVATE;
import static jdk.nashorn.internal.codegen.ClassEmitter.Flag.STATIC;
import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
+import static jdk.nashorn.internal.codegen.CompilerConstants.CREATE_PROGRAM_FUNCTION;
import static jdk.nashorn.internal.codegen.CompilerConstants.GET_MAP;
import static jdk.nashorn.internal.codegen.CompilerConstants.GET_STRING;
import static jdk.nashorn.internal.codegen.CompilerConstants.QUICK_PREFIX;
@@ -45,25 +46,38 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor;
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor;
import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
+import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY;
+import static jdk.nashorn.internal.ir.Symbol.HAS_SLOT;
import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL;
-import static jdk.nashorn.internal.ir.Symbol.IS_TEMP;
+import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
+import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
+import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_APPLY_TO_CALL;
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_FAST_SCOPE;
+import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_OPTIMISTIC;
+import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_PROGRAM_POINT_SHIFT;
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_SCOPE;
-import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_STRICT;
import java.io.PrintWriter;
+import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.BitSet;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Deque;
import java.util.EnumSet;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
+import java.util.function.Supplier;
+import jdk.nashorn.internal.IntDeque;
import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
import jdk.nashorn.internal.codegen.CompilerConstants.Call;
-import jdk.nashorn.internal.codegen.RuntimeCallSite.SpecializedRuntimeNode;
import jdk.nashorn.internal.codegen.types.ArrayType;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.AccessNode;
@@ -86,14 +100,20 @@ 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.JoinPredecessor;
+import jdk.nashorn.internal.ir.JoinPredecessorExpression;
+import jdk.nashorn.internal.ir.LabelNode;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LexicalContextNode;
import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit;
+import jdk.nashorn.internal.ir.LiteralNode.PrimitiveLiteralNode;
+import jdk.nashorn.internal.ir.LocalVariableConversion;
import jdk.nashorn.internal.ir.LoopNode;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.ObjectNode;
+import jdk.nashorn.internal.ir.Optimistic;
import jdk.nashorn.internal.ir.PropertyNode;
import jdk.nashorn.internal.ir.ReturnNode;
import jdk.nashorn.internal.ir.RuntimeNode;
@@ -117,20 +137,26 @@ import jdk.nashorn.internal.parser.Lexer.RegexToken;
import jdk.nashorn.internal.parser.TokenType;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.Debug;
-import jdk.nashorn.internal.runtime.DebugLogger;
import jdk.nashorn.internal.runtime.ECMAException;
import jdk.nashorn.internal.runtime.JSType;
-import jdk.nashorn.internal.runtime.Property;
+import jdk.nashorn.internal.runtime.OptimisticReturnFilters;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
+import jdk.nashorn.internal.runtime.RewriteException;
import jdk.nashorn.internal.runtime.Scope;
+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.Source;
import jdk.nashorn.internal.runtime.Undefined;
+import jdk.nashorn.internal.runtime.UnwarrantedOptimismException;
import jdk.nashorn.internal.runtime.arrays.ArrayData;
import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
+import jdk.nashorn.internal.runtime.logging.DebugLogger;
+import jdk.nashorn.internal.runtime.logging.Loggable;
+import jdk.nashorn.internal.runtime.logging.Logger;
+import jdk.nashorn.internal.runtime.options.Options;
/**
* This is the lowest tier of the code generator. It takes lowered ASTs emitted
@@ -151,16 +177,42 @@ import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
* The CodeGenerator visits nodes only once, tags them as resolved and emits
* bytecode for them.
*/
-final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContext> {
+@Logger(name="codegen")
+final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContext> implements Loggable {
+
+ private static final Type SCOPE_TYPE = Type.typeFor(ScriptObject.class);
private static final String GLOBAL_OBJECT = Type.getInternalName(Global.class);
- private static final String SCRIPTFUNCTION_IMPL_OBJECT = Type.getInternalName(ScriptFunctionImpl.class);
+ private static final String SCRIPTFUNCTION_IMPL_NAME = Type.getInternalName(ScriptFunctionImpl.class);
+ private static final Type SCRIPTFUNCTION_IMPL_TYPE = Type.typeFor(ScriptFunction.class);
+
+ private static final Call CREATE_REWRITE_EXCEPTION = CompilerConstants.staticCallNoLookup(RewriteException.class,
+ "create", RewriteException.class, UnwarrantedOptimismException.class, Object[].class, String[].class);
+ private static final Call CREATE_REWRITE_EXCEPTION_REST_OF = CompilerConstants.staticCallNoLookup(RewriteException.class,
+ "create", RewriteException.class, UnwarrantedOptimismException.class, Object[].class, String[].class, int[].class);
+
+ private static final Call ENSURE_INT = CompilerConstants.staticCallNoLookup(OptimisticReturnFilters.class,
+ "ensureInt", int.class, Object.class, int.class);
+ private static final Call ENSURE_LONG = CompilerConstants.staticCallNoLookup(OptimisticReturnFilters.class,
+ "ensureLong", long.class, Object.class, int.class);
+ private static final Call ENSURE_NUMBER = CompilerConstants.staticCallNoLookup(OptimisticReturnFilters.class,
+ "ensureNumber", double.class, Object.class, int.class);
+
+ private static final Class<?> ITERATOR_CLASS = Iterator.class;
+ static {
+ assert ITERATOR_CLASS == CompilerConstants.ITERATOR_PREFIX.type();
+ }
+ private static final Type ITERATOR_TYPE = Type.typeFor(ITERATOR_CLASS);
+ private static final Type EXCEPTION_TYPE = Type.typeFor(CompilerConstants.EXCEPTION_PREFIX.type());
/** Constant data & installation. The only reason the compiler keeps this is because it is assigned
* by reflection in class installation */
private final Compiler compiler;
+ /** Is the current code submitted by 'eval' call? */
+ private final boolean evalCode;
+
/** Call site flags given to the code generator to be used for all generated call sites */
private final int callSiteFlags;
@@ -180,22 +232,53 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
/** Current compile unit */
private CompileUnit unit;
- private static final DebugLogger LOG = new DebugLogger("codegen", "nashorn.codegen.debug");
+ private final DebugLogger log;
/** From what size should we use spill instead of fields for JavaScript objects? */
- private static final int OBJECT_SPILL_THRESHOLD = 300;
+ private static final int OBJECT_SPILL_THRESHOLD = Options.getIntProperty("nashorn.spill.threshold", 256);
+
+ private static boolean assertsEnabled = false;
+ static {
+ assert assertsEnabled = true; // Intentional side effect
+ }
private final Set<String> emittedMethods = new HashSet<>();
+ // Function Id -> ContinuationInfo. Used by compilation of rest-of function only.
+ private final Map<Integer, ContinuationInfo> fnIdToContinuationInfo = new HashMap<>();
+
+ private final Deque<Label> scopeEntryLabels = new ArrayDeque<>();
+
+ private static final Label METHOD_BOUNDARY = new Label("");
+ private final Deque<Label> catchLabels = new ArrayDeque<>();
+ // Number of live locals on entry to (and thus also break from) labeled blocks.
+ private final IntDeque labeledBlockBreakLiveLocals = new IntDeque();
+
+ //is this a rest of compilation
+ private final int[] continuationEntryPoints;
+
/**
* Constructor.
*
* @param compiler
*/
- CodeGenerator(final Compiler compiler) {
+ CodeGenerator(final Compiler compiler, final int[] continuationEntryPoints) {
super(new CodeGeneratorLexicalContext());
- this.compiler = compiler;
- this.callSiteFlags = compiler.getEnv()._callsite_flags;
+ this.compiler = compiler;
+ this.evalCode = compiler.getSource().isEvalCode();
+ this.continuationEntryPoints = continuationEntryPoints;
+ this.callSiteFlags = compiler.getScriptEnvironment()._callsite_flags;
+ this.log = initLogger(compiler.getContext());
+ }
+
+ @Override
+ public DebugLogger getLogger() {
+ return log;
+ }
+
+ @Override
+ public DebugLogger initLogger(final Context context) {
+ return context.getLogger(this.getClass());
}
/**
@@ -205,7 +288,15 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
* @return the correct flags for a call site in the current function
*/
int getCallSiteFlags() {
- return lc.getCurrentFunction().isStrict() ? callSiteFlags | CALLSITE_STRICT : callSiteFlags;
+ return lc.getCurrentFunction().getCallSiteFlags() | callSiteFlags;
+ }
+
+ /**
+ * Are we generating code for 'eval' code?
+ * @return true if currently compiled code is 'eval' code.
+ */
+ boolean isEvalCode() {
+ return evalCode;
}
/**
@@ -214,38 +305,69 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
* @param identNode an identity node to load
* @return the method generator used
*/
- private MethodEmitter loadIdent(final IdentNode identNode, final Type type) {
+ private MethodEmitter loadIdent(final IdentNode identNode, final TypeBounds resultBounds) {
final Symbol symbol = identNode.getSymbol();
if (!symbol.isScope()) {
+ final Type type = identNode.getType();
+ if(type == Type.UNDEFINED) {
+ return method.loadUndefined(resultBounds.widest);
+ }
+
assert symbol.hasSlot() || symbol.isParam();
- return method.load(symbol).convert(type);
+ return method.load(identNode);
}
- final String name = symbol.getName();
- final Source source = lc.getCurrentFunction().getSource();
-
- if (CompilerConstants.__FILE__.name().equals(name)) {
- return method.load(source.getName());
- } else if (CompilerConstants.__DIR__.name().equals(name)) {
- return method.load(source.getBase());
- } else if (CompilerConstants.__LINE__.name().equals(name)) {
- return method.load(source.getLine(identNode.position())).convert(Type.OBJECT);
+ assert identNode.getSymbol().isScope() : identNode + " is not in scope!";
+ final int flags = CALLSITE_SCOPE | getCallSiteFlags();
+ if (isFastScope(symbol)) {
+ // Only generate shared scope getter for fast-scope symbols so we know we can dial in correct scope.
+ if (symbol.getUseCount() > SharedScopeCall.FAST_SCOPE_GET_THRESHOLD && !isOptimisticOrRestOf()) {
+ method.loadCompilerConstant(SCOPE);
+ // As shared scope vars are only used in non-optimistic compilation, we switch from using TypeBounds to
+ // just a single definitive type, resultBounds.widest.
+ loadSharedScopeVar(resultBounds.widest, symbol, flags);
+ } else {
+ new LoadFastScopeVar(identNode, resultBounds, flags).emit();
+ }
} else {
- assert identNode.getSymbol().isScope() : identNode + " is not in scope!";
+ //slow scope load, we have no proto depth
+ new LoadScopeVar(identNode, resultBounds, flags).emit();
+ }
- final int flags = CALLSITE_SCOPE | getCallSiteFlags();
- method.loadCompilerConstant(SCOPE);
+ return method;
+ }
- if (isFastScope(symbol)) {
- // Only generate shared scope getter for fast-scope symbols so we know we can dial in correct scope.
- if (symbol.getUseCount() > SharedScopeCall.FAST_SCOPE_GET_THRESHOLD) {
- return loadSharedScopeVar(type, symbol, flags);
+ private boolean isRestOf() {
+ return continuationEntryPoints != null;
+ }
+
+ private boolean isOptimisticOrRestOf() {
+ return useOptimisticTypes() || isRestOf();
+ }
+
+ private boolean isCurrentContinuationEntryPoint(final int programPoint) {
+ return isRestOf() && getCurrentContinuationEntryPoint() == programPoint;
+ }
+
+ private int[] getContinuationEntryPoints() {
+ return isRestOf() ? continuationEntryPoints : null;
+ }
+
+ private int getCurrentContinuationEntryPoint() {
+ return isRestOf() ? continuationEntryPoints[0] : INVALID_PROGRAM_POINT;
+ }
+
+ private boolean isContinuationEntryPoint(final int programPoint) {
+ if (isRestOf()) {
+ assert continuationEntryPoints != null;
+ for (final int cep : continuationEntryPoints) {
+ if (cep == programPoint) {
+ return true;
}
- return loadFastScopeVar(type, symbol, flags, identNode.isFunction());
}
- return method.dynamicGet(type, identNode.getName(), flags, identNode.isFunction());
}
+ return false;
}
/**
@@ -285,7 +407,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
}
previousWasBlock = true;
} else {
- if ((node instanceof WithNode && previousWasBlock) || (node instanceof FunctionNode && CodeGeneratorLexicalContext.isFunctionDynamicScope((FunctionNode)node))) {
+ if (node instanceof WithNode && previousWasBlock || node instanceof FunctionNode && ((FunctionNode)node).needsDynamicScope()) {
// If we hit a scope that can have symbols introduced into it at run time before finding the defining
// block, the symbol can't be fast scoped. A WithNode only counts if we've immediately seen a block
// before - its block. Otherwise, we are currently processing the WithNode's expression, and that's
@@ -300,14 +422,57 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
}
private MethodEmitter loadSharedScopeVar(final Type valueType, final Symbol symbol, final int flags) {
- method.load(isFastScope(symbol) ? getScopeProtoDepth(lc.getCurrentBlock(), symbol) : -1);
- final SharedScopeCall scopeCall = lc.getScopeGet(unit, valueType, symbol, flags | CALLSITE_FAST_SCOPE);
- return scopeCall.generateInvoke(method);
+ assert !isOptimisticOrRestOf();
+ if (isFastScope(symbol)) {
+ method.load(getScopeProtoDepth(lc.getCurrentBlock(), symbol));
+ } else {
+ method.load(-1);
+ }
+ return lc.getScopeGet(unit, symbol, valueType, flags | CALLSITE_FAST_SCOPE).generateInvoke(method);
+ }
+
+ private class LoadScopeVar extends OptimisticOperation {
+ final IdentNode identNode;
+ private final int flags;
+
+ LoadScopeVar(final IdentNode identNode, final TypeBounds resultBounds, final int flags) {
+ super(identNode, resultBounds);
+ this.identNode = identNode;
+ this.flags = flags;
+ }
+
+ @Override
+ void loadStack() {
+ method.loadCompilerConstant(SCOPE);
+ getProto();
+ }
+
+ void getProto() {
+ //empty
+ }
+
+ @Override
+ void consumeStack() {
+ // 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());
+ replaceCompileTimeProperty();
+ } else {
+ dynamicGet(identNode.getSymbol().getName(), flags, identNode.isFunction());
+ }
+ }
}
- private MethodEmitter loadFastScopeVar(final Type valueType, final Symbol symbol, final int flags, final boolean isMethod) {
- loadFastScopeProto(symbol, false);
- return method.dynamicGet(valueType, symbol.getName(), flags | CALLSITE_FAST_SCOPE, isMethod);
+ private class LoadFastScopeVar extends LoadScopeVar {
+ LoadFastScopeVar(final IdentNode identNode, final TypeBounds resultBounds, final int flags) {
+ super(identNode, resultBounds, flags | CALLSITE_FAST_SCOPE);
+ }
+
+ @Override
+ void getProto() {
+ loadFastScopeProto(identNode.getSymbol(), false);
+ }
}
private MethodEmitter storeFastScopeVar(final Symbol symbol, final int flags) {
@@ -317,23 +482,30 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
}
private int getScopeProtoDepth(final Block startingBlock, final Symbol symbol) {
+ //walk up the chain from starting block and when we bump into the current function boundary, add the external
+ //information.
+ final FunctionNode fn = lc.getCurrentFunction();
+ final int fnId = fn.getId();
+ final int externalDepth = compiler.getScriptFunctionData(fnId).getExternalSymbolDepth(symbol.getName());
+
+ //count the number of scopes from this place to the start of the function
+
+ final int internalDepth = FindScopeDepths.findInternalDepth(lc, fn, startingBlock, symbol);
+ final int scopesToStart = FindScopeDepths.findScopesToStart(lc, fn, startingBlock);
int depth = 0;
- final String name = symbol.getName();
- for(final Iterator<Block> blocks = lc.getBlocks(startingBlock); blocks.hasNext();) {
- final Block currentBlock = blocks.next();
- if (currentBlock.getExistingSymbol(name) == symbol) {
- return depth;
- }
- if (currentBlock.needsScope()) {
- ++depth;
- }
+ if (internalDepth == -1) {
+ depth = scopesToStart + externalDepth;
+ } else {
+ assert internalDepth <= scopesToStart;
+ depth = internalDepth;
}
- return -1;
+
+ return depth;
}
private void loadFastScopeProto(final Symbol symbol, final boolean swap) {
final int depth = getScopeProtoDepth(lc.getCurrentBlock(), symbol);
- assert depth != -1;
+ assert depth != -1 : "Couldn't find scope depth for symbol " + symbol.getName() + " in " + lc.getCurrentFunction();
if (depth > 0) {
if (swap) {
method.swap();
@@ -348,29 +520,36 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
}
/**
- * Generate code that loads this node to the stack. This method is only
- * public to be accessible from the maps sub package. Do not call externally
+ * Generate code that loads this node to the stack, not constraining its type
*
- * @param node node to load
+ * @param expr node to load
*
* @return the method emitter used
*/
- MethodEmitter load(final Expression node) {
- return load(node, node.hasType() ? node.getType() : null, false);
+ private MethodEmitter loadExpressionUnbounded(final Expression expr) {
+ return loadExpression(expr, TypeBounds.UNBOUNDED);
+ }
+
+ private MethodEmitter loadExpressionAsObject(final Expression expr) {
+ return loadExpression(expr, TypeBounds.OBJECT);
+ }
+
+ MethodEmitter loadExpressionAsBoolean(final Expression expr) {
+ return loadExpression(expr, TypeBounds.BOOLEAN);
}
// Test whether conversion from source to target involves a call of ES 9.1 ToPrimitive
// with possible side effects from calling an object's toString or valueOf methods.
- private boolean noToPrimitiveConversion(final Type source, final Type target) {
+ private static boolean noToPrimitiveConversion(final Type source, final Type target) {
// Object to boolean conversion does not cause ToPrimitive call
return source.isJSPrimitive() || !target.isJSPrimitive() || target.isBoolean();
}
- MethodEmitter loadBinaryOperands(final Expression lhs, final Expression rhs, final Type type) {
- return loadBinaryOperands(lhs, rhs, type, false);
+ MethodEmitter loadBinaryOperands(final BinaryNode binaryNode) {
+ return loadBinaryOperands(binaryNode.lhs(), binaryNode.rhs(), TypeBounds.UNBOUNDED.notWiderThan(binaryNode.getWidestOperandType()), false);
}
- private MethodEmitter loadBinaryOperands(final Expression lhs, final Expression rhs, final Type type, final boolean baseAlreadyOnStack) {
+ private MethodEmitter loadBinaryOperands(final Expression lhs, final Expression rhs, final TypeBounds explicitOperandBounds, final boolean baseAlreadyOnStack) {
// ECMAScript 5.1 specification (sections 11.5-11.11 and 11.13) prescribes that when evaluating a binary
// expression "LEFT op RIGHT", the order of operations must be: LOAD LEFT, LOAD RIGHT, CONVERT LEFT, CONVERT
// RIGHT, EXECUTE OP. Unfortunately, doing it in this order defeats potential optimizations that arise when we
@@ -381,38 +560,130 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
// a primitive value, or RIGHT is an expression that loads without side effects, then we can do the
// reordering and collapse LOAD/CONVERT into a single operation; otherwise we need to do the more costly
// separate operations to preserve specification semantics.
- if (noToPrimitiveConversion(lhs.getType(), type) || rhs.isLocal()) {
+
+ // 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 TypeBounds operandBounds = explicitOperandBounds.notNarrowerThan(narrowestOperandType);
+ if (noToPrimitiveConversion(lhs.getType(), explicitOperandBounds.widest) || rhs.isLocal()) {
// Can reorder. Combine load and convert into single operations.
- load(lhs, type, baseAlreadyOnStack);
- load(rhs, type, false);
+ loadExpression(lhs, operandBounds, baseAlreadyOnStack);
+ loadExpression(rhs, operandBounds, false);
} else {
// Can't reorder. Load and convert separately.
- load(lhs, lhs.getType(), baseAlreadyOnStack);
- load(rhs, rhs.getType(), false);
- method.swap().convert(type).swap().convert(type);
+ final TypeBounds safeConvertBounds = TypeBounds.UNBOUNDED.notNarrowerThan(narrowestOperandType);
+ loadExpression(lhs, safeConvertBounds, baseAlreadyOnStack);
+ loadExpression(rhs, safeConvertBounds, false);
+ method.swap().convert(operandBounds.within(method.peekType())).swap().convert(operandBounds.within(method.peekType()));
}
+ assert Type.generic(method.peekType()) == operandBounds.narrowest;
+ assert Type.generic(method.peekType(1)) == operandBounds.narrowest;
return method;
}
- MethodEmitter loadBinaryOperands(final BinaryNode node) {
- return loadBinaryOperands(node.lhs(), node.rhs(), node.getType(), false);
- }
+ private static final class TypeBounds {
+ final Type narrowest;
+ final Type widest;
- MethodEmitter load(final Expression node, final Type type) {
- return load(node, type, false);
- }
+ 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);
- private MethodEmitter load(final Expression node, final Type type, final boolean baseAlreadyOnStack) {
- final Symbol symbol = node.getSymbol();
+ static TypeBounds exact(final Type type) {
+ return new TypeBounds(type, type);
+ }
- // If we lack symbols, we just generate what we see.
- if (symbol == null || type == null) {
- node.accept(this);
- return method;
+ TypeBounds(final Type narrowest, final Type widest) {
+ assert widest != null && widest != Type.UNDEFINED && widest != Type.UNKNOWN : widest;
+ assert narrowest != null && narrowest != Type.UNDEFINED : narrowest;
+ assert !narrowest.widerThan(widest) : narrowest + " wider than " + widest;
+ assert !widest.narrowerThan(narrowest);
+ this.narrowest = Type.generic(narrowest);
+ this.widest = Type.generic(widest);
+ }
+
+ TypeBounds notNarrowerThan(final Type type) {
+ return maybeNew(Type.narrowest(Type.widest(narrowest, type), widest), widest);
+ }
+
+ TypeBounds notWiderThan(final Type type) {
+ return maybeNew(Type.narrowest(narrowest, type), Type.narrowest(widest, type));
+ }
+
+ boolean canBeNarrowerThan(final Type type) {
+ return narrowest.narrowerThan(type);
+ }
+
+ TypeBounds maybeNew(final Type newNarrowest, final Type newWidest) {
+ if(newNarrowest == narrowest && newWidest == widest) {
+ return this;
+ }
+ return new TypeBounds(newNarrowest, newWidest);
+ }
+
+ TypeBounds booleanToInt() {
+ return maybeNew(booleanToInt(narrowest), booleanToInt(widest));
+ }
+
+ TypeBounds objectToNumber() {
+ return maybeNew(objectToNumber(narrowest), objectToNumber(widest));
+ }
+
+ private static Type booleanToInt(final Type t) {
+ return t == Type.BOOLEAN ? Type.INT : t;
+ }
+
+ private static Type objectToNumber(final Type t) {
+ return t.isObject() ? Type.NUMBER : t;
+ }
+
+ Type within(final Type type) {
+ if(type.narrowerThan(narrowest)) {
+ return narrowest;
+ }
+ if(type.widerThan(widest)) {
+ return widest;
+ }
+ return type;
+ }
+
+ @Override
+ public String toString() {
+ return "[" + narrowest + ", " + widest + "]";
+ }
+ }
+
+ MethodEmitter loadExpressionAsType(final Expression expr, final Type type) {
+ if(type == Type.BOOLEAN) {
+ return loadExpressionAsBoolean(expr);
+ } else if(type == Type.UNDEFINED) {
+ assert expr.getType() == Type.UNDEFINED;
+ return loadExpressionAsObject(expr);
}
+ // having no upper bound preserves semantics of optimistic operations in the expression (by not having them
+ // converted early) and then applies explicit conversion afterwards.
+ return loadExpression(expr, TypeBounds.UNBOUNDED.notNarrowerThan(type)).convert(type);
+ }
+
+ private MethodEmitter loadExpression(final Expression expr, final TypeBounds resultBounds) {
+ return loadExpression(expr, resultBounds, false);
+ }
- assert !type.isUnknown();
+ /**
+ * Emits code for evaluating an expression and leaving its value on top of the stack, narrowing or widening it if
+ * necessary.
+ * @param expr the expression to load
+ * @param resultBounds the incoming type bounds. The value on the top of the stack is guaranteed to not be of narrower
+ * type than the narrowest bound, or wider type than the widest bound after it is loaded.
+ * @param baseAlreadyOnStack true if the base of an access or index node is already on the stack. Used to avoid
+ * double evaluation of bases in self-assignment expressions to access and index nodes. {@code Type.OBJECT} is used
+ * to indicate the widest possible type.
+ * @return the method emitter
+ */
+ private MethodEmitter loadExpression(final Expression expr, final TypeBounds resultBounds, final boolean baseAlreadyOnStack) {
/*
* The load may be of type IdentNode, e.g. "x", AccessNode, e.g. "x.y"
@@ -421,30 +692,49 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
*/
final CodeGenerator codegen = this;
- node.accept(new NodeVisitor<LexicalContext>(lc) {
+ final Node currentDiscard = codegen.lc.getCurrentDiscard();
+ expr.accept(new NodeOperatorVisitor<LexicalContext>(new LexicalContext()) {
@Override
public boolean enterIdentNode(final IdentNode identNode) {
- loadIdent(identNode, type);
+ loadIdent(identNode, resultBounds);
return false;
}
@Override
public boolean enterAccessNode(final AccessNode accessNode) {
- if (!baseAlreadyOnStack) {
- load(accessNode.getBase(), Type.OBJECT);
- }
- assert method.peekType().isObject();
- method.dynamicGet(type, accessNode.getProperty().getName(), getCallSiteFlags(), accessNode.isFunction());
+ new OptimisticOperation(accessNode, resultBounds) {
+ @Override
+ void loadStack() {
+ if (!baseAlreadyOnStack) {
+ loadExpressionAsObject(accessNode.getBase());
+ }
+ assert method.peekType().isObject();
+ }
+ @Override
+ void consumeStack() {
+ final int flags = getCallSiteFlags();
+ dynamicGet(accessNode.getProperty(), flags, accessNode.isFunction());
+ }
+ }.emit(baseAlreadyOnStack ? 1 : 0);
return false;
}
@Override
public boolean enterIndexNode(final IndexNode indexNode) {
- if (!baseAlreadyOnStack) {
- load(indexNode.getBase(), Type.OBJECT);
- load(indexNode.getIndex());
- }
- method.dynamicGetIndex(type, getCallSiteFlags(), indexNode.isFunction());
+ new OptimisticOperation(indexNode, resultBounds) {
+ @Override
+ void loadStack() {
+ if (!baseAlreadyOnStack) {
+ loadExpressionAsObject(indexNode.getBase());
+ loadExpressionUnbounded(indexNode.getIndex());
+ }
+ }
+ @Override
+ void consumeStack() {
+ final int flags = getCallSiteFlags();
+ dynamicGetIndex(flags, indexNode.isFunction());
+ }
+ }.emit(baseAlreadyOnStack ? 2 : 0);
return false;
}
@@ -459,204 +749,485 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
// is the last element in the compilation pipeline, the AST it produces is not used externally. So, we
// re-push the original functionNode.
lc.push(functionNode);
- method.convert(type);
+ return false;
+ }
+
+ @Override
+ public boolean enterASSIGN(final BinaryNode binaryNode) {
+ loadASSIGN(binaryNode);
+ return false;
+ }
+
+ @Override
+ public boolean enterASSIGN_ADD(final BinaryNode binaryNode) {
+ loadASSIGN_ADD(binaryNode);
+ return false;
+ }
+
+ @Override
+ public boolean enterASSIGN_BIT_AND(final BinaryNode binaryNode) {
+ loadASSIGN_BIT_AND(binaryNode);
+ return false;
+ }
+
+ @Override
+ public boolean enterASSIGN_BIT_OR(final BinaryNode binaryNode) {
+ loadASSIGN_BIT_OR(binaryNode);
+ return false;
+ }
+
+ @Override
+ public boolean enterASSIGN_BIT_XOR(final BinaryNode binaryNode) {
+ loadASSIGN_BIT_XOR(binaryNode);
+ return false;
+ }
+
+ @Override
+ public boolean enterASSIGN_DIV(final BinaryNode binaryNode) {
+ loadASSIGN_DIV(binaryNode);
+ return false;
+ }
+
+ @Override
+ public boolean enterASSIGN_MOD(final BinaryNode binaryNode) {
+ loadASSIGN_MOD(binaryNode);
+ return false;
+ }
+
+ @Override
+ public boolean enterASSIGN_MUL(final BinaryNode binaryNode) {
+ loadASSIGN_MUL(binaryNode);
+ return false;
+ }
+
+ @Override
+ public boolean enterASSIGN_SAR(final BinaryNode binaryNode) {
+ loadASSIGN_SAR(binaryNode);
+ return false;
+ }
+
+ @Override
+ public boolean enterASSIGN_SHL(final BinaryNode binaryNode) {
+ loadASSIGN_SHL(binaryNode);
+ return false;
+ }
+
+ @Override
+ public boolean enterASSIGN_SHR(final BinaryNode binaryNode) {
+ loadASSIGN_SHR(binaryNode);
+ return false;
+ }
+
+ @Override
+ public boolean enterASSIGN_SUB(final BinaryNode binaryNode) {
+ loadASSIGN_SUB(binaryNode);
return false;
}
@Override
public boolean enterCallNode(final CallNode callNode) {
- return codegen.enterCallNode(callNode, type);
+ return loadCallNode(callNode, resultBounds);
}
@Override
public boolean enterLiteralNode(final LiteralNode<?> literalNode) {
- return codegen.enterLiteralNode(literalNode, type);
+ loadLiteral(literalNode, resultBounds);
+ return false;
}
@Override
- public boolean enterDefault(final Node otherNode) {
- final Node currentDiscard = codegen.lc.getCurrentDiscard();
- otherNode.accept(codegen); // generate code for whatever we are looking at.
- if(currentDiscard != otherNode) {
- method.load(symbol); // load the final symbol to the stack (or nop if no slot, then result is already there)
- assert method.peekType() != null;
- method.convert(type);
- }
+ public boolean enterTernaryNode(final TernaryNode ternaryNode) {
+ loadTernaryNode(ternaryNode, resultBounds);
return false;
}
- });
- return method;
- }
+ @Override
+ public boolean enterADD(final BinaryNode binaryNode) {
+ loadADD(binaryNode, resultBounds);
+ return false;
+ }
- @Override
- public boolean enterAccessNode(final AccessNode accessNode) {
- load(accessNode);
- return false;
- }
+ @Override
+ public boolean enterSUB(final UnaryNode unaryNode) {
+ loadSUB(unaryNode, resultBounds);
+ return false;
+ }
- /**
- * Initialize a specific set of vars to undefined. This has to be done at
- * the start of each method for local variables that aren't passed as
- * parameters.
- *
- * @param symbols list of symbols.
- */
- private void initSymbols(final Iterable<Symbol> symbols) {
- final LinkedList<Symbol> numbers = new LinkedList<>();
- final LinkedList<Symbol> objects = new LinkedList<>();
+ @Override
+ public boolean enterSUB(final BinaryNode binaryNode) {
+ loadSUB(binaryNode, resultBounds);
+ return false;
+ }
- for (final Symbol symbol : symbols) {
- /*
- * The following symbols are guaranteed to be defined and thus safe
- * from having undefined written to them: parameters internals this
- *
- * Otherwise we must, unless we perform control/escape analysis,
- * assign them undefined.
- */
- final boolean isInternal = symbol.isParam() || symbol.isInternal() || symbol.isThis() || !symbol.canBeUndefined();
-
- if (symbol.hasSlot() && !isInternal) {
- assert symbol.getSymbolType().isNumber() || symbol.getSymbolType().isObject() : "no potentially undefined narrower local vars than doubles are allowed: " + symbol + " in " + lc.getCurrentFunction();
- if (symbol.getSymbolType().isNumber()) {
- numbers.add(symbol);
- } else if (symbol.getSymbolType().isObject()) {
- objects.add(symbol);
- }
+ @Override
+ public boolean enterMUL(final BinaryNode binaryNode) {
+ loadMUL(binaryNode, resultBounds);
+ return false;
}
- }
- initSymbols(numbers, Type.NUMBER);
- initSymbols(objects, Type.OBJECT);
- }
+ @Override
+ public boolean enterDIV(final BinaryNode binaryNode) {
+ loadDIV(binaryNode, resultBounds);
+ return false;
+ }
- private void initSymbols(final LinkedList<Symbol> symbols, final Type type) {
- final Iterator<Symbol> it = symbols.iterator();
- if(it.hasNext()) {
- method.loadUndefined(type);
- boolean hasNext;
- do {
- final Symbol symbol = it.next();
- hasNext = it.hasNext();
- if(hasNext) {
- method.dup();
- }
- method.store(symbol);
- } while(hasNext);
+ @Override
+ public boolean enterMOD(final BinaryNode binaryNode) {
+ loadMOD(binaryNode, resultBounds);
+ return false;
+ }
+
+ @Override
+ public boolean enterSAR(final BinaryNode binaryNode) {
+ loadSAR(binaryNode);
+ return false;
+ }
+
+ @Override
+ public boolean enterSHL(final BinaryNode binaryNode) {
+ loadSHL(binaryNode);
+ return false;
+ }
+
+ @Override
+ public boolean enterSHR(final BinaryNode binaryNode) {
+ loadSHR(binaryNode);
+ return false;
+ }
+
+ @Override
+ public boolean enterCOMMALEFT(final BinaryNode binaryNode) {
+ loadCOMMALEFT(binaryNode, resultBounds);
+ return false;
+ }
+
+ @Override
+ public boolean enterCOMMARIGHT(final BinaryNode binaryNode) {
+ loadCOMMARIGHT(binaryNode, resultBounds);
+ return false;
+ }
+
+ @Override
+ public boolean enterAND(final BinaryNode binaryNode) {
+ loadAND_OR(binaryNode, resultBounds, true);
+ return false;
+ }
+
+ @Override
+ public boolean enterOR(final BinaryNode binaryNode) {
+ loadAND_OR(binaryNode, resultBounds, false);
+ return false;
+ }
+
+ @Override
+ public boolean enterNOT(final UnaryNode unaryNode) {
+ loadNOT(unaryNode);
+ return false;
+ }
+
+ @Override
+ public boolean enterADD(final UnaryNode unaryNode) {
+ loadADD(unaryNode, resultBounds);
+ return false;
+ }
+
+ @Override
+ public boolean enterBIT_NOT(final UnaryNode unaryNode) {
+ loadBIT_NOT(unaryNode);
+ return false;
+ }
+
+ @Override
+ public boolean enterBIT_AND(final BinaryNode binaryNode) {
+ loadBIT_AND(binaryNode);
+ return false;
+ }
+
+ @Override
+ public boolean enterBIT_OR(final BinaryNode binaryNode) {
+ loadBIT_OR(binaryNode);
+ return false;
+ }
+
+ @Override
+ public boolean enterBIT_XOR(final BinaryNode binaryNode) {
+ loadBIT_XOR(binaryNode);
+ return false;
+ }
+
+ @Override
+ public boolean enterVOID(final UnaryNode unaryNode) {
+ loadVOID(unaryNode, resultBounds);
+ return false;
+ }
+
+ @Override
+ public boolean enterEQ(final BinaryNode binaryNode) {
+ loadCmp(binaryNode, Condition.EQ);
+ return false;
+ }
+
+ @Override
+ public boolean enterEQ_STRICT(final BinaryNode binaryNode) {
+ loadCmp(binaryNode, Condition.EQ);
+ return false;
+ }
+
+ @Override
+ public boolean enterGE(final BinaryNode binaryNode) {
+ loadCmp(binaryNode, Condition.GE);
+ return false;
+ }
+
+ @Override
+ public boolean enterGT(final BinaryNode binaryNode) {
+ loadCmp(binaryNode, Condition.GT);
+ return false;
+ }
+
+ @Override
+ public boolean enterLE(final BinaryNode binaryNode) {
+ loadCmp(binaryNode, Condition.LE);
+ return false;
+ }
+
+ @Override
+ public boolean enterLT(final BinaryNode binaryNode) {
+ loadCmp(binaryNode, Condition.LT);
+ return false;
+ }
+
+ @Override
+ public boolean enterNE(final BinaryNode binaryNode) {
+ loadCmp(binaryNode, Condition.NE);
+ return false;
+ }
+
+ @Override
+ public boolean enterNE_STRICT(final BinaryNode binaryNode) {
+ loadCmp(binaryNode, Condition.NE);
+ return false;
+ }
+
+ @Override
+ public boolean enterObjectNode(final ObjectNode objectNode) {
+ loadObjectNode(objectNode);
+ return false;
+ }
+
+ @Override
+ public boolean enterRuntimeNode(final RuntimeNode runtimeNode) {
+ loadRuntimeNode(runtimeNode);
+ return false;
+ }
+
+ @Override
+ public boolean enterNEW(final UnaryNode unaryNode) {
+ loadNEW(unaryNode);
+ return false;
+ }
+
+ @Override
+ public boolean enterDECINC(final UnaryNode unaryNode) {
+ loadDECINC(unaryNode);
+ return false;
+ }
+
+ @Override
+ public boolean enterJoinPredecessorExpression(final JoinPredecessorExpression joinExpr) {
+ loadExpression(joinExpr.getExpression(), resultBounds);
+ return false;
+ }
+
+ @Override
+ public boolean enterDefault(final Node otherNode) {
+ // Must have handled all expressions that can legally be encountered.
+ throw new AssertionError(otherNode.getClass().getName());
+ }
+ });
+ if(currentDiscard != expr) {
+ coerceStackTop(resultBounds);
}
+ return method;
+ }
+
+ private MethodEmitter coerceStackTop(final TypeBounds typeBounds) {
+ return method.convert(typeBounds.within(method.peekType()));
}
/**
- * Create symbol debug information.
+ * Closes any still open entries for this block's local variables in the bytecode local variable table.
*
* @param block block containing symbols.
*/
- private void symbolInfo(final Block block) {
+ private void closeBlockVariables(final Block block) {
for (final Symbol symbol : block.getSymbols()) {
- if (symbol.hasSlot()) {
- method.localVariable(symbol, block.getEntryLabel(), block.getBreakLabel());
+ if (symbol.isBytecodeLocal()) {
+ method.closeLocalVariable(symbol, block.getBreakLabel());
}
}
}
@Override
public boolean enterBlock(final Block block) {
+ method.label(block.getEntryLabel());
+ if(!method.isReachable()) {
+ return false;
+ }
if(lc.isFunctionBody() && emittedMethods.contains(lc.getCurrentFunction().getName())) {
return false;
}
- method.label(block.getEntryLabel());
initLocals(block);
+ assert lc.getUsedSlotCount() == method.getFirstTemp();
return true;
}
+ private boolean useOptimisticTypes() {
+ return !lc.inSplitNode() && compiler.useOptimisticTypes();
+ }
+
@Override
public Node leaveBlock(final Block block) {
- method.label(block.getBreakLabel());
- symbolInfo(block);
+ popBlockScope(block);
+ method.beforeJoinPoint(block);
+
+ closeBlockVariables(block);
+ lc.releaseSlots();
+ assert !method.isReachable() || (lc.isFunctionBody() ? 0 : lc.getUsedSlotCount()) == method.getFirstTemp() :
+ "reachable="+method.isReachable() +
+ " isFunctionBody=" + lc.isFunctionBody() +
+ " usedSlotCount=" + lc.getUsedSlotCount() +
+ " firstTemp=" + method.getFirstTemp();
- if (block.needsScope() && !block.isTerminal()) {
- popBlockScope(block);
- }
return block;
}
private void popBlockScope(final Block block) {
- final Label exitLabel = new Label("block_exit");
- final Label recoveryLabel = new Label("block_catch");
- final Label skipLabel = new Label("skip_catch");
+ final Label breakLabel = block.getBreakLabel();
- /* pop scope a la try-finally */
- method.loadCompilerConstant(SCOPE);
- method.invoke(ScriptObject.GET_PROTO);
- method.storeCompilerConstant(SCOPE);
- method._goto(skipLabel);
- method.label(exitLabel);
+ if(!block.needsScope() || lc.isFunctionBody()) {
+ emitBlockBreakLabel(breakLabel);
+ return;
+ }
+
+ final Label beginTryLabel = scopeEntryLabels.pop();
+ final Label recoveryLabel = new Label("block_popscope_catch");
+ emitBlockBreakLabel(breakLabel);
+ final boolean bodyCanThrow = breakLabel.isAfter(beginTryLabel);
+ if(bodyCanThrow) {
+ method._try(beginTryLabel, breakLabel, recoveryLabel);
+ }
+
+ Label afterCatchLabel = null;
+
+ if(method.isReachable()) {
+ popScope();
+ if(bodyCanThrow) {
+ afterCatchLabel = new Label("block_after_catch");
+ method._goto(afterCatchLabel);
+ }
+ }
+
+ if(bodyCanThrow) {
+ assert !method.isReachable();
+ method._catch(recoveryLabel);
+ popScopeException();
+ method.athrow();
+ }
+ if(afterCatchLabel != null) {
+ method.label(afterCatchLabel);
+ }
+ }
+
+ private void emitBlockBreakLabel(final Label breakLabel) {
+ // TODO: this is totally backwards. Block should not be breakable, LabelNode should be breakable.
+ final LabelNode labelNode = lc.getCurrentBlockLabelNode();
+ if(labelNode != null) {
+ // Only have conversions if we're reachable
+ assert labelNode.getLocalVariableConversion() == null || method.isReachable();
+ method.beforeJoinPoint(labelNode);
+ method.breakLabel(breakLabel, labeledBlockBreakLiveLocals.pop());
+ } else {
+ method.label(breakLabel);
+ }
+ }
+
+ private void popScope() {
+ popScopes(1);
+ }
+
+ /**
+ * Pop scope as part of an exception handler. Similar to {@code popScope()} but also takes care of adjusting the
+ * number of scopes that needs to be popped in case a rest-of continuation handler encounters an exception while
+ * performing a ToPrimitive conversion.
+ */
+ private void popScopeException() {
+ popScope();
+ final ContinuationInfo ci = getContinuationInfo();
+ if(ci != null) {
+ final Label catchLabel = ci.catchLabel;
+ if(catchLabel != METHOD_BOUNDARY && catchLabel == catchLabels.peek()) {
+ ++ci.exceptionScopePops;
+ }
+ }
+ }
+
+ private void popScopesUntil(final LexicalContextNode until) {
+ popScopes(lc.getScopeNestingLevelTo(until));
+ }
- method._catch(recoveryLabel);
+ private void popScopes(final int count) {
+ if(count == 0) {
+ return;
+ }
+ assert count > 0; // together with count == 0 check, asserts nonnegative count
+ if (!method.hasScope()) {
+ // We can sometimes invoke this method even if the method has no slot for the scope object. Typical example:
+ // for(;;) { with({}) { break; } }. WithNode normally creates a scope, but if it uses no identifiers and
+ // nothing else forces creation of a scope in the method, we just won't have the :scope local variable.
+ return;
+ }
method.loadCompilerConstant(SCOPE);
- method.invoke(ScriptObject.GET_PROTO);
+ for(int i = 0; i < count; ++i) {
+ method.invoke(ScriptObject.GET_PROTO);
+ }
method.storeCompilerConstant(SCOPE);
- method.athrow();
- method.label(skipLabel);
- method._try(block.getEntryLabel(), exitLabel, recoveryLabel, Throwable.class);
}
@Override
public boolean enterBreakNode(final BreakNode breakNode) {
- lineNumber(breakNode);
-
- final BreakableNode breakFrom = lc.getBreakable(breakNode.getLabel());
- for (int i = 0; i < lc.getScopeNestingLevelTo(breakFrom); i++) {
- closeWith();
+ if(!method.isReachable()) {
+ return false;
}
- method.splitAwareGoto(lc, breakFrom.getBreakLabel());
+ enterStatement(breakNode);
+
+ method.beforeJoinPoint(breakNode);
+ final BreakableNode breakFrom = lc.getBreakable(breakNode.getLabelName());
+ popScopesUntil(breakFrom);
+ final Label breakLabel = breakFrom.getBreakLabel();
+ breakLabel.markAsBreakTarget();
+ method.splitAwareGoto(lc, breakLabel, breakFrom);
return false;
}
private int loadArgs(final List<Expression> args) {
- return loadArgs(args, null, false, args.size());
- }
-
- private int loadArgs(final List<Expression> args, final String signature, final boolean isVarArg, final int argCount) {
+ final int argCount = args.size();
// arg have already been converted to objects here.
- if (isVarArg || argCount > LinkerCallSite.ARGLIMIT) {
+ if (argCount > LinkerCallSite.ARGLIMIT) {
loadArgsArray(args);
return 1;
}
- // pad with undefined if size is too short. argCount is the real number of args
- int n = 0;
- final Type[] params = signature == null ? null : Type.getMethodArguments(signature);
for (final Expression arg : args) {
assert arg != null;
- if (n >= argCount) {
- load(arg);
- method.pop(); // we had to load the arg for its side effects
- } else if (params != null) {
- load(arg, params[n]);
- } else {
- load(arg);
- }
- n++;
+ loadExpressionUnbounded(arg);
}
-
- while (n < argCount) {
- method.loadUndefined(Type.OBJECT);
- n++;
- }
-
return argCount;
}
-
- @Override
- public boolean enterCallNode(final CallNode callNode) {
- return enterCallNode(callNode, callNode.getType());
- }
-
- private boolean enterCallNode(final CallNode callNode, final Type callNodeType) {
+ private boolean loadCallNode(final CallNode callNode, final TypeBounds resultBounds) {
lineNumber(callNode.getLineNumber());
final List<Expression> args = callNode.getArgs();
@@ -668,68 +1239,122 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
private MethodEmitter sharedScopeCall(final IdentNode identNode, final int flags) {
final Symbol symbol = identNode.getSymbol();
- int scopeCallFlags = flags;
- method.loadCompilerConstant(SCOPE);
- if (isFastScope(symbol)) {
- method.load(getScopeProtoDepth(currentBlock, symbol));
- scopeCallFlags |= CALLSITE_FAST_SCOPE;
- } else {
- method.load(-1); // Bypass fast-scope code in shared callsite
- }
- loadArgs(args);
- final Type[] paramTypes = method.getTypesFromStack(args.size());
- final SharedScopeCall scopeCall = codegenLexicalContext.getScopeCall(unit, symbol, identNode.getType(), callNodeType, paramTypes, scopeCallFlags);
- return scopeCall.generateInvoke(method);
+ final boolean isFastScope = isFastScope(symbol);
+ final int scopeCallFlags = flags | (isFastScope ? CALLSITE_FAST_SCOPE : 0);
+ new OptimisticOperation(callNode, resultBounds) {
+ @Override
+ void loadStack() {
+ method.loadCompilerConstant(SCOPE);
+ if (isFastScope) {
+ method.load(getScopeProtoDepth(currentBlock, symbol));
+ } else {
+ method.load(-1); // Bypass fast-scope code in shared callsite
+ }
+ loadArgs(args);
+ }
+ @Override
+ void consumeStack() {
+ final Type[] paramTypes = method.getTypesFromStack(args.size());
+ // We have trouble finding e.g. in Type.typeFor(asm.Type) because it can't see the Context class
+ // loader, so we need to weaken reference signatures to Object.
+ for(int i = 0; i < paramTypes.length; ++i) {
+ paramTypes[i] = Type.generic(paramTypes[i]);
+ }
+ // As shared scope calls are only used in non-optimistic compilation, we switch from using
+ // TypeBounds to just a single definitive type, resultBounds.widest.
+ final SharedScopeCall scopeCall = codegenLexicalContext.getScopeCall(unit, symbol,
+ identNode.getType(), resultBounds.widest, paramTypes, scopeCallFlags);
+ scopeCall.generateInvoke(method);
+ }
+ }.emit();
+ return method;
}
- private void scopeCall(final IdentNode node, final int flags) {
- load(node, Type.OBJECT); // Type.OBJECT as foo() makes no sense if foo == 3
- // ScriptFunction will see CALLSITE_SCOPE and will bind scope accordingly.
- method.loadUndefined(Type.OBJECT); //the 'this' object
- method.dynamicCall(callNodeType, 2 + loadArgs(args), flags);
+ private void scopeCall(final IdentNode ident, final int flags) {
+ new OptimisticOperation(callNode, resultBounds) {
+ int argsCount;
+ @Override
+ void loadStack() {
+ loadExpressionAsObject(ident); // foo() makes no sense if foo == 3
+ // ScriptFunction will see CALLSITE_SCOPE and will bind scope accordingly.
+ method.loadUndefined(Type.OBJECT); //the 'this'
+ argsCount = loadArgs(args);
+ }
+ @Override
+ void consumeStack() {
+ dynamicCall(2 + argsCount, flags);
+ }
+ }.emit();
}
- private void evalCall(final IdentNode node, final int flags) {
- load(node, Type.OBJECT); // Type.OBJECT as foo() makes no sense if foo == 3
-
- final Label not_eval = new Label("not_eval");
+ private void evalCall(final IdentNode ident, final int flags) {
+ final Label invoke_direct_eval = new Label("invoke_direct_eval");
+ final Label is_not_eval = new Label("is_not_eval");
final Label eval_done = new Label("eval_done");
- // check if this is the real built-in eval
- method.dup();
- globalIsEval();
-
- method.ifeq(not_eval);
- // We don't need ScriptFunction object for 'eval'
- method.pop();
-
- method.loadCompilerConstant(SCOPE); // Load up self (scope).
-
- final CallNode.EvalArgs evalArgs = callNode.getEvalArgs();
- // load evaluated code
- load(evalArgs.getCode(), Type.OBJECT);
- // load second and subsequent args for side-effect
- final List<Expression> args = callNode.getArgs();
- final int numArgs = args.size();
- for (int i = 1; i < numArgs; i++) {
- load(args.get(i)).pop();
- }
- // special/extra 'eval' arguments
- load(evalArgs.getThis());
- method.load(evalArgs.getLocation());
- method.load(evalArgs.getStrictMode());
- method.convert(Type.OBJECT);
-
- // direct call to Global.directEval
- globalDirectEval();
- method.convert(callNodeType);
- method._goto(eval_done);
+ new OptimisticOperation(callNode, resultBounds) {
+ int argsCount;
+ @Override
+ void loadStack() {
+ /**
+ * We want to load 'eval' to check if it is indeed global builtin eval.
+ * If this eval call is inside a 'with' statement, dyn:getMethod|getProp|getElem
+ * would be generated if ident is a "isFunction". But, that would result in a
+ * bound function from WithObject. We don't want that as bound function as that
+ * won't be detected as builtin eval. So, we make ident as "not a function" which
+ * results in "dyn:getProp|getElem|getMethod" being generated and so WithObject
+ * would return unbounded eval function.
+ *
+ * Example:
+ *
+ * var global = this;
+ * function func() {
+ * with({ eval: global.eval) { eval("var x = 10;") }
+ * }
+ */
+ loadExpressionAsObject(ident.setIsNotFunction()); // Type.OBJECT as foo() makes no sense if foo == 3
+ globalIsEval();
+ method.ifeq(is_not_eval);
+
+ // Load up self (scope).
+ method.loadCompilerConstant(SCOPE);
+ final List<Expression> evalArgs = callNode.getEvalArgs().getArgs();
+ // load evaluated code
+ loadExpressionAsObject(evalArgs.get(0));
+ // load second and subsequent args for side-effect
+ final int numArgs = evalArgs.size();
+ for (int i = 1; i < numArgs; i++) {
+ loadAndDiscard(evalArgs.get(i));
+ }
+ method._goto(invoke_direct_eval);
+
+ method.label(is_not_eval);
+ // load this time but with dyn:getMethod|getProp|getElem
+ loadExpressionAsObject(ident); // Type.OBJECT as foo() makes no sense if foo == 3
+ // This is some scope 'eval' or global eval replaced by user
+ // but not the built-in ECMAScript 'eval' function call
+ method.loadNull();
+ argsCount = loadArgs(callNode.getArgs());
+ }
- method.label(not_eval);
- // This is some scope 'eval' or global eval replaced by user
- // but not the built-in ECMAScript 'eval' function call
- method.loadNull();
- method.dynamicCall(callNodeType, 2 + loadArgs(args), flags);
+ @Override
+ void consumeStack() {
+ // Ordinary call
+ dynamicCall(2 + argsCount, flags);
+ method._goto(eval_done);
+
+ method.label(invoke_direct_eval);
+ // Special/extra 'eval' arguments. These can be loaded late (in consumeStack) as we know none of
+ // them can ever be optimistic.
+ method.loadCompilerConstant(THIS);
+ method.load(callNode.getEvalArgs().getLocation());
+ method.load(CodeGenerator.this.lc.getCurrentFunction().isStrict());
+ // direct call to Global.directEval
+ globalDirectEval();
+ convertOptimisticReturnValue();
+ coerceStackTop(resultBounds);
+ }
+ }.emit();
method.label(eval_done);
}
@@ -748,13 +1373,14 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
if (callNode.isEval()) {
evalCall(node, flags);
} else if (useCount <= SharedScopeCall.FAST_SCOPE_CALL_THRESHOLD
- || (!isFastScope(symbol) && useCount <= SharedScopeCall.SLOW_SCOPE_CALL_THRESHOLD)
- || CodeGenerator.this.lc.inDynamicScope()) {
+ || !isFastScope(symbol) && useCount <= SharedScopeCall.SLOW_SCOPE_CALL_THRESHOLD
+ || CodeGenerator.this.lc.inDynamicScope()
+ || isOptimisticOrRestOf()) {
scopeCall(node, flags);
} else {
sharedScopeCall(node, flags);
}
- assert method.peekType().equals(callNodeType) : method.peekType() + "!=" + callNode.getType();
+ assert method.peekType().equals(resultBounds.within(callNode.getType())) : method.peekType() + " != " + resultBounds + "(" + callNode.getType() + ")";
} else {
enterDefault(node);
}
@@ -764,104 +1390,170 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
@Override
public boolean enterAccessNode(final AccessNode node) {
- load(node.getBase(), Type.OBJECT);
- method.dup();
- method.dynamicGet(node.getType(), node.getProperty().getName(), getCallSiteFlags(), true);
- method.swap();
- method.dynamicCall(callNodeType, 2 + loadArgs(args), getCallSiteFlags());
+ //check if this is an apply to call node. only real applies, that haven't been
+ //shadowed from their way to the global scope counts
+
+ //call nodes have program points.
+
+ final int flags = getCallSiteFlags() | (callNode.isApplyToCall() ? CALLSITE_APPLY_TO_CALL : 0);
+
+ new OptimisticOperation(callNode, resultBounds) {
+ int argCount;
+ @Override
+ void loadStack() {
+ loadExpressionAsObject(node.getBase());
+ method.dup();
+ // 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.swap();
+ argCount = loadArgs(args);
+ }
+ @Override
+ void consumeStack() {
+ dynamicCall(2 + argCount, flags);
+ }
+ }.emit();
return false;
}
@Override
public boolean enterFunctionNode(final FunctionNode origCallee) {
- // NOTE: visiting the callee will leave a constructed ScriptFunction object on the stack if
- // callee.needsCallee() == true
- final FunctionNode callee = (FunctionNode)origCallee.accept(CodeGenerator.this);
-
- final boolean isVarArg = callee.isVarArg();
- final int argCount = isVarArg ? -1 : callee.getParameters().size();
-
- final String signature = new FunctionSignature(true, callee.needsCallee(), callee.getReturnType(), isVarArg ? null : callee.getParameters()).toString();
+ new OptimisticOperation(callNode, resultBounds) {
+ FunctionNode callee;
+ int argsCount;
+ @Override
+ void loadStack() {
+ callee = (FunctionNode)origCallee.accept(CodeGenerator.this);
+ if (callee.isStrict()) { // "this" is undefined
+ method.loadUndefined(Type.OBJECT);
+ } else { // get global from scope (which is the self)
+ globalInstance();
+ }
+ argsCount = loadArgs(args);
+ }
- if (callee.isStrict()) { // self is undefined
- method.loadUndefined(Type.OBJECT);
- } else { // get global from scope (which is the self)
- globalInstance();
- }
- loadArgs(args, signature, isVarArg, argCount);
- assert callee.getCompileUnit() != null : "no compile unit for " + callee.getName() + " " + Debug.id(callee) + " " + callNode;
- method.invokestatic(callee.getCompileUnit().getUnitClassName(), callee.getName(), signature);
- assert method.peekType().equals(callee.getReturnType()) : method.peekType() + " != " + callee.getReturnType();
- method.convert(callNodeType);
+ @Override
+ void consumeStack() {
+ final int flags = getCallSiteFlags();
+ //assert callNodeType.equals(callee.getReturnType()) : callNodeType + " != " + callee.getReturnType();
+ dynamicCall(2 + argsCount, flags);
+ }
+ }.emit();
return false;
}
@Override
public boolean enterIndexNode(final IndexNode node) {
- load(node.getBase(), Type.OBJECT);
- method.dup();
- final Type indexType = node.getIndex().getType();
- if (indexType.isObject() || indexType.isBoolean()) {
- load(node.getIndex(), Type.OBJECT); //TODO
- } else {
- load(node.getIndex());
- }
- method.dynamicGetIndex(node.getType(), getCallSiteFlags(), true);
- method.swap();
- method.dynamicCall(callNodeType, 2 + loadArgs(args), getCallSiteFlags());
-
+ new OptimisticOperation(callNode, resultBounds) {
+ int argsCount;
+ @Override
+ void loadStack() {
+ loadExpressionAsObject(node.getBase());
+ method.dup();
+ final Type indexType = node.getIndex().getType();
+ if (indexType.isObject() || indexType.isBoolean()) {
+ loadExpressionAsObject(node.getIndex()); //TODO boolean
+ } else {
+ loadExpressionUnbounded(node.getIndex());
+ }
+ // NOTE: not using a nested OptimisticOperation on this dynamicGetIndex, as we expect to get
+ // back a callable object. Nobody in their right mind would optimistically type this call site.
+ assert !node.isOptimistic();
+ method.dynamicGetIndex(node.getType(), getCallSiteFlags(), true);
+ method.swap();
+ argsCount = loadArgs(args);
+ }
+ @Override
+ void consumeStack() {
+ final int flags = getCallSiteFlags();
+ dynamicCall(2 + argsCount, flags);
+ }
+ }.emit();
return false;
}
@Override
protected boolean enterDefault(final Node node) {
- // Load up function.
- load(function, Type.OBJECT); //TODO, e.g. booleans can be used as functions
- method.loadUndefined(Type.OBJECT); // ScriptFunction will figure out the correct this when it sees CALLSITE_SCOPE
- method.dynamicCall(callNodeType, 2 + loadArgs(args), getCallSiteFlags() | CALLSITE_SCOPE);
-
+ new OptimisticOperation(callNode, resultBounds) {
+ int argsCount;
+ @Override
+ void loadStack() {
+ // Load up function.
+ loadExpressionAsObject(function); //TODO, e.g. booleans can be used as functions
+ method.loadUndefined(Type.OBJECT); // ScriptFunction will figure out the correct this when it sees CALLSITE_SCOPE
+ argsCount = loadArgs(args);
+ }
+ @Override
+ void consumeStack() {
+ final int flags = getCallSiteFlags() | CALLSITE_SCOPE;
+ dynamicCall(2 + argsCount, flags);
+ }
+ }.emit();
return false;
}
});
- method.store(callNode.getSymbol());
-
return false;
}
+ /**
+ * Returns the flags with optimistic flag and program point removed.
+ * @param flags the flags that need optimism stripped from them.
+ * @return flags without optimism
+ */
+ static int nonOptimisticFlags(final int flags) {
+ return flags & ~(CALLSITE_OPTIMISTIC | -1 << CALLSITE_PROGRAM_POINT_SHIFT);
+ }
+
@Override
public boolean enterContinueNode(final ContinueNode continueNode) {
- lineNumber(continueNode);
-
- final LoopNode continueTo = lc.getContinueTo(continueNode.getLabel());
- for (int i = 0; i < lc.getScopeNestingLevelTo(continueTo); i++) {
- closeWith();
+ if(!method.isReachable()) {
+ return false;
}
- method.splitAwareGoto(lc, continueTo.getContinueLabel());
+ enterStatement(continueNode);
+ method.beforeJoinPoint(continueNode);
+
+ final LoopNode continueTo = lc.getContinueTo(continueNode.getLabelName());
+ popScopesUntil(continueTo);
+ final Label continueLabel = continueTo.getContinueLabel();
+ continueLabel.markAsBreakTarget();
+ method.splitAwareGoto(lc, continueLabel, continueTo);
return false;
}
@Override
public boolean enterEmptyNode(final EmptyNode emptyNode) {
- lineNumber(emptyNode);
+ if(!method.isReachable()) {
+ return false;
+ }
+ enterStatement(emptyNode);
return false;
}
@Override
public boolean enterExpressionStatement(final ExpressionStatement expressionStatement) {
- lineNumber(expressionStatement);
+ if(!method.isReachable()) {
+ return false;
+ }
+ enterStatement(expressionStatement);
- expressionStatement.getExpression().accept(this);
+ loadAndDiscard(expressionStatement.getExpression());
+ assert method.getStackSize() == 0;
return false;
}
@Override
public boolean enterBlockStatement(final BlockStatement blockStatement) {
- lineNumber(blockStatement);
+ if(!method.isReachable()) {
+ return false;
+ }
+ enterStatement(blockStatement);
blockStatement.getBlock().accept(this);
@@ -870,83 +1562,70 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
@Override
public boolean enterForNode(final ForNode forNode) {
- lineNumber(forNode);
-
+ if(!method.isReachable()) {
+ return false;
+ }
+ enterStatement(forNode);
if (forNode.isForIn()) {
enterForIn(forNode);
} else {
- enterFor(forNode);
+ final Expression init = forNode.getInit();
+ if (init != null) {
+ loadAndDiscard(init);
+ }
+ enterForOrWhile(forNode, forNode.getModify());
}
return false;
}
- private void enterFor(final ForNode forNode) {
- final Expression init = forNode.getInit();
- final Expression test = forNode.getTest();
- final Block body = forNode.getBody();
- final Expression modify = forNode.getModify();
-
- if (init != null) {
- init.accept(this);
- }
-
- final Label loopLabel = new Label("loop");
- final Label testLabel = new Label("test");
-
- method._goto(testLabel);
- method.label(loopLabel);
- body.accept(this);
- method.label(forNode.getContinueLabel());
-
- if (!body.isTerminal() && modify != null) {
- load(modify);
- }
-
- method.label(testLabel);
- if (test != null) {
- new BranchOptimizer(this, method).execute(test, loopLabel, true);
- } else {
- method._goto(loopLabel);
- }
-
- method.label(forNode.getBreakLabel());
- }
-
private void enterForIn(final ForNode forNode) {
- final Block body = forNode.getBody();
- final Expression modify = forNode.getModify();
-
- final Symbol iter = forNode.getIterator();
- final Label loopLabel = new Label("loop");
-
- final Expression init = forNode.getInit();
-
- load(modify, Type.OBJECT);
+ loadExpression(forNode.getModify(), TypeBounds.OBJECT);
method.invoke(forNode.isForEach() ? ScriptRuntime.TO_VALUE_ITERATOR : ScriptRuntime.TO_PROPERTY_ITERATOR);
- method.store(iter);
- method._goto(forNode.getContinueLabel());
- method.label(loopLabel);
+ final Symbol iterSymbol = forNode.getIterator();
+ final int iterSlot = iterSymbol.getSlot(Type.OBJECT);
+ method.store(iterSymbol, ITERATOR_TYPE);
+
+ method.beforeJoinPoint(forNode);
+
+ final Label continueLabel = forNode.getContinueLabel();
+ final Label breakLabel = forNode.getBreakLabel();
+
+ method.label(continueLabel);
+ method.load(ITERATOR_TYPE, iterSlot);
+ method.invoke(interfaceCallNoLookup(ITERATOR_CLASS, "hasNext", boolean.class));
+ final JoinPredecessorExpression test = forNode.getTest();
+ final Block body = forNode.getBody();
+ if(LocalVariableConversion.hasLiveConversion(test)) {
+ final Label afterConversion = new Label("for_in_after_test_conv");
+ method.ifne(afterConversion);
+ method.beforeJoinPoint(test);
+ method._goto(breakLabel);
+ method.label(afterConversion);
+ } else {
+ method.ifeq(breakLabel);
+ }
- new Store<Expression>(init) {
+ new Store<Expression>(forNode.getInit()) {
@Override
protected void storeNonDiscard() {
- return;
+ // This expression is neither part of a discard, nor needs to be left on the stack after it was
+ // stored, so we override storeNonDiscard to be a no-op.
}
+
@Override
protected void evaluate() {
- method.load(iter);
- method.invoke(interfaceCallNoLookup(Iterator.class, "next", Object.class));
+ method.load(ITERATOR_TYPE, iterSlot);
+ // TODO: optimistic for-in iteration
+ method.invoke(interfaceCallNoLookup(ITERATOR_CLASS, "next", Object.class));
}
}.store();
-
body.accept(this);
- method.label(forNode.getContinueLabel());
- method.load(iter);
- method.invoke(interfaceCallNoLookup(Iterator.class, "hasNext", boolean.class));
- method.ifne(loopLabel);
- method.label(forNode.getBreakLabel());
+ if(method.isReachable()) {
+ method._goto(continueLabel);
+ }
+ method.label(breakLabel);
}
/**
@@ -955,13 +1634,16 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
* @param block block with local vars.
*/
private void initLocals(final Block block) {
- lc.nextFreeSlot(block);
+ lc.onEnterBlock(block);
final boolean isFunctionBody = lc.isFunctionBody();
-
final FunctionNode function = lc.getCurrentFunction();
if (isFunctionBody) {
- if(method.hasScope()) {
+ initializeMethodParameters(function);
+ if(!function.isVarArg()) {
+ expandParameterSlots(function);
+ }
+ if (method.hasScope()) {
if (function.needsParentScope()) {
method.loadCompilerConstant(CALLEE);
method.invoke(ScriptFunction.GET_SCOPE);
@@ -988,76 +1670,173 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
// TODO for LET we can do better: if *block* does not contain any eval/with, we don't need its vars in scope.
- final List<String> nameList = new ArrayList<>();
- final List<Symbol> locals = new ArrayList<>();
-
- // Initalize symbols and values
- final List<Symbol> newSymbols = new ArrayList<>();
- final List<Symbol> values = new ArrayList<>();
-
final boolean hasArguments = function.needsArguments();
-
+ final List<MapTuple<Symbol>> tuples = new ArrayList<>();
+ final Iterator<IdentNode> paramIter = function.getParameters().iterator();
for (final Symbol symbol : block.getSymbols()) {
-
- if (symbol.isInternal() || symbol.isThis() || symbol.isTemp()) {
+ if (symbol.isInternal() || symbol.isThis()) {
continue;
}
if (symbol.isVar()) {
+ assert !varsInScope || symbol.isScope();
if (varsInScope || symbol.isScope()) {
- nameList.add(symbol.getName());
- newSymbols.add(symbol);
- values.add(null);
assert symbol.isScope() : "scope for " + symbol + " should have been set in Lower already " + function.getName();
assert !symbol.hasSlot() : "slot for " + symbol + " should have been removed in Lower already" + function.getName();
+
+ //this tuple will not be put fielded, as it has no value, just a symbol
+ tuples.add(new MapTuple<Symbol>(symbol.getName(), symbol, null));
} else {
- assert symbol.hasSlot() : symbol + " should have a slot only, no scope";
- locals.add(symbol);
+ assert symbol.hasSlot() || symbol.slotCount() == 0 : symbol + " should have a slot only, no scope";
}
} else if (symbol.isParam() && (varsInScope || hasArguments || symbol.isScope())) {
- nameList.add(symbol.getName());
- newSymbols.add(symbol);
- values.add(hasArguments ? null : symbol);
- assert symbol.isScope() : "scope for " + symbol + " should have been set in Lower already " + function.getName() + " varsInScope="+varsInScope+" hasArguments="+hasArguments+" symbol.isScope()=" + symbol.isScope();
+ assert symbol.isScope() : "scope for " + symbol + " should have been set in AssignSymbols already " + function.getName() + " varsInScope="+varsInScope+" hasArguments="+hasArguments+" symbol.isScope()=" + symbol.isScope();
assert !(hasArguments && symbol.hasSlot()) : "slot for " + symbol + " should have been removed in Lower already " + function.getName();
+
+ final Type paramType;
+ final Symbol paramSymbol;
+
+ if (hasArguments) {
+ assert !symbol.hasSlot() : "slot for " + symbol + " should have been removed in Lower already ";
+ paramSymbol = null;
+ paramType = null;
+ } else {
+ paramSymbol = symbol;
+ // NOTE: We're relying on the fact here that Block.symbols is a LinkedHashMap, hence it will
+ // return symbols in the order they were defined, and parameters are defined in the same order
+ // they appear in the function. That's why we can have a single pass over the parameter list
+ // with an iterator, always just scanning forward for the next parameter that matches the symbol
+ // name.
+ for(;;) {
+ final IdentNode nextParam = paramIter.next();
+ if(nextParam.getName().equals(symbol.getName())) {
+ paramType = nextParam.getType();
+ break;
+ }
+ }
+ }
+
+ tuples.add(new MapTuple<Symbol>(symbol.getName(), symbol, paramType, paramSymbol) {
+ //this symbol will be put fielded, we can't initialize it as undefined with a known type
+ @Override
+ public Class<?> getValueType() {
+ if (OBJECT_FIELDS_ONLY || value == null || paramType == null) {
+ return Object.class;
+ }
+ return paramType.isBoolean() ? Object.class : paramType.getTypeClass();
+ }
+ });
}
}
- // we may have locals that need to be initialized
- initSymbols(locals);
-
/*
* Create a new object based on the symbols and values, generate
* bootstrap code for object
*/
- new FieldObjectCreator<Symbol>(this, nameList, newSymbols, values, true, hasArguments) {
+ new FieldObjectCreator<Symbol>(this, tuples, true, hasArguments) {
@Override
- protected void loadValue(final Symbol value) {
- method.load(value);
+ protected void loadValue(final Symbol value, final Type type) {
+ method.load(value, type);
}
}.makeObject(method);
-
- // runScript(): merge scope into global
+ // program function: merge scope into global
if (isFunctionBody && function.isProgram()) {
method.invoke(ScriptRuntime.MERGE_SCOPE);
}
method.storeCompilerConstant(SCOPE);
- } else {
+ if(!isFunctionBody) {
+ // Function body doesn't need a try/catch to restore scope, as it'd be a dead store anyway. Allowing it
+ // actually causes issues with UnwarrantedOptimismException handlers as ASM will sort this handler to
+ // the top of the exception handler table, so it'll be triggered instead of the UOE handlers.
+ final Label scopeEntryLabel = new Label("scope_entry");
+ scopeEntryLabels.push(scopeEntryLabel);
+ method.label(scopeEntryLabel);
+ }
+ } else if (isFunctionBody && function.isVarArg()) {
// Since we don't have a scope, parameters didn't get assigned array indices by the FieldObjectCreator, so
// we need to assign them separately here.
int nextParam = 0;
- if (isFunctionBody && function.isVarArg()) {
- for (final IdentNode param : function.getParameters()) {
- param.getSymbol().setFieldIndex(nextParam++);
- }
+ for (final IdentNode param : function.getParameters()) {
+ param.getSymbol().setFieldIndex(nextParam++);
}
-
- initSymbols(block.getSymbols());
}
// Debugging: print symbols? @see --print-symbols flag
- printSymbols(block, (isFunctionBody ? "Function " : "Block in ") + (function.getIdent() == null ? "<anonymous>" : function.getIdent().getName()));
+ printSymbols(block, function, (isFunctionBody ? "Function " : "Block in ") + (function.getIdent() == null ? "<anonymous>" : function.getIdent().getName()));
+ }
+
+ /**
+ * Incoming method parameters are always declared on method entry; declare them in the local variable table.
+ * @param function function for which code is being generated.
+ */
+ private void initializeMethodParameters(final FunctionNode function) {
+ final Label functionStart = new Label("fn_start");
+ method.label(functionStart);
+ int nextSlot = 0;
+ if(function.needsCallee()) {
+ initializeInternalFunctionParameter(CALLEE, function, functionStart, nextSlot++);
+ }
+ initializeInternalFunctionParameter(THIS, function, functionStart, nextSlot++);
+ if(function.isVarArg()) {
+ initializeInternalFunctionParameter(VARARGS, function, functionStart, nextSlot++);
+ } else {
+ for(final IdentNode param: function.getParameters()) {
+ final Symbol symbol = param.getSymbol();
+ if(symbol.isBytecodeLocal()) {
+ method.initializeMethodParameter(symbol, param.getType(), functionStart);
+ }
+ }
+ }
+ }
+
+ private void initializeInternalFunctionParameter(final CompilerConstants cc, final FunctionNode fn, final Label functionStart, final int slot) {
+ final Symbol symbol = initializeInternalFunctionOrSplitParameter(cc, fn, functionStart, slot);
+ // Internal function params (:callee, this, and :varargs) are never expanded to multiple slots
+ assert symbol.getFirstSlot() == slot;
+ }
+
+ private Symbol initializeInternalFunctionOrSplitParameter(final CompilerConstants cc, final FunctionNode fn, final Label functionStart, final int slot) {
+ final Symbol symbol = fn.getBody().getExistingSymbol(cc.symbolName());
+ final Type type = Type.typeFor(cc.type());
+ method.initializeMethodParameter(symbol, type, functionStart);
+ method.onLocalStore(type, slot);
+ return symbol;
+ }
+
+ /**
+ * Parameters come into the method packed into local variable slots next to each other. Nashorn on the other hand
+ * can use 1-6 slots for a local variable depending on all the types it needs to store. When this method is invoked,
+ * the symbols are already allocated such wider slots, but the values are still in tightly packed incoming slots,
+ * and we need to spread them into their new locations.
+ * @param function the function for which parameter-spreading code needs to be emitted
+ */
+ private void expandParameterSlots(final FunctionNode function) {
+ final List<IdentNode> parameters = function.getParameters();
+ // Calculate the total number of incoming parameter slots
+ int currentIncomingSlot = function.needsCallee() ? 2 : 1;
+ for(final IdentNode parameter: parameters) {
+ currentIncomingSlot += parameter.getType().getSlots();
+ }
+ // Starting from last parameter going backwards, move the parameter values into their new slots.
+ for(int i = parameters.size(); i-- > 0;) {
+ final IdentNode parameter = parameters.get(i);
+ final Type parameterType = parameter.getType();
+ final int typeWidth = parameterType.getSlots();
+ currentIncomingSlot -= typeWidth;
+ final Symbol symbol = parameter.getSymbol();
+ final int slotCount = symbol.slotCount();
+ assert slotCount > 0;
+ // Scoped parameters must not hold more than one value
+ assert symbol.isBytecodeLocal() || slotCount == typeWidth;
+
+ // Mark it as having its value stored into it by the method invocation.
+ method.onLocalStore(parameterType, currentIncomingSlot);
+ if(currentIncomingSlot != symbol.getSlot(parameterType)) {
+ method.load(parameterType, currentIncomingSlot);
+ method.store(symbol, parameterType);
+ }
+ }
}
private void initArguments(final FunctionNode function) {
@@ -1075,15 +1854,45 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
method.storeCompilerConstant(ARGUMENTS);
}
+ private boolean skipFunction(final FunctionNode functionNode) {
+ final ScriptEnvironment env = compiler.getScriptEnvironment();
+ final boolean lazy = env._lazy_compilation;
+ final boolean onDemand = compiler.isOnDemandCompilation();
+
+ // If this is on-demand or lazy compilation, don't compile a nested (not topmost) function.
+ if((onDemand || lazy) && lc.getOutermostFunction() != functionNode) {
+ return true;
+ }
+
+ // If lazy compiling with optimistic types, don't compile the program eagerly either. It will soon be
+ // invalidated anyway. In presence of a class cache, this further means that an obsoleted program version
+ // lingers around. Also, currently loading previously persisted optimistic types information only works if
+ // we're on-demand compiling a function, so with this strategy the :program method can also have the warmup
+ // benefit of using previously persisted types.
+ //
+ // NOTE that this means the first compiled class will effectively just have a :createProgramFunction method, and
+ // the RecompilableScriptFunctionData (RSFD) object in its constants array. It won't even have the :program
+ // method. This is by design. It does mean that we're wasting one compiler execution (and we could minimize this
+ // by just running it up to scope depth calculation, which creates the RSFDs and then this limited codegen).
+ // We could emit an initial separate compile unit with the initial version of :program in it to better utilize
+ // the compilation pipeline, but that would need more invasive changes, as currently the assumption that
+ // :program is emitted into the first compilation unit of the function lives in many places.
+ return !onDemand && lazy && env._optimistic_types && functionNode.isProgram();
+ }
+
@Override
public boolean enterFunctionNode(final FunctionNode functionNode) {
- if (functionNode.isLazy()) {
- // Must do it now; can't postpone it until leaveFunctionNode()
- newFunctionObject(functionNode, functionNode);
+ final int fnId = functionNode.getId();
+
+ if (skipFunction(functionNode)) {
+ // In case we are not generating code for the function, we must create or retrieve the function object and
+ // load it on the stack here.
+ newFunctionObject(functionNode, false);
return false;
}
final String fnName = functionNode.getName();
+
// NOTE: we only emit the method for a function with the given name once. We can have multiple functions with
// the same name as a result of inlining finally blocks. However, in the future -- with type specialization,
// notably -- we might need to check for both name *and* signature. Of course, even that might not be
@@ -1092,35 +1901,67 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
// to decide to either generate a unique method for each inlined copy of the function, maybe figure out its
// exact type closure and deduplicate based on that, or just decide that functions in finally blocks aren't
// worth it, and generate one method with most generic type closure.
- if(!emittedMethods.contains(fnName)) {
- LOG.info("=== BEGIN ", fnName);
+ if (!emittedMethods.contains(fnName)) {
+ log.info("=== BEGIN ", fnName);
assert functionNode.getCompileUnit() != null : "no compile unit for " + fnName + " " + Debug.id(functionNode);
unit = lc.pushCompileUnit(functionNode.getCompileUnit());
assert lc.hasCompileUnits();
- method = lc.pushMethodEmitter(unit.getClassEmitter().method(functionNode));
+ final ClassEmitter classEmitter = unit.getClassEmitter();
+ pushMethodEmitter(isRestOf() ? classEmitter.restOfMethod(functionNode) : classEmitter.method(functionNode));
+ method.setPreventUndefinedLoad();
+ if(useOptimisticTypes()) {
+ lc.pushUnwarrantedOptimismHandlers();
+ }
+
// new method - reset last line number
lastLineNumber = -1;
- // Mark end for variable tables.
+
method.begin();
+
+ if (isRestOf()) {
+ final ContinuationInfo ci = new ContinuationInfo();
+ fnIdToContinuationInfo.put(fnId, ci);
+ method.gotoLoopStart(ci.getHandlerLabel());
+ }
}
return true;
}
+ private void pushMethodEmitter(final MethodEmitter newMethod) {
+ method = lc.pushMethodEmitter(newMethod);
+ catchLabels.push(METHOD_BOUNDARY);
+ }
+
+ private void popMethodEmitter() {
+ method = lc.popMethodEmitter(method);
+ assert catchLabels.peek() == METHOD_BOUNDARY;
+ catchLabels.pop();
+ }
+
@Override
public Node leaveFunctionNode(final FunctionNode functionNode) {
try {
- if(emittedMethods.add(functionNode.getName())) {
+ final boolean markOptimistic;
+ if (emittedMethods.add(functionNode.getName())) {
+ markOptimistic = generateUnwarrantedOptimismExceptionHandlers(functionNode);
+ generateContinuationHandler();
method.end(); // wrap up this method
unit = lc.popCompileUnit(functionNode.getCompileUnit());
- method = lc.popMethodEmitter(method);
- LOG.info("=== END ", functionNode.getName());
+ popMethodEmitter();
+ log.info("=== END ", functionNode.getName());
+ } else {
+ markOptimistic = false;
+ }
+
+ FunctionNode newFunctionNode = functionNode.setState(lc, CompilationState.BYTECODE_GENERATED);
+ if (markOptimistic) {
+ newFunctionNode = newFunctionNode.setFlag(lc, FunctionNode.IS_DEOPTIMIZABLE);
}
- final FunctionNode newFunctionNode = functionNode.setState(lc, CompilationState.EMITTED);
- newFunctionObject(newFunctionNode, functionNode);
+ newFunctionObject(newFunctionNode, true);
return newFunctionNode;
} catch (final Throwable t) {
Context.printStackTrace(t);
@@ -1131,51 +1972,47 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
}
@Override
- public boolean enterIdentNode(final IdentNode identNode) {
- return false;
- }
-
- @Override
public boolean enterIfNode(final IfNode ifNode) {
- lineNumber(ifNode);
+ if(!method.isReachable()) {
+ return false;
+ }
+ enterStatement(ifNode);
final Expression test = ifNode.getTest();
final Block pass = ifNode.getPass();
final Block fail = ifNode.getFail();
+ final boolean hasFailConversion = LocalVariableConversion.hasLiveConversion(ifNode);
final Label failLabel = new Label("if_fail");
- final Label afterLabel = fail == null ? failLabel : new Label("if_done");
-
- new BranchOptimizer(this, method).execute(test, failLabel, false);
+ final Label afterLabel = (fail == null && !hasFailConversion) ? null : new Label("if_done");
- boolean passTerminal = false;
- boolean failTerminal = false;
+ emitBranch(test, failLabel, false);
pass.accept(this);
- if (!pass.hasTerminalFlags()) {
+ if(method.isReachable() && afterLabel != null) {
method._goto(afterLabel); //don't fallthru to fail block
- } else {
- passTerminal = pass.isTerminal();
}
+ method.label(failLabel);
if (fail != null) {
- method.label(failLabel);
fail.accept(this);
- failTerminal = fail.isTerminal();
+ } else if(hasFailConversion) {
+ method.beforeJoinPoint(ifNode);
}
- //if if terminates, put the after label there
- if (!passTerminal || !failTerminal) {
+ if(afterLabel != null) {
method.label(afterLabel);
}
return false;
}
- @Override
- public boolean enterIndexNode(final IndexNode indexNode) {
- load(indexNode);
- return false;
+ private void emitBranch(final Expression test, final Label label, final boolean jumpWhenTrue) {
+ new BranchOptimizer(this, method).execute(test, label, jumpWhenTrue);
+ }
+
+ private void enterStatement(final Statement statement) {
+ lineNumber(statement);
}
private void lineNumber(final Statement statement) {
@@ -1189,6 +2026,10 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
lastLineNumber = lineNumber;
}
+ int getLastLineNumber() {
+ return lastLineNumber;
+ }
+
/**
* Load a list of nodes as an array of a specific type
* The array will contain the visited nodes.
@@ -1219,26 +2060,34 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
unit = lc.pushCompileUnit(arrayUnit.getCompileUnit());
final String className = unit.getUnitClassName();
+ assert unit != null;
final String name = currentFunction.uniqueName(SPLIT_PREFIX.symbolName());
final String signature = methodDescriptor(type, ScriptFunction.class, Object.class, ScriptObject.class, type);
- final MethodEmitter me = unit.getClassEmitter().method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), name, signature);
- method = lc.pushMethodEmitter(me);
+ pushMethodEmitter(unit.getClassEmitter().method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), name, signature));
method.setFunctionNode(currentFunction);
method.begin();
+ defineCommonSplitMethodParameters();
+ defineSplitMethodParameter(3, arrayType);
+
fixScopeSlot(currentFunction);
- method.load(arrayType, SPLIT_ARRAY_ARG.slot());
+ lc.enterSplitNode();
+ final int arraySlot = SPLIT_ARRAY_ARG.slot();
for (int i = arrayUnit.getLo(); i < arrayUnit.getHi(); i++) {
+ method.load(arrayType, arraySlot);
storeElement(nodes, elementType, postsets[i]);
}
+ method.load(arrayType, arraySlot);
method._return();
+ lc.exitSplitNode();
method.end();
- method = lc.popMethodEmitter(me);
+ lc.releaseSlots();
+ popMethodEmitter();
assert method == savedMethod;
method.loadCompilerConstant(CALLEE);
@@ -1255,15 +2104,19 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
return method;
}
- for (final int postset : postsets) {
- storeElement(nodes, elementType, postset);
+ if(postsets.length > 0) {
+ final int arraySlot = method.getUsedSlotsWithLiveTemporaries();
+ method.storeTemp(arrayType, arraySlot);
+ for (final int postset : postsets) {
+ method.load(arrayType, arraySlot);
+ storeElement(nodes, elementType, postset);
+ }
+ method.load(arrayType, arraySlot);
}
-
return method;
}
private void storeElement(final Expression[] nodes, final Type elementType, final int index) {
- method.dup();
method.load(index);
final Expression element = nodes[index];
@@ -1271,7 +2124,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
if (element == null) {
method.loadEmpty(elementType);
} else {
- load(element, elementType);
+ loadExpressionAsType(element, elementType);
}
method.arraystore();
@@ -1284,7 +2137,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
for (int i = 0; i < args.size(); i++) {
method.dup();
method.load(i);
- load(args.get(i), Type.OBJECT); //has to be upcast to object or we fail
+ loadExpression(args.get(i), TypeBounds.OBJECT); // variable arity methods always take objects
method.arraystore();
}
@@ -1314,44 +2167,48 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
* @param object object to load
*/
void loadConstant(final Object object) {
- final String unitClassName = unit.getUnitClassName();
- final ClassEmitter classEmitter = unit.getClassEmitter();
+ loadConstant(object, unit, method);
+ }
+
+ private void loadConstant(final Object object, final CompileUnit compileUnit, final MethodEmitter methodEmitter) {
+ final String unitClassName = compileUnit.getUnitClassName();
+ final ClassEmitter classEmitter = compileUnit.getClassEmitter();
final int index = compiler.getConstantData().add(object);
final Class<?> cls = object.getClass();
if (cls == PropertyMap.class) {
- method.load(index);
- method.invokestatic(unitClassName, GET_MAP.symbolName(), methodDescriptor(PropertyMap.class, int.class));
+ methodEmitter.load(index);
+ methodEmitter.invokestatic(unitClassName, GET_MAP.symbolName(), methodDescriptor(PropertyMap.class, int.class));
classEmitter.needGetConstantMethod(PropertyMap.class);
} else if (cls.isArray()) {
- method.load(index);
+ methodEmitter.load(index);
final String methodName = ClassEmitter.getArrayMethodName(cls);
- method.invokestatic(unitClassName, methodName, methodDescriptor(cls, int.class));
+ methodEmitter.invokestatic(unitClassName, methodName, methodDescriptor(cls, int.class));
classEmitter.needGetConstantMethod(cls);
} else {
- method.loadConstants().load(index).arrayload();
+ methodEmitter.loadConstants().load(index).arrayload();
if (object instanceof ArrayData) {
// avoid cast to non-public ArrayData subclass
- method.checkcast(ArrayData.class);
- method.invoke(virtualCallNoLookup(ArrayData.class, "copy", ArrayData.class));
+ methodEmitter.checkcast(ArrayData.class);
+ methodEmitter.invoke(virtualCallNoLookup(ArrayData.class, "copy", ArrayData.class));
} else if (cls != Object.class) {
- method.checkcast(cls);
+ methodEmitter.checkcast(cls);
}
}
}
// literal values
- private MethodEmitter loadLiteral(final LiteralNode<?> node, final Type type) {
+ private void loadLiteral(final LiteralNode<?> node, final TypeBounds resultBounds) {
final Object value = node.getValue();
if (value == null) {
method.loadNull();
} else if (value instanceof Undefined) {
- method.loadUndefined(Type.OBJECT);
+ method.loadUndefined(resultBounds.within(Type.OBJECT));
} else if (value instanceof String) {
final String string = (String)value;
- if (string.length() > (MethodEmitter.LARGE_STRING_THRESHOLD / 3)) { // 3 == max bytes per encoded char
+ if (string.length() > MethodEmitter.LARGE_STRING_THRESHOLD / 3) { // 3 == max bytes per encoded char
loadConstant(string);
} else {
method.load(string);
@@ -1361,31 +2218,40 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
} else if (value instanceof Boolean) {
method.load((Boolean)value);
} else if (value instanceof Integer) {
- if(type.isEquivalentTo(Type.NUMBER)) {
+ if(!resultBounds.canBeNarrowerThan(Type.OBJECT)) {
+ method.load((Integer)value);
+ method.convert(Type.OBJECT);
+ } else if(!resultBounds.canBeNarrowerThan(Type.NUMBER)) {
method.load(((Integer)value).doubleValue());
- } else if(type.isEquivalentTo(Type.LONG)) {
+ } else if(!resultBounds.canBeNarrowerThan(Type.LONG)) {
method.load(((Integer)value).longValue());
} else {
method.load((Integer)value);
}
} else if (value instanceof Long) {
- if(type.isEquivalentTo(Type.NUMBER)) {
+ if(!resultBounds.canBeNarrowerThan(Type.OBJECT)) {
+ method.load((Long)value);
+ method.convert(Type.OBJECT);
+ } else if(!resultBounds.canBeNarrowerThan(Type.NUMBER)) {
method.load(((Long)value).doubleValue());
} else {
method.load((Long)value);
}
} else if (value instanceof Double) {
- method.load((Double)value);
+ if(!resultBounds.canBeNarrowerThan(Type.OBJECT)) {
+ method.load((Double)value);
+ method.convert(Type.OBJECT);
+ } else {
+ method.load((Double)value);
+ }
} else if (node instanceof ArrayLiteralNode) {
final ArrayLiteralNode arrayLiteral = (ArrayLiteralNode)node;
final ArrayType atype = arrayLiteral.getArrayType();
loadArray(arrayLiteral, atype);
globalAllocateArray(atype);
} else {
- assert false : "Unknown literal for " + node.getClass() + " " + value.getClass() + " " + value;
+ throw new UnsupportedOperationException("Unknown literal for " + node.getClass() + " " + value.getClass() + " " + value);
}
-
- return method;
}
private MethodEmitter loadRegexToken(final RegexToken value) {
@@ -1422,35 +2288,65 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
return method;
}
- @Override
- public boolean enterLiteralNode(final LiteralNode<?> literalNode) {
- return enterLiteralNode(literalNode, literalNode.getType());
- }
+ /**
+ * Check if a property value contains a particular program point
+ * @param value value
+ * @param pp program point
+ * @return true if it's there.
+ */
+ private static boolean propertyValueContains(final Expression value, final int pp) {
+ return new Supplier<Boolean>() {
+ boolean contains;
- private boolean enterLiteralNode(final LiteralNode<?> literalNode, final Type type) {
- assert literalNode.getSymbol() != null : literalNode + " has no symbol";
- loadLiteral(literalNode, type).convert(type).store(literalNode.getSymbol());
- return false;
+ @Override
+ public Boolean get() {
+ value.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
+ @Override
+ public boolean enterFunctionNode(final FunctionNode functionNode) {
+ return false;
+ }
+
+ @Override
+ public boolean enterObjectNode(final ObjectNode objectNode) {
+ return false;
+ }
+
+ @Override
+ public boolean enterDefault(final Node node) {
+ if (contains) {
+ return false;
+ }
+ if (node instanceof Optimistic && ((Optimistic)node).getProgramPoint() == pp) {
+ contains = true;
+ return false;
+ }
+ return true;
+ }
+ });
+
+ return contains;
+ }
+ }.get();
}