aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorattila <none@none>2014-08-20 10:26:01 +0200
committerattila <none@none>2014-08-20 10:26:01 +0200
commit1a6dc8268c841b348bd97c07e739ecd871ce15a4 (patch)
tree6e361a5ea5259a0bc7eeea0bbe727e74187d418a
parent0b6e5c2050ccc3aac8dfcbd8f0daa9e707cf1093 (diff)
downloadnashorn-1a6dc8268c841b348bd97c07e739ecd871ce15a4.tar.gz
8027043: Turn global accesses into MethodHandle.constant, with one chance of reassignment, e.g. x = value occuring once in the global scope is ok, twice is not.
8027958: NASHORN TEST: Create tests to test markdown javascript engine work with Nashorn 8028345: Remove nashorn repo "bin" scripts to avoid confusion with JDK bin launcher programs 8029090: Developers should be able to pass nashorn properties and enable/disable JFR from command line 8030169: Need regression test for bug JDK-8010731 8033105: Make sure Nashorn test harness can run zlib benchmark 8033334: Make sure that scope depth information is maintained in the RecompilableScriptFunctionDatas, to avoid unnecessary slow proto linkage when doing on demand compilation 8034206: Make parts of code pipeline reusable in order to facilitate faster warmup and faster lazy compilation. 8035820: Optimistic recompilation 8035836: Array performance improvements 8036127: Prototype filter needs to be applied to getter guard as well, not just getter 8036986: Test should check that correctly type is returned running with optimistic. If optimistic assumption was wrong we should get the right one. 8037086: Check that deoptimizing recompilations are correct 8037177: -Dnashorn.optimistic should be enabled by default, meaning that it has to be explicitly set to false to run with the jdk 8 style conservative types 8037534: Use scope types to determine optimistic types 8037572: Add more test cases to check static types 8037967: Broke the build, by commiting without saving the last review comment 8038223: Symbol trace debug output takes time 8038396: fix for the compiler expression evaluator to be more inquisitive about types 8038398: OptimisticRecompilationTest fails on staging repo nashorn/jdk9/nashorn due to test framework 8038406: Testability: as a first step of moving loggers away from the process global space, the Debug object now supports logging POJOs from log entries as an event queue, which can be introspected from test scripts. This is way better than screen scraping brittle and subject-to-change log output. 8038413: NPE in unboxInteger 8038416: Access to undefined scoped variables deoptimized too much 8038426: Move all loggers from process wide scope into Global scope 8038799: Guard and unbox boxed primitives types on setting them in Properties to avoid megamorphisism 8038945: Simplify strict undefined checks 8039044: Expand undefined intrinsics for all commutative combinators of scrict undefined checks 8039746: Transform applies to calls wherever possible, for ScriptFunctions and JSObjects. 8040024: BranchOptimizer produces bad code for NaN FP comparison 8040089: Apply to call transform was incomplete. Now passes all tests and performance is back 8040093: Make sure that optimistic splitting works in optimistic types 8040102: Remove all references to Unsafe and definition of anonymous clases from the code 8040655: When processing a RewriteException debug object, the return value has already been reset to null. We need to catch this value before that. 8041434: Add synchronization to the common global constants structure 8041625: AccessorProperty currentType must only by Object.class when non-primitive, and scoping followup problem for lazily generated with bodies 8041905: Fix apply2call bug that prevented avatar.js unit tests from running correctly 8041995: Problems when loading tree expressions with several optimistic program points when optimistically initializing ObjectNodes 8042118: Separate types from symbols 8043002: Improve performance of Nashorn equality operators 8043003: Use strongly referenced generic invokers 8043004: Reduce variability at JavaAdapter call sites 8043132: Nashorn : all tests failed with java.security.AccessControlException 8043133: Fix corner cases of JDK-8041995 8043137: Collapse long sequences of NOP in Nashorn bytecode output 8043232: Index selection of overloaded java new constructors 8043235: Type-based optimizations interfere with continuation methods 8043431: Fix yet another corner case of JDK-8041995 8043504: Octane test harness was missing argument to print_always at one callsite, causing erroneous logging 8043605: Enable history for empty property maps 8043608: Make equality tests inline better 8043611: Move timing dependent benchmark for apply2call specialization to currently_failing. It is dependent that nothing takes machine time when doing the two runs, causing spurious assertions. Suggest running octane.raytrace manually instead to verify that this works, or incorporating it in the nightly test suite 8043632: Parallelize class installation and various script fixes. 8043633: In order to remove global state outside of contexts, make sure Timing class is an instance and not a static global collection of data. Move into Context. Move -Dnashorn.timing to an official logging option. 8043956: Make code caching work with optimistic typing and lazy compilation 8044012: Integrate the latest best known performance flags int ant octane jobs, and make sure that it's easy to compare 'ant octane-nashorn' and 'ant octane-v8' at the push of a button. (or rather; the entry of a command line) 8044102: Ensure bechmark exclude list for Octane benchmarks is in only one place, project.properties, and fix benchmark harness 8044154: Nashorn : all tests failed with java.security.AccessControlException 8044171: Make optimistic exception handlers smaller 8044502: Get rid of global optimistic flag 8044518: Ensure exceptions related to optimistic recompilation are not serializable 8044533: Deoptimizing negation produces wrong result for zero 8044534: Constant folding for unary + should produce int for boolean literals 8044760: Avoid PropertyMap duplicate for global instances 8044786: Some tests fail with non-optimistic compilation 8044803: Unnecessary restOf check 8044816: On-demand compiled top-level program doesn't need :createProgramFunction 8044851: nashorn properties leak memory 8046013: TypeError: Cannot apply "with" to non script object 8046014: MultiGlobalCompiledScript should cache :createProgramFunction handle 8046025: AccessorProperty.getGetter is not threadsafe 8046026: CompiledFunction.relinkComposableInvoker assert is being hit 8046201: Avoid repeated flattening of nested ConsStrings 8046215: Running uncompilable scripts throws NullPointerException 8046898: Make sure that lazy compilation is the default, remove redundant "enable lazy compilation" flags, added warning message if compile logging is enabled and lazy is switched off. Verified existing test suite code coverage equivalence between lazy and eager. 8046905: apply on apply is broken 8046921: Deoptimization type information peristence 8047035: (function() "hello")() crashes in Lexer with jdk9 8047057: Add a regression test for the passing test cases from JDK-8042304 8047067: all eval arguments need to be copied in Lower 8047078: Fuzzing bug discovered when ArrayLiteralNodes weren't immutable 8047166: 'do with({}) break ; while(0);' crashes in CodeGenerator 8047331: Assertion in CompiledFunction when running earley-boyer after Merge 8047357: More precise synthetic return + unreachable throw 8047359: large string size RangeError should be thrown rather than reporting negative length 8047369: Add regression tests for passing test cases of JDK-8024971 8047371: local variable declaration in TypeEvaluator should use ScriptObject.addOwnProperty instead of .set 8047728: (function(x){var o={x:0}; with(o){delete x} return o.x})() evaluates to 0 instead of undefined 8047959: bindings created for declarations in eval code are not mutable 8048009: Type info caching accidentally defeated 8048071: eval within 'with' statement does not use correct scope if with scope expression has a copy of eval 8048079: Persistent code store is broken after optimistic types merge 8048505: ScriptingFunctions.readFully couldn't handle file names represented as ConsStrings 8048586: String concatenation with optimistic types is slow 8048718: JSON.parse('{"0":0, "64":0}') throws ArrayindexOutOfBoundsException 8048869: Reduce compile time by about 5% by removing the Class.casts from the AST nodes 8049086: Minor API convenience functions on "Java" object 8049222: JSType class exposes public mutable arrays 8049223: RewriteException class exposes public mutable arrays 8049242: Explicit constructor overload selection should work with StaticClass as well 8049318: Test hideLocationProperties.js fails on Window due to backslash in path 8049524: Global object initialization via javax.script API should be minimal 8050432: javax.script.filename variable should not be enumerable with nashorn engine's ENGINE_SCOPE bindings 8050964: OptimisticTypesPersistence.java should use java.util.Date instead of java.sql.Date 8051019: Separate src and test execution sandbox directories 8051346: Test262 tests for ECMAScript 5 now in branch "es5-tests" 8051439: Wrong type calculated for ADD operator with undefined operand 8051839: GuardedInvocation needs to clone an argument 8053908: jdeps is not PATH on Mac, results in ant clean test failure on Mac 8053910: ScriptObjectMirror causing havoc with Invocation interface 8053913: Auto format caused warning in CompositeTypeBasedGuardingDynamicLinker 8054223: Nashorn: AssertionError when use __DIR__ and ScriptEngine.eval() 8054411: Add nashorn.args.prepend system property 8054503: test/script/external/test262/test/suite/ch12/12.6/12.6.4/12.6.4-2.js fails with tip 8054651: Global.initConstructor and ScriptFunction.getPrototype(Object) can have stricter types 8054898: Avoid creation of empty type info files 8054993: type info cache may be disabled for test262 and tests explicitly changing that property should use @fork 8055034: jjs exits interactive mode if exception was thrown when trying to print value of last evaluated expression 8055042: Compile-time expression evaluator was missing variables 8055107: Extension directives to turn on callsite profiling, tracing, AST print and other debug features locally 8055139: test/script/trusted/JDK-8055107.js fails with access control exception 8055186: Backport Nashorn optimistic typing to 8u repository 8055529: Clean up the bin directory Reviewed-by: jlaskey, lagergren, sundar Contributed-by: marcus.largergren@oracle.com, hannes.wallnoefer@oracle.com, sundararajan.athijegannathan@oracle.com --HG-- rename : test/script/basic/arraysIntKey.js => test/script/basic/arrays_int_key.js rename : test/script/basic/arraysIntKey.js.EXPECTED => test/script/basic/arrays_int_key.js.EXPECTED rename : test/script/basic/runsunspider-lazy.js.EXPECTED => test/script/basic/runsunspider.js.EXPECTED rename : test/script/basic/JDK-8010697.js.EXPECTED => test/script/currently-failing/JDK-8010697.js.EXPECTED rename : test/script/maptests/property_delete.js => test/script/currently-failing/property_delete.js
-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);