Mercurial > people > rkennke > jdk9-shenandoah-final > nashorn
changeset 1075:2a3502a38f8c
Merge
author | lana |
---|---|
date | Thu, 23 Oct 2014 13:45:22 -0700 |
parents | bba8e963ccf2 (current diff) 41b5976633aa (diff) |
children | 871cd9451896 |
files | src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/SplitMethodEmitter.java src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/NoTypeArrayData.java |
diffstat | 118 files changed, 3128 insertions(+), 1674 deletions(-) [+] |
line wrap: on
line diff
--- a/bin/runopt.sh Thu Oct 23 11:19:29 2014 -0700 +++ b/bin/runopt.sh Thu Oct 23 13:45:22 2014 -0700 @@ -69,7 +69,6 @@ if [ -z $JFR_FILENAME ]; then JFR_FILENAME="./nashorn_$(date|sed "s/ /_/g"|sed "s/:/_/g").jfr" - echo "Using default JFR filename: ${JFR_FILENAME}..." fi # Flight recorder
--- a/docs/DEVELOPER_README Thu Oct 23 11:19:29 2014 -0700 +++ b/docs/DEVELOPER_README Thu Oct 23 13:45:22 2014 -0700 @@ -25,6 +25,14 @@ > java -Dnashorn.args="--lazy-complation --log=compiler" large-java-app-with-nashorn.jar > ant -Dnashorn.args="--log=codegen" antjob +SYSTEM PROPERTY: -Dnashorn.args.prepend=<string> + +This property behaves like nashorn.args, but adds the given arguments +before the existing ones instead of after them. Later arguments will +overwrite earlier ones, so this is useful for setting default arguments +that can be overwritten. + + SYSTEM PROPERTY: -Dnashorn.unstable.relink.threshold=x This property controls how many call site misses are allowed before a @@ -42,533 +50,38 @@ The default value is 0x8000 (32768). -SYSTEM PROPERTY: -Dnashorn.compiler.intarithmetic - -(and integer arithmetic in general) - -<currently disabled - this is being refactored for update releases> - -Arithmetic operations in Nashorn (except bitwise ones) typically -coerce the operands to doubles (as per the JavaScript spec). To switch -this off and remain in integer mode, for example for "var x = a&b; var -y = c&d; var z = x*y;", use this flag. This will force the -multiplication of variables that are ints to be done with the IMUL -bytecode and the result "z" to become an int. - -WARNING: Note that is is experimental only to ensure that type support -exists for all primitive types. The generated code is unsound. This -will be the case until we do optimizations based on it. There is a CR -in Nashorn to do better range analysis, and ensure that this is only -done where the operation can't overflow into a wider type. Currently -no overflow checking is done, so at the moment, until range analysis -has been completed, this option is turned off. - -We've experimented by using int arithmetic for everything and putting -overflow checks afterwards, which would recompute the operation with -the correct precision, but have yet to find a configuration where this -is faster than just using doubles directly, even if the int operation -does not overflow. Getting access to a JVM intrinsic that does branch -on overflow would probably alleviate this. - -The future: - -We are transitioning to an optimistic type system that uses int -arithmetic everywhere until proven wrong. The problem here is mostly -catch an overflow exception and rolling back the state to a new method -with less optimistic assumptions for an operation at a particular -program point. This will most likely not be in the Java 8.0 release -but likely end up in an update release - -For Java 8, several java.lang.Math methods like addExact, subExact and -mulExact are available to help us. Experiments intrinsifying these -show a lot of promise, and we have devised a system that basically -does on stack replacement with exceptions in bytecode to revert -erroneous assumptions. An explanation of how this works and what we -are doing can be found here: -http://www.slideshare.net/lagergren/lagergren-jvmls2013final - -Experiments with this show significant ~x2-3 performance increases on -pretty much everything, provided that optimistic assumptions don't -fail much. It will affect warmup time negatively, depending on how -many erroneous too optimistic assumptions are placed in the code at -compile time. We don't think this will be much of an issue. - -For example for a small benchmark that repeatedly executes this -method taken from the Crypto Octane benchmark - -function am3(i,x,w,j,c,n) { - var this_array = this.array; - var w_array = w.array; - var xl = x&0x3fff, xh = x>>14; - while(--n >= 0) { - var l = this_array[i]&0x3fff; - var h = this_array[i++]>>14; - var m = xh*l+h*xl; - l = xl*l+((m&0x3fff)<<14)+w_array[j]+c; - c = (l>>28)+(m>>14)+xh*h; - w_array[j++] = l&0xfffffff; - } - - return c; -} - -The performance increase more than doubles. We are also working hard -with the code generation team in the Java Virtual Machine to fix -things that are lacking in invokedynamic performance, which is another -area where a lot of ongoing performance work takes place - -"Pessimistic" bytecode for am3, guaranteed to be semantically correct: +SYSTEM PROPERTY: -Dnashorn.serialize.compression=<x> -// access flags 0x9 - public static am3(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; - L0 - LINENUMBER 12 L0 - ALOAD 0 - INVOKEDYNAMIC dyn:getProp|getElem|getMethod:array(Ljava/lang/Object;)Ljava/lang/Object; [ - // handle kind 0x6 : INVOKESTATIC - jdk/nashorn/internal/runtime/linker/Bootstrap.bootstrap((Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;I)Ljava/lang/invoke/CallSite;) - // arguments: - 0 - ] - ASTORE 8 - L1 - LINENUMBER 13 L1 - ALOAD 3 - INVOKEDYNAMIC dyn:getProp|getElem|getMethod:array(Ljava/lang/Object;)Ljava/lang/Object; [ - // handle kind 0x6 : INVOKESTATIC - jdk/nashorn/internal/runtime/linker/Bootstrap.bootstrap((Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;I)Ljava/lang/invoke/CallSite;) - // arguments: - 0 - ] - ASTORE 9 - L2 - LINENUMBER 14 L2 - ALOAD 2 - INVOKESTATIC jdk/nashorn/internal/runtime/JSType.toInt32 (Ljava/lang/Object;)I - SIPUSH 16383 - IAND - ISTORE 10 - ALOAD 2 - INVOKESTATIC jdk/nashorn/internal/runtime/JSType.toInt32 (Ljava/lang/Object;)I - BIPUSH 14 - ISHR - ISTORE 11 - L3 - LINENUMBER 15 L3 - GOTO L4 - L5 - LINENUMBER 16 L5 - FRAME FULL [java/lang/Object java/lang/Object java/lang/Object java/lang/Object java/lang/Object java/lang/Object java/lang/Double T java/lang/Object java/lang/Object I I] [] - ALOAD 8 - ALOAD 1 - INVOKEDYNAMIC dyn:getElem|getProp|getMethod(Ljava/lang/Object;Ljava/lang/Object;)I [ - // handle kind 0x6 : INVOKESTATIC - jdk/nashorn/internal/runtime/linker/Bootstrap.bootstrap((Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;I)Ljava/lang/invoke/CallSite;) - // arguments: - 0 - ] - SIPUSH 16383 - IAND - INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer; - ASTORE 12 - L6 - LINENUMBER 17 L6 - ALOAD 8 - ALOAD 1 - INVOKESTATIC jdk/nashorn/internal/runtime/JSType.toNumber (Ljava/lang/Object;)D - DUP2 - DCONST_1 - DADD - INVOKESTATIC java/lang/Double.valueOf (D)Ljava/lang/Double; - ASTORE 1 - INVOKEDYNAMIC dyn:getElem|getProp|getMethod(Ljava/lang/Object;D)I [ - // handle kind 0x6 : INVOKESTATIC - jdk/nashorn/internal/runtime/linker/Bootstrap.bootstrap((Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;I)Ljava/lang/invoke/CallSite;) - // arguments: - 0 - ] - BIPUSH 14 - ISHR - ISTORE 13 - L7 - LINENUMBER 18 L7 - ILOAD 11 - I2D - ALOAD 12 - INVOKESTATIC jdk/nashorn/internal/runtime/JSType.toNumber (Ljava/lang/Object;)D - DMUL - ILOAD 13 - I2D - ILOAD 10 - I2D - DMUL - DADD - DSTORE 14 - L8 - LINENUMBER 19 L8 - ILOAD 10 - I2D - ALOAD 12 - INVOKESTATIC jdk/nashorn/internal/runtime/JSType.toNumber (Ljava/lang/Object;)D - DMUL - DLOAD 14 - INVOKESTATIC jdk/nashorn/internal/runtime/JSType.toInt32 (D)I - SIPUSH 16383 - IAND - BIPUSH 14 - ISHL - I2D - DADD - ALOAD 9 - ALOAD 4 - INVOKEDYNAMIC dyn:getElem|getProp|getMethod(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; [ - // handle kind 0x6 : INVOKESTATIC - jdk/nashorn/internal/runtime/linker/Bootstrap.bootstrap((Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;I)Ljava/lang/invoke/CallSite;) - // arguments: - 0 - ] - INVOKEDYNAMIC ADD:ODO_D(DLjava/lang/Object;)Ljava/lang/Object; [ - // handle kind 0x6 : INVOKESTATIC - jdk/nashorn/internal/runtime/linker/Bootstrap.runtimeBootstrap((Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;) - // arguments: none - ] - ALOAD 5 - INVOKEDYNAMIC ADD:OOO_I(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; [ - // handle kind 0x6 : INVOKESTATIC - jdk/nashorn/internal/runtime/linker/Bootstrap.runtimeBootstrap((Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;) - // arguments: none - ] - ASTORE 12 - L9 - LINENUMBER 20 L9 - ALOAD 12 - INVOKESTATIC jdk/nashorn/internal/runtime/JSType.toInt32 (Ljava/lang/Object;)I - BIPUSH 28 - ISHR - I2D - DLOAD 14 - INVOKESTATIC jdk/nashorn/internal/runtime/JSType.toInt32 (D)I - BIPUSH 14 - ISHR - I2D - DADD - ILOAD 11 - I2D - ILOAD 13 - I2D - DMUL - DADD - INVOKESTATIC java/lang/Double.valueOf (D)Ljava/lang/Double; - ASTORE 5 - L10 - LINENUMBER 21 L10 - ALOAD 9 - ALOAD 4 - INVOKESTATIC jdk/nashorn/internal/runtime/JSType.toNumber (Ljava/lang/Object;)D - DUP2 - DCONST_1 - DADD - INVOKESTATIC java/lang/Double.valueOf (D)Ljava/lang/Double; - ASTORE 4 - ALOAD 12 - INVOKESTATIC jdk/nashorn/internal/runtime/JSType.toInt32 (Ljava/lang/Object;)I - LDC 268435455 - IAND - INVOKEDYNAMIC dyn:setElem|setProp(Ljava/lang/Object;DI)V [ - // handle kind 0x6 : INVOKESTATIC - jdk/nashorn/internal/runtime/linker/Bootstrap.bootstrap((Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;I)Ljava/lang/invoke/CallSite;) - // arguments: - 0 - ] - L4 - FRAME FULL [java/lang/Object java/lang/Object java/lang/Object java/lang/Object java/lang/Object java/lang/Object java/lang/Object T java/lang/Object java/lang/Object I I] [] - ALOAD 6 - INVOKESTATIC jdk/nashorn/internal/runtime/JSType.toNumber (Ljava/lang/Object;)D - LDC -1.0 - DADD - DUP2 - INVOKESTATIC java/lang/Double.valueOf (D)Ljava/lang/Double; - ASTORE 6 - DCONST_0 - DCMPL - IFGE L5 - L11 - LINENUMBER 24 L11 - ALOAD 5 - ARETURN - -"Optimistic" bytecode that requires invalidation on e.g overflow. Factor -x2-3 speedup: - -public static am3(Ljava/lang/Object;IILjava/lang/Object;III)I - L0 - LINENUMBER 12 L0 - ALOAD 0 - INVOKEDYNAMIC dyn:getProp|getElem|getMethod:array(Ljava/lang/Object;)Ljava/lang/Object; [ - // handle kind 0x6 : INVOKESTATIC - jdk/nashorn/internal/runtime/linker/Bootstrap.bootstrap((Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;I)Ljava/lang/invoke/CallSite;) - // arguments: - 0 - ] - ASTORE 8 - L1 - LINENUMBER 13 L1 - ALOAD 3 - INVOKEDYNAMIC dyn:getProp|getElem|getMethod:array(Ljava/lang/Object;)Ljava/lang/Object; [ - // handle kind 0x6 : INVOKESTATIC - jdk/nashorn/internal/runtime/linker/Bootstrap.bootstrap((Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;I)Ljava/lang/invoke/CallSite;) - // arguments: - 0 - ] - ASTORE 9 - L2 - LINENUMBER 14 L2 - ILOAD 2 - SIPUSH 16383 - IAND - ISTORE 10 - ILOAD 2 - BIPUSH 14 - ISHR - ISTORE 11 - L3 - LINENUMBER 15 L3 - GOTO L4 - L5 - LINENUMBER 16 L5 - FRAME FULL [java/lang/Object I I java/lang/Object I I I T java/lang/Object java/lang/Object I I] [] - ALOAD 8 - ILOAD 1 - INVOKEDYNAMIC dyn:getElem|getProp|getMethod(Ljava/lang/Object;I)I [ - // handle kind 0x6 : INVOKESTATIC - jdk/nashorn/internal/runtime/linker/Bootstrap.bootstrap((Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;I)Ljava/lang/invoke/CallSite;) - // arguments: - 0 - ] - SIPUSH 16383 - IAND - ISTORE 12 - L6 - LINENUMBER 17 L6 - ALOAD 8 - ILOAD 1 - DUP - ICONST_1 - IADD - ISTORE 1 - INVOKEDYNAMIC dyn:getElem|getProp|getMethod(Ljava/lang/Object;I)I [ - // handle kind 0x6 : INVOKESTATIC - jdk/nashorn/internal/runtime/linker/Bootstrap.bootstrap((Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;I)Ljava/lang/invoke/CallSite;) - // arguments: - 0 - ] - BIPUSH 14 - ISHR - ISTORE 13 - L7 - LINENUMBER 18 L7 - ILOAD 11 - ILOAD 12 - BIPUSH 8 - INVOKESTATIC jdk/nashorn/internal/runtime/JSType.mulExact (III)I - ILOAD 13 - ILOAD 10 - BIPUSH 9 - INVOKESTATIC jdk/nashorn/internal/runtime/JSType.mulExact (III)I - IADD - ISTORE 14 - L8 - LINENUMBER 19 L8 - ILOAD 10 - ILOAD 12 - BIPUSH 11 - INVOKESTATIC jdk/nashorn/internal/runtime/JSType.mulExact (III)I - ILOAD 14 - SIPUSH 16383 - IAND - BIPUSH 14 - ISHL - IADD - ALOAD 9 - ILOAD 4 - INVOKEDYNAMIC dyn:getElem|getProp|getMethod(Ljava/lang/Object;I)I [ - // handle kind 0x6 : INVOKESTATIC - jdk/nashorn/internal/runtime/linker/Bootstrap.bootstrap((Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;I)Ljava/lang/invoke/CallSite;) - // arguments: - 0 - ] - IADD - ILOAD 5 - IADD - ISTORE 12 - L9 - LINENUMBER 20 L9 - ILOAD 12 - BIPUSH 28 - ISHR - ILOAD 14 - BIPUSH 14 - ISHR - IADD - ILOAD 11 - ILOAD 13 - BIPUSH 21 - INVOKESTATIC jdk/nashorn/internal/runtime/JSType.mulExact (III)I - IADD - ISTORE 5 - L10 - LINENUMBER 21 L10 - ALOAD 9 - ILOAD 4 - DUP - ICONST_1 - IADD - ISTORE 4 - ILOAD 12 - LDC 268435455 - IAND - INVOKEDYNAMIC dyn:setElem|setProp(Ljava/lang/Object;II)V [ - // handle kind 0x6 : INVOKESTATIC - jdk/nashorn/internal/runtime/linker/Bootstrap.bootstrap((Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;I)Ljava/lang/invoke/CallSite;) - // arguments: - 0 - ] - L4 - FRAME SAME - ILOAD 6 - ICONST_M1 - IADD - DUP - ISTORE 6 - ICONST_0 - IF_ICMPGE L5 - L11 - LINENUMBER 24 L11 - ILOAD 5 - IRETURN +This property sets the compression level used when deflating serialized +AST structures of anonymous split functions. Valid values range from 0 to 9, +the default value is 4. Higher values will reduce memory size of serialized +AST but increase CPU usage required for compression. -SYSTEM PROPERTY: -Dnashorn.codegen.debug, -Dnashorn.codegen.debug.trace=<x> +SYSTEM PROPERTY: -Dnashorn.codegen.debug.trace=<x> See the description of the codegen logger below. -SYSTEM_PROPERTY: -Dnashorn.fields.debug +SYSTEM PROPERTY: -Dnashorn.fields.objects -See the description on the fields logger below. - - -SYSTEM PROPERTY: -Dnashorn.fields.dual +When this property is true, Nashorn will only use object fields for +AccessorProperties. This means that primitive values must be boxed +when stored in a field, which is significantly slower than using +primitive fields. -When this property is true, Nashorn will attempt to use primitive -fields for AccessorProperties (currently just AccessorProperties, not -spill properties). Memory footprint for script objects will increase, -as we need to maintain both a primitive field (a long) as well as an -Object field for the property value. Ints are represented as the 32 -low bits of the long fields. Doubles are represented as the -doubleToLongBits of their value. This way a single field can be used -for all primitive types. Packing and unpacking doubles to their bit -representation is intrinsified by the JVM and extremely fast. - -While dual fields in theory runs significantly faster than Object -fields due to reduction of boxing and memory allocation overhead, -there is still work to be done to make this a general purpose -solution. Research is ongoing. +By default, Nashorn uses dual object and long fields. Ints are +represented as the 32 low bits of the long fields. Doubles are +represented as the doubleToLongBits of their value. This way a +single field can be used for all primitive types. Packing and +unpacking doubles to their bit representation is intrinsified by +the JVM and extremely fast. In the future, this might complement or be replaced by experimental feature sun.misc.TaggedArray, which has been discussed on the mlvm mailing list. TaggedArrays are basically a way to share data space between primitives and references, and have the GC understand this. -As long as only primitive values are written to the fields and enough -type information exists to make sure that any reads don't have to be -uselessly boxed and unboxed, this is significantly faster than the -standard "Objects only" approach that currently is the default. See -test/examples/dual-fields-micro.js for an example that runs twice as -fast with dual fields as without them. Here, the compiler, can -determine that we are dealing with numbers only throughout the entire -property life span of the properties involved. - -If a "real" object (not a boxed primitive) is written to a field that -has a primitive representation, its callsite is relinked and an Object -field is used forevermore for that particular field in that -PropertyMap and its children, even if primitives are later assigned to -it. - -As the amount of compile time type information is very small in a -dynamic language like JavaScript, it is frequently the case that -something has to be treated as an object, because we don't know any -better. In reality though, it is often a boxed primitive is stored to -an AccessorProperty. The fastest way to handle this soundly is to use -a callsite typecheck and avoid blowing the field up to an Object. We -never revert object fields to primitives. Ping-pong:ing back and forth -between primitive representation and Object representation would cause -fatal performance overhead, so this is not an option. - -For a general application the dual fields approach is still slower -than objects only fields in some places, about the same in most cases, -and significantly faster in very few. This is due the program using -primitives, but we still can't prove it. For example "local_var a = -call(); field = a;" may very well write a double to the field, but the -compiler dare not guess a double type if field is a local variable, -due to bytecode variables being strongly typed and later non -interchangeable. To get around this, the entire method would have to -be replaced and a continuation retained to restart from. We believe -that the next steps we should go through are instead: - -1) Implement method specialization based on callsite, as it's quite -frequently the case that numbers are passed around, but currently our -function nodes just have object types visible to the compiler. For -example "var b = 17; func(a,b,17)" is an example where two parameters -can be specialized, but the main version of func might also be called -from another callsite with func(x,y,"string"). - -2) This requires lazy jitting as the functions have to be specialized -per callsite. - -Even though "function square(x) { return x*x }" might look like a -trivial function that can always only take doubles, this is not -true. Someone might have overridden the valueOf for x so that the -toNumber coercion has side effects. To fulfil JavaScript semantics, -the coercion has to run twice for both terms of the multiplication -even if they are the same object. This means that call site -specialization is necessary, not parameter specialization on the form -"function square(x) { var xd = (double)x; return xd*xd; }", as one -might first think. - -Generating a method specialization for any variant of a function that -we can determine by types at compile time is a combinatorial explosion -of byte code (try it e.g. on all the variants of am3 in the Octane -benchmark crypto.js). Thus, this needs to be lazy - -3) Optimistic callsite writes, something on the form - -x = y; //x is a field known to be a primitive. y is only an object as -far as we can tell - -turns into - -try { - x = (int)y; -} catch (X is not an integer field right now | ClassCastException e) { - x = y; -} - -Mini POC shows that this is the key to a lot of dual field performance -in seemingly trivial micros where one unknown object, in reality -actually a primitive, foils it for us. Very common pattern. Once we -are "all primitives", dual fields runs a lot faster than Object fields -only. - -We still have to deal with objects vs primitives for local bytecode -slots, possibly through code copying and versioning. - -The Future: - -We expect the usefulness of dual fields to increase significantly -after the optimistic type system described in the section on -integer arithmetic above is implemented. - SYSTEM PROPERTY: -Dnashorn.compiler.symbol.trace=[<x>[,*]], -Dnashorn.compiler.symbol.stacktrace=[<x>[,*]] @@ -628,6 +141,9 @@ "identical" - this method compares two script objects for reference equality. It is a == Java comparison +"equals" - Returns true if two objects are either referentially +identical or equal as defined by java.lang.Object.equals. + "dumpCounters" - will dump the debug counters' current values to stdout. @@ -648,66 +164,66 @@ when a callsite has to be relinked, due to a previous assumption of object layout being invalidated. +"getContext" - return the current Nashorn context. -SYSTEM PROPERTY: -Dnashorn.methodhandles.debug, --Dnashorn.methodhandles.debug=create +"equalWithoutType" - Returns true if if the two objects are both +property maps, and they have identical properties in the same order, +but allows the properties to differ in their types. + +"diffPropertyMaps" Returns a diagnostic string representing the difference +of two property maps. + +"getClass" - Returns the Java class of an object, or undefined if null. + +"toJavaString" - Returns the Java toString representation of an object. + +"toIdentString" - Returns a string representation of an object consisting +of its java class name and hash code. + +"getListenerCount" - Return the number of property listeners for a +script object. -If this property is enabled, each MethodHandle related call that uses -the java.lang.invoke package gets its MethodHandle intercepted and an -instrumentation printout of arguments and return value appended to -it. This shows exactly which method handles are executed and from -where. (Also MethodTypes and SwitchPoints). This can be augmented with -more information, for example, instance count, by subclassing or -further extending the TraceMethodHandleFactory implementation in -MethodHandleFactory.java. +"getEventQueueCapacity" - Get the capacity of the event queue. + +"setEventQueueCapacity" - Set the event queue capacity. + +"addRuntimeEvent" - Add a runtime event to the runtime event queue. +The queue has a fixed size (see -Dnashorn.runtime.event.queue.size) +and the oldest entry will be thrown out of the queue is about to overflow. -If the property is specialized with "=create" as its option, -instrumentation will be shown for method handles upon creation time -rather than at runtime usage. +"expandEventQueueCapacity" - Expands the event queue capacity, +or truncates if capacity is lower than current capacity. Then only +the newest entries are kept. + +"clearRuntimeEvents" - Clear the runtime event queue. + +"removeRuntimeEvent" - Remove a specific runtime event from the event queue. + +"getRuntimeEvents" - Return all runtime events in the queue as an array. + +"getLastRuntimeEvent" - Return the last runtime event in the queue. SYSTEM PROPERTY: -Dnashorn.methodhandles.debug.stacktrace -This does the same as nashorn.methodhandles.debug, but when enabled -also dumps the stack trace for every instrumented method handle -operation. Warning: This is enormously verbose, but provides a pretty +This enhances methodhandles logging (see below) to also dump the +stack trace for every instrumented method handle operation. +Warning: This is enormously verbose, but provides a pretty decent "grep:able" picture of where the calls are coming from. -See the description of the codegen logger below for a more verbose -description of this option - -SYSTEM PROPERTY: -Dnashorn.scriptfunction.specialization.disable +SYSTEM PROPERTY: -Dnashorn.cce + +Setting this system property causes the Nashorn linker to rely on +ClassCastExceptions for triggering a callsite relink. If not set, the linker +will add an explicit instanceof guard. -There are several "fast path" implementations of constructors and -functions in the NativeObject classes that, in their original form, -take a variable amount of arguments. Said functions are also declared -to take Object parameters in their original form, as this is what the -JavaScript specification mandates. -However, we often know quite a lot more at a callsite of one of these -functions. For example, Math.min is called with a fixed number (2) of -integer arguments. The overhead of boxing these ints to Objects and -folding them into an Object array for the generic varargs Math.min -function is an order of magnitude slower than calling a specialized -implementation of Math.min that takes two integers. Specialized -functions and constructors are identified by the tag -@SpecializedFunction and @SpecializedConstructor in the Nashorn -code. The linker will link in the most appropriate (narrowest types, -right number of types and least number of arguments) specialization if -specializations are available. + +SYSTEM PROPERTY: -Dnashorn.spill.threshold=<x> -Every ScriptFunction may carry specializations that the linker can -choose from. This framework will likely be extended for user defined -functions. The compiler can often infer enough parameter type info -from callsites for in order to generate simpler versions with less -generic Object types. This feature depends on future lazy jitting, as -there tend to be many calls to user defined functions, some where the -callsite can be specialized, some where we mostly see object -parameters even at the callsite. - -If this system property is set to true, the linker will not attempt to -use any specialized function or constructor for native objects, but -just call the generic one. +This property sets the number of fields in an object from which to use +generic array based spill storage instead of Java fields. The default value +is 256. SYSTEM PROPERTY: -Dnashorn.tcs.miss.samplePercent=<x> @@ -719,8 +235,47 @@ should be logged. Typically this is set to 1 or 5 (percent). 1% is the default value. +SYSTEM PROPERTY: -Dnashorn.persistent.code.cache -SYSTEM_PROPERTY: -Dnashorn.profilefile=<filename> +This property can be used to set the directory where Nashorn stores +serialized script classes generated with the -pcc/--persistent-code-cache +option. The default directory name is "nashorn_code_cache". + + +SYSTEM PROPERTY: -Dnashorn.typeInfo.maxFiles + +Maximum number of files to store in the type info cache. The type info cache +is used to cache type data of JavaScript functions when running with +optimistic types (-ot/--optimistic-types). There is one file per JavaScript +function in the cache. + +The default value is 0 which means the feature is disabled. Setting this +to something like 20000 is probably good enough for most applications and +will usually cap the cache directory to about 80MB presuming a 4kB +filesystem allocation unit. Set this to "unlimited" to run without limit. + +If the value is not 0 or "unlimited", Nashorn will spawn a cleanup thread +that makes sure the number of files in the cache does not exceed the given +value by deleting the least recently modified files. + + +SYSTEM PROPERTY: -Dnashorn.typeInfo.cacheDir + +This property can be used to set the directory where Nashorn stores the +type info cache when -Dnashorn.typeInfo.maxFiles is set to a nonzero +value. The default location is platform specific. On Windows, it is +"${java.io.tmpdir}\com.oracle.java.NashornTypeInfo". On Linux and +Solaris it is "~/.cache/com.oracle.java.NashornTypeInfo". On Mac OS X, +it is "~/Library/Caches/com.oracle.java.NashornTypeInfo". + + +SYSTEM PROPERTY: -Dnashorn.typeInfo.cleanupDelaySeconds=<value> + +This sets the delay between cleanups of the typeInfo cache, in seconds. +The default delay is 20 seconds. + + +SYSTEM PROPERTY: -Dnashorn.profilefile=<filename> When running with the profile callsite options (-pcs), Nashorn will dump profiling data for all callsites to stderr as a shutdown hook. To @@ -736,6 +291,11 @@ an implementation based on Joni, the regular expression engine used by the JRuby project. The default value for this flag is "joni" +SYSTEM PROPERTY: -Dnashorn.runtime.event.queue.size=<value> + +Nashorn provides a fixed sized runtime event queue for debugging purposes. +See -Dnashorn.debug for methods to access the event queue. +The default value is 1024. =============== 2. The loggers. @@ -767,7 +327,9 @@ For example: --log=codegen,fields:finest is equivalent to --log=codegen:info --log=fields:finest -The subsystems that currently support logging are: +The following is an incomplete list of subsystems that currently +support logging. Look for classes implementing +jdk.nashorn.internal.runtime.logging.Loggable for more loggers. * compiler @@ -780,6 +342,14 @@ use.s +* recompile + +This logger shows information about recompilation of scripts and +functions at runtime. Recompilation may happen because a function +was called with different parameter types, or because an optimistic +assumption failed while executing a function with -ot/--optimistic-types. + + * codegen The code generator is the emitter stage of the code pipeline, and @@ -836,25 +406,13 @@ Lower is also responsible for determining control flow information like end points. - -* attr +* symbols -The lowering annotates a FunctionNode with symbols for each identifier -and transforms high level constructs into lower level ones, that the -CodeGenerator consumes. +The symbols logger tracks the assignment os symbols to identifiers. -Lower logging typically outputs things like post pass actions, -insertions of casts because symbol types have been changed and type -specialization information. Currently very little info is generated by -this logger. This will probably change. - +* scopedepths -* finalize - -This --log=finalize log option outputs information for type finalization, -the third tier of the compiler. This means things like placement of -specialized scope nodes or explicit conversions. - +This logs the calculation of scope depths for non-local symbols. * fields @@ -896,6 +454,21 @@ [time] [time] Total runtime: 11994 ms (Non-runtime: 11027 ms [91%]) +* methodhandles + +If this logger is enabled, each MethodHandle related call that uses +the java.lang.invoke package gets its MethodHandle intercepted and an +instrumentation printout of arguments and return value appended to +it. This shows exactly which method handles are executed and from +where. (Also MethodTypes and SwitchPoints). + +* classcache + +This logger shows information about reusing code classes using the +in-memory class cache. Nashorn will try to avoid compilation of +scripts by using existing classes. This can significantly improve +performance when repeatedly evaluating the same script. + ======================= 3. Undocumented options =======================
--- a/make/build-nasgen.xml Thu Oct 23 11:19:29 2014 -0700 +++ b/make/build-nasgen.xml Thu Oct 23 13:45:22 2014 -0700 @@ -25,7 +25,7 @@ <description>Builds and runs nasgen.</description> <import file="build.xml"/> - <target name="build-nasgen" depends="compile-asm"> + <target name="build-nasgen" depends="prepare"> <ant inheritAll="false" dir="${basedir}/buildtools/nasgen" antfile="build.xml" target="jar"/> </target>
--- a/make/build.xml Thu Oct 23 11:19:29 2014 -0700 +++ b/make/build.xml Thu Oct 23 13:45:22 2014 -0700 @@ -49,8 +49,6 @@ <condition property="git.executable" value="/usr/local/bin/git" else="git"> <available file="/usr/local/bin/git"/> </condition> - <!-- check if JDK already has ASM classes --> - <available property="asm.available" classname="jdk.internal.org.objectweb.asm.Type"/> <!-- check if testng.jar is avaiable --> <available property="testng.available" file="${file.reference.testng.jar}"/> <!-- check if Jemmy ang testng.jar are avaiable --> @@ -78,8 +76,31 @@ <istrue value="${jfr}"/> </condition> </target> + + <!-- check minimum ant version required to be 1.8.4 --> + <target name="check-ant-version"> + <property name="ant.version.required" value="1.8.4"/> + <antversion property="ant.current.version" /> + <fail message="The current ant version, ${ant.current.version}, is too old. Please use 1.8.4 or above."> + <condition> + <not> + <antversion atleast="${ant.version.required}"/> + </not> + </condition> + </fail> + </target> + + <target name="check-java-version"> + <!-- look for a Class that is available only in jdk1.8 or above --> + <!-- core/exposed API class is better than an implementation class --> + <available property="jdk1.8+" classname="java.util.stream.Stream"/> + + <!-- need jdk1.8 or above --> + <fail message="Unsupported Java version: ${ant.java.version}. Please use Java version 1.8 or greater." unless="jdk1.8+"> + </fail> + </target> - <target name="init" depends="init-conditions, init-cc"> + <target name="init" depends="check-ant-version, check-java-version, init-conditions, init-cc"> <!-- extends jvm args --> <property name="run.test.jvmargs" value="${run.test.jvmargs.main} ${run.test.cc.jvmargs} ${jfr.options}"/> <property name="run.test.jvmargs.octane" value="${run.test.jvmargs.octane.main} ${run.test.cc.jvmargs} ${jfr.options}"/> @@ -107,19 +128,7 @@ <delete dir="${dist.dir}"/> </target> - <!-- do it only if ASM is not available --> - <target name="compile-asm" depends="prepare" unless="asm.available"> - <javac srcdir="${jdk.asm.src.dir}" - destdir="${build.classes.dir}" - excludes="**/optimizer/* **/xml/* **/attrs/*" - source="${javac.source}" - target="${javac.target}" - debug="${javac.debug}" - encoding="${javac.encoding}" - includeantruntime="false"/> - </target> - - <target name="compile" depends="compile-asm" description="Compiles nashorn"> + <target name="compile" depends="prepare" description="Compiles nashorn"> <javac srcdir="${src.dir}" destdir="${build.classes.dir}" classpath="${javac.classpath}"
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/ApplySpecialization.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/ApplySpecialization.java Thu Oct 23 13:45:22 2014 -0700 @@ -27,6 +27,7 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS_VAR; import static jdk.nashorn.internal.codegen.CompilerConstants.EXPLODED_ARGUMENT_PREFIX; + import java.lang.invoke.MethodType; import java.util.ArrayDeque; import java.util.ArrayList; @@ -38,6 +39,7 @@ import jdk.nashorn.internal.ir.CallNode; import jdk.nashorn.internal.ir.Expression; import jdk.nashorn.internal.ir.FunctionNode; +import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.IdentNode; import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.Node; @@ -321,7 +323,7 @@ explodedArguments.pop(); - return newFunctionNode; + return newFunctionNode.setState(lc, CompilationState.BUILTINS_TRANSFORMED); } private static boolean isApply(final CallNode callNode) {
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/AssignSymbols.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/AssignSymbols.java Thu Oct 23 13:45:22 2014 -0700 @@ -76,7 +76,6 @@ import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.RuntimeNode; import jdk.nashorn.internal.ir.RuntimeNode.Request; -import jdk.nashorn.internal.ir.SplitNode; import jdk.nashorn.internal.ir.Statement; import jdk.nashorn.internal.ir.SwitchNode; import jdk.nashorn.internal.ir.Symbol; @@ -135,9 +134,6 @@ 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()); @@ -1014,7 +1010,7 @@ boolean previousWasBlock = false; for (final Iterator<LexicalContextNode> it = lc.getAllNodes(); it.hasNext();) { final LexicalContextNode node = it.next(); - if (node instanceof FunctionNode || node instanceof SplitNode || isSplitArray(node)) { + if (node instanceof FunctionNode || isSplitArray(node)) { // We reached the function boundary or a splitting boundary without seeing a definition for the symbol. // It needs to be in scope. return true;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/AstSerializer.java Thu Oct 23 13:45:22 2014 -0700 @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.nashorn.internal.codegen; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.util.Collections; +import java.util.zip.Deflater; +import java.util.zip.DeflaterOutputStream; +import jdk.nashorn.internal.ir.Block; +import jdk.nashorn.internal.ir.FunctionNode; +import jdk.nashorn.internal.ir.LexicalContext; +import jdk.nashorn.internal.ir.Node; +import jdk.nashorn.internal.ir.Statement; +import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.runtime.options.Options; + +/** + * This static utility class performs serialization of FunctionNode ASTs to a byte array. + * The format is a standard Java serialization stream, deflated. + */ +final class AstSerializer { + // Experimentally, we concluded that compression level 4 gives a good tradeoff between serialization speed + // and size. + private static final int COMPRESSION_LEVEL = Options.getIntProperty("nashorn.serialize.compression", 4); + static byte[] serialize(final FunctionNode fn) { + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + try (final ObjectOutputStream oout = new ObjectOutputStream(new DeflaterOutputStream(out, + new Deflater(COMPRESSION_LEVEL)))) { + oout.writeObject(removeInnerFunctionBodies(fn)); + } catch (final IOException e) { + throw new AssertionError("Unexpected exception serializing function", e); + } + return out.toByteArray(); + } + + private static FunctionNode removeInnerFunctionBodies(final FunctionNode fn) { + return (FunctionNode)fn.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { + @Override + public Node leaveBlock(final Block block) { + if (lc.isFunctionBody() && lc.getFunction(block) != lc.getOutermostFunction()) { + return block.setStatements(lc, Collections.<Statement>emptyList()); + } + return super.leaveBlock(block); + } + }); + } +}
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/ClassEmitter.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/ClassEmitter.java Thu Oct 23 13:45:22 2014 -0700 @@ -51,6 +51,7 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor; 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.security.AccessController; @@ -64,7 +65,6 @@ 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; @@ -476,12 +476,6 @@ methodsStarted.remove(method); } - SplitMethodEmitter method(final SplitNode splitNode, final String methodName, final Class<?> rtype, final Class<?>... ptypes) { - methodCount++; - methodNames.add(methodName); - return new SplitMethodEmitter(this, methodVisitor(EnumSet.of(Flag.PUBLIC, Flag.STATIC), methodName, rtype, ptypes), splitNode); - } - /** * Add a new method to the class - defaults to public method *
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java Thu Oct 23 13:45:22 2014 -0700 @@ -34,9 +34,7 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.GET_STRING; import static jdk.nashorn.internal.codegen.CompilerConstants.QUICK_PREFIX; import static jdk.nashorn.internal.codegen.CompilerConstants.REGEX_PREFIX; -import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN; import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE; -import static jdk.nashorn.internal.codegen.CompilerConstants.SPLIT_ARRAY_ARG; import static jdk.nashorn.internal.codegen.CompilerConstants.SPLIT_PREFIX; import static jdk.nashorn.internal.codegen.CompilerConstants.THIS; import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS; @@ -99,10 +97,10 @@ import jdk.nashorn.internal.ir.ForNode; import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.FunctionNode.CompilationState; +import jdk.nashorn.internal.ir.GetSplitState; import jdk.nashorn.internal.ir.IdentNode; import jdk.nashorn.internal.ir.IfNode; import jdk.nashorn.internal.ir.IndexNode; -import jdk.nashorn.internal.ir.JoinPredecessor; import jdk.nashorn.internal.ir.JoinPredecessorExpression; import jdk.nashorn.internal.ir.JumpStatement; import jdk.nashorn.internal.ir.LabelNode; @@ -121,7 +119,8 @@ import jdk.nashorn.internal.ir.ReturnNode; import jdk.nashorn.internal.ir.RuntimeNode; import jdk.nashorn.internal.ir.RuntimeNode.Request; -import jdk.nashorn.internal.ir.SplitNode; +import jdk.nashorn.internal.ir.SetSplitState; +import jdk.nashorn.internal.ir.SplitReturn; import jdk.nashorn.internal.ir.Statement; import jdk.nashorn.internal.ir.SwitchNode; import jdk.nashorn.internal.ir.Symbol; @@ -493,8 +492,7 @@ //walk up the chain from starting block and when we bump into the current function boundary, add the external //information. final FunctionNode fn = lc.getCurrentFunction(); - final int fnId = fn.getId(); - final int externalDepth = compiler.getScriptFunctionData(fnId).getExternalSymbolDepth(symbol.getName()); + final int externalDepth = compiler.getScriptFunctionData(fn.getId()).getExternalSymbolDepth(symbol.getName()); //count the number of scopes from this place to the start of the function @@ -554,10 +552,10 @@ } MethodEmitter loadBinaryOperands(final BinaryNode binaryNode) { - return loadBinaryOperands(binaryNode.lhs(), binaryNode.rhs(), TypeBounds.UNBOUNDED.notWiderThan(binaryNode.getWidestOperandType()), false); + return loadBinaryOperands(binaryNode.lhs(), binaryNode.rhs(), TypeBounds.UNBOUNDED.notWiderThan(binaryNode.getWidestOperandType()), false, false); } - private MethodEmitter loadBinaryOperands(final Expression lhs, final Expression rhs, final TypeBounds explicitOperandBounds, final boolean baseAlreadyOnStack) { + private MethodEmitter loadBinaryOperands(final Expression lhs, final Expression rhs, final TypeBounds explicitOperandBounds, final boolean baseAlreadyOnStack, final boolean forceConversionSeparation) { // ECMAScript 5.1 specification (sections 11.5-11.11 and 11.13) prescribes that when evaluating a binary // expression "LEFT op RIGHT", the order of operations must be: LOAD LEFT, LOAD RIGHT, CONVERT LEFT, CONVERT // RIGHT, EXECUTE OP. Unfortunately, doing it in this order defeats potential optimizations that arise when we @@ -574,15 +572,34 @@ final Type narrowestOperandType = Type.narrowest(Type.widest(lhs.getType(), rhs.getType()), explicitOperandBounds.widest); final TypeBounds operandBounds = explicitOperandBounds.notNarrowerThan(narrowestOperandType); if (noToPrimitiveConversion(lhs.getType(), explicitOperandBounds.widest) || rhs.isLocal()) { - // Can reorder. Combine load and convert into single operations. - loadExpression(lhs, operandBounds, baseAlreadyOnStack); - loadExpression(rhs, operandBounds, false); + // Can reorder. We might still need to separate conversion, but at least we can do it with reordering + if (forceConversionSeparation) { + // Can reorder, but can't move conversion into the operand as the operation depends on operands + // exact types for its overflow guarantees. E.g. with {L}{%I}expr1 {L}* {L}{%I}expr2 we are not allowed + // to merge {L}{%I} into {%L}, as that can cause subsequent overflows; test for JDK-8058610 contains + // concrete cases where this could happen. + final TypeBounds safeConvertBounds = TypeBounds.UNBOUNDED.notNarrowerThan(narrowestOperandType); + loadExpression(lhs, safeConvertBounds, baseAlreadyOnStack); + method.convert(operandBounds.within(method.peekType())); + loadExpression(rhs, safeConvertBounds, false); + method.convert(operandBounds.within(method.peekType())); + } else { + // Can reorder and move conversion into the operand. Combine load and convert into single operations. + loadExpression(lhs, operandBounds, baseAlreadyOnStack); + loadExpression(rhs, operandBounds, false); + } } else { // Can't reorder. Load and convert separately. final TypeBounds safeConvertBounds = TypeBounds.UNBOUNDED.notNarrowerThan(narrowestOperandType); loadExpression(lhs, safeConvertBounds, baseAlreadyOnStack); + final Type lhsType = method.peekType(); loadExpression(rhs, safeConvertBounds, false); - method.swap().convert(operandBounds.within(method.peekType())).swap().convert(operandBounds.within(method.peekType())); + final Type convertedLhsType = operandBounds.within(method.peekType()); + if (convertedLhsType != lhsType) { + // Do it conditionally, so that if conversion is a no-op we don't introduce a SWAP, SWAP. + method.swap().convert(convertedLhsType).swap(); + } + method.convert(operandBounds.within(method.peekType())); } assert Type.generic(method.peekType()) == operandBounds.narrowest; assert Type.generic(method.peekType(1)) == operandBounds.narrowest; @@ -633,19 +650,11 @@ } TypeBounds booleanToInt() { - return maybeNew(booleanToInt(narrowest), booleanToInt(widest)); + return maybeNew(CodeGenerator.booleanToInt(narrowest), CodeGenerator.booleanToInt(widest)); } TypeBounds objectToNumber() { - return maybeNew(objectToNumber(narrowest), objectToNumber(widest)); - } - - private static Type booleanToInt(final Type t) { - return t == Type.BOOLEAN ? Type.INT : t; - } - - private static Type objectToNumber(final Type t) { - return t.isObject() ? Type.NUMBER : t; + return maybeNew(CodeGenerator.objectToNumber(narrowest), CodeGenerator.objectToNumber(widest)); } Type within(final Type type) { @@ -664,6 +673,14 @@ } } + private static Type booleanToInt(final Type t) { + return t == Type.BOOLEAN ? Type.INT : t; + } + + private static Type objectToNumber(final Type t) { + return t.isObject() ? Type.NUMBER : t; + } + MethodEmitter loadExpressionAsType(final Expression expr, final Type type) { if(type == Type.BOOLEAN) { return loadExpressionAsBoolean(expr); @@ -1048,6 +1065,13 @@ } @Override + public boolean enterGetSplitState(final GetSplitState getSplitState) { + method.loadScope(); + method.invoke(Scope.GET_SPLIT_STATE); + return false; + } + + @Override public boolean enterDefault(final Node otherNode) { // Must have handled all expressions that can legally be encountered. throw new AssertionError(otherNode.getClass().getName()); @@ -1219,7 +1243,7 @@ popScopesUntil(target); final Label targetLabel = jump.getTargetLabel(target); targetLabel.markAsBreakTarget(); - method.splitAwareGoto(lc, targetLabel, target); + method._goto(targetLabel); return false; } @@ -2029,10 +2053,10 @@ } private void lineNumber(final int lineNumber) { - if (lineNumber != lastLineNumber) { + if (lineNumber != lastLineNumber && lineNumber != Node.NO_LINE_NUMBER) { method.lineNumber(lineNumber); - } - lastLineNumber = lineNumber; + lastLineNumber = lineNumber; + } } int getLastLineNumber() { @@ -2079,13 +2103,14 @@ method.begin(); defineCommonSplitMethodParameters(); - defineSplitMethodParameter(3, arrayType); - - fixScopeSlot(currentFunction); + defineSplitMethodParameter(CompilerConstants.SPLIT_ARRAY_ARG.slot(), arrayType); + + // NOTE: when this is no longer needed, SplitIntoFunctions will no longer have to add IS_SPLIT + // to synthetic functions, and FunctionNode.needsCallee() will no longer need to test for isSplit(). + final int arraySlot = fixScopeSlot(currentFunction, 3); lc.enterSplitNode(); - final int arraySlot = SPLIT_ARRAY_ARG.slot(); for (int i = arrayUnit.getLo(); i < arrayUnit.getHi(); i++) { method.load(arrayType, arraySlot); storeElement(nodes, elementType, postsets[i]); @@ -2700,73 +2725,6 @@ method.convert(newRuntimeNode.getType()); } - @Override - public boolean enterSplitNode(final SplitNode splitNode) { - if(!method.isReachable()) { - return false; - } - - final CompileUnit splitCompileUnit = splitNode.getCompileUnit(); - - final FunctionNode fn = lc.getCurrentFunction(); - final String className = splitCompileUnit.getUnitClassName(); - final String name = splitNode.getName(); - - final Type returnType = fn.getReturnType(); - - final Class<?> rtype = fn.getReturnType().getTypeClass(); - final boolean needsArguments = fn.needsArguments(); - final Class<?>[] ptypes = needsArguments ? - new Class<?>[] {ScriptFunction.class, Object.class, ScriptObject.class, ScriptObject.class} : - new Class<?>[] {ScriptFunction.class, Object.class, ScriptObject.class}; - - final MethodEmitter caller = method; - unit = lc.pushCompileUnit(splitCompileUnit); - - final Call splitCall = staticCallNoLookup( - className, - name, - methodDescriptor(rtype, ptypes)); - - final MethodEmitter splitEmitter = - splitCompileUnit.getClassEmitter().method( - splitNode, - name, - rtype, - ptypes); - - pushMethodEmitter(splitEmitter); - method.setFunctionNode(fn); - - assert fn.needsCallee() : "split function should require callee"; - caller.loadCompilerConstant(CALLEE); - caller.loadCompilerConstant(THIS); - caller.loadCompilerConstant(SCOPE); - if (needsArguments) { - caller.loadCompilerConstant(ARGUMENTS); - } - caller.invoke(splitCall); - caller.storeCompilerConstant(RETURN, returnType); - - method.begin(); - - defineCommonSplitMethodParameters(); - if(needsArguments) { - defineSplitMethodParameter(3, ARGUMENTS); - } - - // Copy scope to its target slot as first thing because the original slot could be used by return symbol. - fixScopeSlot(fn); - - final int returnSlot = fn.compilerConstant(RETURN).getSlot(returnType); - method.defineBlockLocalVariable(returnSlot, returnSlot + returnType.getSlots()); - method.loadUndefined(returnType); - method.storeCompilerConstant(RETURN, returnType); - - lc.enterSplitNode(); - return true; - } - private void defineCommonSplitMethodParameters() { defineSplitMethodParameter(0, CALLEE); defineSplitMethodParameter(1, THIS); @@ -2782,114 +2740,40 @@ method.onLocalStore(type, slot); } - private void fixScopeSlot(final FunctionNode functionNode) { + private int fixScopeSlot(final FunctionNode functionNode, final int extraSlot) { // TODO hack to move the scope to the expected slot (needed because split methods reuse the same slots as the root method) final int actualScopeSlot = functionNode.compilerConstant(SCOPE).getSlot(SCOPE_TYPE); final int defaultScopeSlot = SCOPE.slot(); + int newExtraSlot = extraSlot; if (actualScopeSlot != defaultScopeSlot) { - method.defineBlockLocalVariable(actualScopeSlot, actualScopeSlot + 1); + if (actualScopeSlot == extraSlot) { + newExtraSlot = extraSlot + 1; + method.defineBlockLocalVariable(newExtraSlot, newExtraSlot + 1); + method.load(Type.OBJECT, extraSlot); + method.storeHidden(Type.OBJECT, newExtraSlot); + } else { + method.defineBlockLocalVariable(actualScopeSlot, actualScopeSlot + 1); + } method.load(SCOPE_TYPE, defaultScopeSlot); method.storeCompilerConstant(SCOPE); } + return newExtraSlot; } @Override - public Node leaveSplitNode(final SplitNode splitNode) { - assert method instanceof SplitMethodEmitter; - lc.exitSplitNode(); - final boolean hasReturn = method.hasReturn(); - final SplitMethodEmitter splitMethod = ((SplitMethodEmitter)method); - final List<Label> targets = splitMethod.getExternalTargets(); - final boolean hasControlFlow = hasReturn || !targets.isEmpty(); - final List<BreakableNode> targetNodes = splitMethod.getExternalTargetNodes(); - final Type returnType = lc.getCurrentFunction().getReturnType(); - - try { - // Wrap up this method. - - if(method.isReachable()) { - if (hasControlFlow) { - method.setSplitState(-1); - } - method.loadCompilerConstant(RETURN, returnType); - method._return(returnType); - } - method.end(); - - lc.releaseSlots(); - - unit = lc.popCompileUnit(splitNode.getCompileUnit()); - popMethodEmitter(); - - } catch (final Throwable t) { - Context.printStackTrace(t); - final VerifyError e = new VerifyError("Code generation bug in \"" + splitNode.getName() + "\": likely stack misaligned: " + t + " " + getCurrentSource().getName()); - e.initCause(t); - throw e; - } - - //no external jump targets or return in switch node - if (!hasControlFlow) { - return splitNode; - } - - // Handle return from split method if there was one. - final MethodEmitter caller = method; - final int targetCount = targets.size(); - - caller.loadScope(); - caller.invoke(Scope.GET_SPLIT_STATE); - - final Label breakLabel = new Label("no_split_state"); - // Split state is -1 for no split state, 0 for return, 1..n+1 for break/continue - - //the common case is that we don't need a switch - if (targetCount == 0) { - assert hasReturn; - caller.ifne(breakLabel); - //has to be zero - caller.label(new Label("split_return")); - caller.loadCompilerConstant(RETURN, returnType); - caller._return(returnType); - caller.label(breakLabel); - } else { - assert !targets.isEmpty(); - - final int low = hasReturn ? 0 : 1; - final int labelCount = targetCount + 1 - low; - final Label[] labels = new Label[labelCount]; - - for (int i = 0; i < labelCount; i++) { - labels[i] = new Label(i == 0 ? "split_return" : "split_" + targets.get(i - 1)); - } - caller.tableswitch(low, targetCount, breakLabel, labels); - for (int i = low; i <= targetCount; i++) { - caller.label(labels[i - low]); - if (i == 0) { - caller.loadCompilerConstant(RETURN, returnType); - caller._return(returnType); - } else { - final BreakableNode targetNode = targetNodes.get(i - 1); - final Label label = targets.get(i - 1); - if (!lc.isExternalTarget(splitNode, targetNode)) { - final JoinPredecessor jumpOrigin = splitNode.getJumpOrigin(label); - if(jumpOrigin != null) { - method.beforeJoinPoint(jumpOrigin); - } - popScopesUntil(targetNode); - } - caller.splitAwareGoto(lc, label, targetNode); - } - } - caller.label(breakLabel); - } - - // If split has a return and caller is itself a split method it needs to propagate the return. - if (hasReturn) { - caller.setHasReturn(); - } - - return splitNode; + public boolean enterSplitReturn(final SplitReturn splitReturn) { + if (method.isReachable()) { + method.loadUndefined(lc.getCurrentFunction().getReturnType())._return(); + } + return false; + } + + @Override + public boolean enterSetSplitState(final SetSplitState setSplitState) { + if (method.isReachable()) { + method.setSplitState(setSplitState.getState()); + } + return false; } @Override @@ -3678,13 +3562,15 @@ void loadStack() { final TypeBounds operandBounds; final boolean isOptimistic = isValid(getProgramPoint()); + boolean forceConversionSeparation = false; if(isOptimistic) { operandBounds = new TypeBounds(binaryNode.getType(), Type.OBJECT); } else { // Non-optimistic, non-FP +. Allow it to overflow. operandBounds = new TypeBounds(binaryNode.getWidestOperandType(), Type.OBJECT); + forceConversionSeparation = binaryNode.getWidestOperationType().narrowerThan(resultBounds.widest); } - loadBinaryOperands(binaryNode.lhs(), binaryNode.rhs(), operandBounds, false); + loadBinaryOperands(binaryNode.lhs(), binaryNode.rhs(), operandBounds, false, forceConversionSeparation); } @Override @@ -3795,12 +3681,21 @@ @Override protected void evaluate() { final Expression lhs = assignNode.lhs(); - final Type widest = assignNode.isTokenType(TokenType.ASSIGN_ADD) ? Type.OBJECT : assignNode.getWidestOperationType(); + final Expression rhs = assignNode.rhs(); + final Type widestOperationType = assignNode.getWidestOperationType(); + final Type widest = assignNode.isTokenType(TokenType.ASSIGN_ADD) ? Type.OBJECT : widestOperationType; final TypeBounds bounds = new TypeBounds(assignNode.getType(), widest); new OptimisticOperation(assignNode, bounds) { @Override void loadStack() { - loadBinaryOperands(lhs, assignNode.rhs(), bounds, true); + final boolean forceConversionSeparation; + if (isValid(getProgramPoint()) || widestOperationType == Type.NUMBER) { + forceConversionSeparation = false; + } else { + final Type operandType = Type.widest(booleanToInt(objectToNumber(lhs.getType())), booleanToInt(objectToNumber(rhs.getType()))); + forceConversionSeparation = operandType.narrowerThan(widestOperationType); + } + loadBinaryOperands(lhs, rhs, bounds, true, forceConversionSeparation); } @Override void consumeStack() { @@ -3823,7 +3718,7 @@ @Override protected void evaluate() { - loadBinaryOperands(assignNode.lhs(), assignNode.rhs(), TypeBounds.UNBOUNDED.notWiderThan(assignNode.getWidestOperandType()), true); + loadBinaryOperands(assignNode.lhs(), assignNode.rhs(), TypeBounds.UNBOUNDED.notWiderThan(assignNode.getWidestOperandType()), true, false); op(); } } @@ -3946,6 +3841,7 @@ @Override void loadStack() { final TypeBounds operandBounds; + boolean forceConversionSeparation = false; if(numericBounds.narrowest == Type.NUMBER) { // Result should be double always. Propagate it into the operands so we don't have lots of I2D // and L2D after operand evaluation. @@ -3963,9 +3859,10 @@ // Non-optimistic, non-FP subtraction or multiplication. Allow them to overflow. operandBounds = new TypeBounds(Type.narrowest(node.getWidestOperandType(), numericBounds.widest), Type.NUMBER); + forceConversionSeparation = node.getWidestOperationType().narrowerThan(numericBounds.widest); } } - loadBinaryOperands(node.lhs(), node.rhs(), operandBounds, false); + loadBinaryOperands(node.lhs(), node.rhs(), operandBounds, false, forceConversionSeparation); } @Override @@ -4379,11 +4276,7 @@ private void newFunctionObject(final FunctionNode functionNode, final boolean addInitializer) { assert lc.peek() == functionNode; - final int fnId = functionNode.getId(); - - final RecompilableScriptFunctionData data = compiler.getScriptFunctionData(fnId); - - assert data != null : functionNode.getName() + " has no data"; + final RecompilableScriptFunctionData data = compiler.getScriptFunctionData(functionNode.getId()); if (functionNode.isProgram() && !compiler.isOnDemandCompilation()) { final CompileUnit fnUnit = functionNode.getCompileUnit();
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CompilationPhase.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CompilationPhase.java Thu Oct 23 13:45:22 2014 -0700 @@ -38,12 +38,11 @@ import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SPLIT; import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SYMBOLS_ASSIGNED; import static jdk.nashorn.internal.runtime.logging.DebugLogger.quote; + import java.io.PrintWriter; -import java.util.ArrayList; import java.util.EnumSet; import java.util.HashMap; import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; @@ -53,10 +52,7 @@ import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LiteralNode; -import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; -import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit; import jdk.nashorn.internal.ir.Node; -import jdk.nashorn.internal.ir.SplitNode; import jdk.nashorn.internal.ir.debug.ASTWriter; import jdk.nashorn.internal.ir.debug.PrintVisitor; import jdk.nashorn.internal.ir.visitor.NodeVisitor; @@ -81,7 +77,7 @@ PARSED)) { @Override FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { - return (FunctionNode)fn.accept(new FoldConstants(compiler)); + return transformFunction(fn, new FoldConstants(compiler)); } @Override @@ -104,7 +100,7 @@ CONSTANT_FOLDED)) { @Override FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { - return (FunctionNode)fn.accept(new Lower(compiler)); + return transformFunction(fn, new Lower(compiler)); } @Override @@ -118,23 +114,6 @@ * optimistic ops a program point so that an UnwarrantedException knows from where * a guess went wrong when creating the continuation to roll back this execution */ - PROGRAM_POINT_PHASE( - EnumSet.of( - INITIALIZED, - PARSED, - CONSTANT_FOLDED, - LOWERED)) { - @Override - FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { - return (FunctionNode)fn.accept(new ProgramPoints()); - } - - @Override - public String toString() { - return "'Program Point Calculation'"; - } - }, - TRANSFORM_BUILTINS_PHASE( EnumSet.of( INITIALIZED, @@ -144,13 +123,7 @@ //we only do this if we have a param type map, otherwise this is not a specialized recompile @Override FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { - final FunctionNode newFunctionNode = (FunctionNode)fn.accept(new ApplySpecialization(compiler)); - return (FunctionNode)newFunctionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { - @Override - public Node leaveFunctionNode(final FunctionNode node) { - return node.setState(lc, BUILTINS_TRANSFORMED); - } - }); + return setStates(transformFunction(fn, new ApplySpecialization(compiler)), BUILTINS_TRANSFORMED); } @Override @@ -177,7 +150,7 @@ FunctionNode newFunctionNode; //ensure elementTypes, postsets and presets exist for splitter and arraynodes - newFunctionNode = (FunctionNode)fn.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { + newFunctionNode = transformFunction(fn, new NodeVisitor<LexicalContext>(new LexicalContext()) { @Override public LiteralNode<?> leaveLiteralNode(final LiteralNode<?> literalNode) { return literalNode.initialize(lc); @@ -185,7 +158,7 @@ }); newFunctionNode = new Splitter(compiler, newFunctionNode, outermostCompileUnit).split(newFunctionNode, true); - + newFunctionNode = transformFunction(newFunctionNode, new SplitIntoFunctions(compiler)); assert newFunctionNode.getCompileUnit() == outermostCompileUnit : "fn=" + fn.getName() + ", fn.compileUnit (" + newFunctionNode.getCompileUnit() + ") != " + outermostCompileUnit; assert newFunctionNode.isStrict() == compiler.isStrict() : "functionNode.isStrict() != compiler.isStrict() for " + quote(newFunctionNode.getName()); @@ -198,6 +171,52 @@ } }, + PROGRAM_POINT_PHASE( + EnumSet.of( + INITIALIZED, + PARSED, + CONSTANT_FOLDED, + LOWERED, + BUILTINS_TRANSFORMED, + SPLIT)) { + @Override + FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { + return transformFunction(fn, new ProgramPoints()); + } + + @Override + public String toString() { + return "'Program Point Calculation'"; + } + }, + + SERIALIZE_SPLIT_PHASE( + EnumSet.of( + INITIALIZED, + PARSED, + CONSTANT_FOLDED, + LOWERED, + BUILTINS_TRANSFORMED, + SPLIT)) { + @Override + FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { + return transformFunction(fn, new NodeVisitor<LexicalContext>(new LexicalContext()) { + @Override + public boolean enterFunctionNode(final FunctionNode functionNode) { + if (functionNode.isSplit()) { + compiler.serializeAst(functionNode); + } + return true; + } + }); + } + + @Override + public String toString() { + return "'Serialize Split Functions'"; + } + }, + SYMBOL_ASSIGNMENT_PHASE( EnumSet.of( INITIALIZED, @@ -208,7 +227,7 @@ SPLIT)) { @Override FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { - return (FunctionNode)fn.accept(new AssignSymbols(compiler)); + return transformFunction(fn, new AssignSymbols(compiler)); } @Override @@ -228,7 +247,7 @@ SYMBOLS_ASSIGNED)) { @Override FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { - return (FunctionNode)fn.accept(new FindScopeDepths(compiler)); + return transformFunction(fn, new FindScopeDepths(compiler)); } @Override @@ -250,7 +269,7 @@ @Override FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { if (compiler.useOptimisticTypes()) { - return (FunctionNode)fn.accept(new OptimisticTypesCalculator(compiler)); + return transformFunction(fn, new OptimisticTypesCalculator(compiler)); } return setStates(fn, OPTIMISTIC_TYPES_ASSIGNED); } @@ -274,8 +293,7 @@ OPTIMISTIC_TYPES_ASSIGNED)) { @Override FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { - final FunctionNode newFunctionNode = (FunctionNode)fn.accept(new LocalVariableTypesCalculator(compiler)); - + final FunctionNode newFunctionNode = transformFunction(fn, new LocalVariableTypesCalculator(compiler)); final ScriptEnvironment senv = compiler.getScriptEnvironment(); final PrintWriter err = senv.getErr(); @@ -330,13 +348,7 @@ for (final CompileUnit oldUnit : compiler.getCompileUnits()) { assert map.get(oldUnit) == null; - final StringBuilder sb = new StringBuilder(compiler.nextCompileUnitName()); - if (phases.isRestOfCompilation()) { - sb.append("$restOf"); - } - //it's ok to not copy the initCount, methodCount and clinitCount here, as codegen is what - //fills those out anyway. Thus no need for a copy constructor - final CompileUnit newUnit = compiler.createCompileUnit(sb.toString(), oldUnit.getWeight()); + final CompileUnit newUnit = createNewCompileUnit(compiler, phases); log.fine("Creating new compile unit ", oldUnit, " => ", newUnit); map.put(oldUnit, newUnit); assert newUnit != null; @@ -350,47 +362,10 @@ //replace old compile units in function nodes, if any are assigned, //for example by running the splitter on this function node in a previous //partial code generation - final FunctionNode newFunctionNode = (FunctionNode)fn.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { - @Override - public Node leaveFunctionNode(final FunctionNode node) { - final CompileUnit oldUnit = node.getCompileUnit(); - assert oldUnit != null : "no compile unit in function node"; - - final CompileUnit newUnit = map.get(oldUnit); - assert newUnit != null : "old unit has no mapping to new unit " + oldUnit; - - log.fine("Replacing compile unit: ", oldUnit, " => ", newUnit, " in ", quote(node.getName())); - return node.setCompileUnit(lc, newUnit).setState(lc, CompilationState.COMPILE_UNITS_REUSED); - } - + final FunctionNode newFunctionNode = transformFunction(fn, new ReplaceCompileUnits() { @Override - public Node leaveSplitNode(final SplitNode node) { - final CompileUnit oldUnit = node.getCompileUnit(); - assert oldUnit != null : "no compile unit in function node"; - - final CompileUnit newUnit = map.get(oldUnit); - assert newUnit != null : "old unit has no mapping to new unit " + oldUnit; - - log.fine("Replacing compile unit: ", oldUnit, " => ", newUnit, " in ", quote(node.getName())); - return node.setCompileUnit(lc, newUnit); - } - - @Override - public Node leaveLiteralNode(final LiteralNode<?> node) { - if (node instanceof ArrayLiteralNode) { - final ArrayLiteralNode aln = (ArrayLiteralNode)node; - if (aln.getUnits() == null) { - return node; - } - final List<ArrayUnit> newArrayUnits = new ArrayList<>(); - for (final ArrayUnit au : aln.getUnits()) { - final CompileUnit newUnit = map.get(au.getCompileUnit()); - assert newUnit != null; - newArrayUnits.add(new ArrayUnit(newUnit, au.getLo(), au.getHi())); - } - return aln.setUnits(lc, newArrayUnits); - } - return node; + CompileUnit getReplacement(CompileUnit original) { + return map.get(original); } @Override @@ -408,7 +383,59 @@ } }, - /** + REINITIALIZE_SERIALIZED( + EnumSet.of( + INITIALIZED, + PARSED, + CONSTANT_FOLDED, + LOWERED, + BUILTINS_TRANSFORMED, + SPLIT)) { + @Override + FunctionNode transform(final Compiler compiler, final CompilationPhases phases, final FunctionNode fn) { + final Set<CompileUnit> unitSet = CompileUnit.createCompileUnitSet(); + final Map<CompileUnit, CompileUnit> unitMap = new HashMap<>(); + + // Ensure that the FunctionNode's compile unit is the first in the list of new units. Install phase + // will use that as the root class. + createCompileUnit(fn.getCompileUnit(), unitSet, unitMap, compiler, phases); + + final FunctionNode newFn = transformFunction(fn, new ReplaceCompileUnits() { + @Override + CompileUnit getReplacement(final CompileUnit oldUnit) { + final CompileUnit existing = unitMap.get(oldUnit); + if (existing != null) { + return existing; + } + return createCompileUnit(oldUnit, unitSet, unitMap, compiler, phases); + } + + @Override + public Node leaveFunctionNode(final FunctionNode fn2) { + return super.leaveFunctionNode( + // restore flags for deserialized nested function nodes + compiler.getScriptFunctionData(fn2.getId()).restoreFlags(lc, fn2)); + }; + }); + compiler.replaceCompileUnits(unitSet); + return newFn; + } + + private CompileUnit createCompileUnit(final CompileUnit oldUnit, final Set<CompileUnit> unitSet, + final Map<CompileUnit, CompileUnit> unitMap, final Compiler compiler, final CompilationPhases phases) { + final CompileUnit newUnit = createNewCompileUnit(compiler, phases); + unitMap.put(oldUnit, newUnit); + unitSet.add(newUnit); + return newUnit; + } + + @Override + public String toString() { + return "'Deserialize'"; + } + }, + + /** * Bytecode generation: * * Generate the byte code class(es) resulting from the compiled FunctionNode @@ -443,7 +470,7 @@ try { // Explicitly set BYTECODE_GENERATED here; it can not be set in case of skipping codegen for :program // in the lazy + optimistic world. See CodeGenerator.skipFunction(). - newFunctionNode = ((FunctionNode)newFunctionNode.accept(codegen)).setState(null, BYTECODE_GENERATED); + newFunctionNode = transformFunction(newFunctionNode, codegen).setState(null, BYTECODE_GENERATED); codegen.generateScopeCalls(); } catch (final VerifyError e) { if (senv._verify_code || senv._print_code) { @@ -615,7 +642,7 @@ if (!AssertsEnabled.assertsEnabled()) { return functionNode; } - return (FunctionNode)functionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { + return transformFunction(functionNode, new NodeVisitor<LexicalContext>(new LexicalContext()) { @Override public Node leaveFunctionNode(final FunctionNode fn) { return fn.setState(lc, state); @@ -701,4 +728,17 @@ return end(compiler, transform(compiler, phases, begin(compiler, functionNode))); } + private static FunctionNode transformFunction(final FunctionNode fn, final NodeVisitor<?> visitor) { + return (FunctionNode) fn.accept(visitor); + } + + private static CompileUnit createNewCompileUnit(final Compiler compiler, final CompilationPhases phases) { + final StringBuilder sb = new StringBuilder(compiler.nextCompileUnitName()); + if (phases.isRestOfCompilation()) { + sb.append("$restOf"); + } + //it's ok to not copy the initCount, methodCount and clinitCount here, as codegen is what + //fills those out anyway. Thus no need for a copy constructor + return compiler.createCompileUnit(sb.toString(), 0); + } }
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CompileUnit.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CompileUnit.java Thu Oct 23 13:45:22 2014 -0700 @@ -25,24 +25,31 @@ package jdk.nashorn.internal.codegen; +import java.io.Serializable; import java.util.Set; import java.util.TreeSet; +import jdk.nashorn.internal.ir.CompileUnitHolder; /** - * Used to track split class compilation. - */ -public final class CompileUnit implements Comparable<CompileUnit> { + * Used to track split class compilation. Note that instances of the class are serializable, but all fields are + * transient, making the serialized version of the class only useful for tracking the referential topology of other + * AST nodes referencing the same or different compile units. We do want to preserve this topology though as + * {@link CompileUnitHolder}s in a deserialized AST will undergo reinitialization. + */ +public final class CompileUnit implements Comparable<CompileUnit>, Serializable { + private static final long serialVersionUID = 1L; + /** Current class name */ - private final String className; + private transient final String className; /** Current class generator */ - private ClassEmitter classEmitter; + private transient ClassEmitter classEmitter; - private long weight; + private transient long weight; - private Class<?> clazz; + private transient Class<?> clazz; - private boolean isUsed; + private transient boolean isUsed; private static int emittedUnitCount; @@ -122,14 +129,6 @@ } /** - * Get the current weight of the compile unit. - * @return the unit's weight - */ - long getWeight() { - return weight; - } - - /** * Check if this compile unit can hold {@code weight} more units of weight * @param w weight to check if can be added * @return true if weight fits in this compile unit @@ -155,7 +154,7 @@ } private static String shortName(final String name) { - return name.lastIndexOf('/') == -1 ? name : name.substring(name.lastIndexOf('/') + 1); + return name == null ? null : name.lastIndexOf('/') == -1 ? name : name.substring(name.lastIndexOf('/') + 1); } @Override
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/Compiler.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/Compiler.java Thu Oct 23 13:45:22 2014 -0700 @@ -32,15 +32,16 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.THIS; import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS; import static jdk.nashorn.internal.runtime.logging.DebugLogger.quote; + import java.io.File; import java.lang.invoke.MethodType; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; @@ -159,75 +160,142 @@ */ private static final int COMPILE_UNIT_NAME_BUFFER_SIZE = 32; + private final Map<Integer, byte[]> serializedAsts = new HashMap<>(); + /** * Compilation phases that a compilation goes through */ public static class CompilationPhases implements Iterable<CompilationPhase> { - /** Singleton that describes a standard eager compilation - this includes code installation */ - public final static CompilationPhases COMPILE_ALL = new CompilationPhases( - "Compile all", - new CompilationPhase[] { - CompilationPhase.CONSTANT_FOLDING_PHASE, - CompilationPhase.LOWERING_PHASE, - CompilationPhase.PROGRAM_POINT_PHASE, - CompilationPhase.TRANSFORM_BUILTINS_PHASE, - CompilationPhase.SPLITTING_PHASE, - CompilationPhase.SYMBOL_ASSIGNMENT_PHASE, - CompilationPhase.SCOPE_DEPTH_COMPUTATION_PHASE, - CompilationPhase.OPTIMISTIC_TYPE_ASSIGNMENT_PHASE, - CompilationPhase.LOCAL_VARIABLE_TYPE_CALCULATION_PHASE, - CompilationPhase.BYTECODE_GENERATION_PHASE, - CompilationPhase.INSTALL_PHASE - }); + /** + * Singleton that describes compilation up to the phase where a function can be serialized. + */ + private final static CompilationPhases COMPILE_UPTO_SERIALIZABLE = new CompilationPhases( + "Common initial phases", + CompilationPhase.CONSTANT_FOLDING_PHASE, + CompilationPhase.LOWERING_PHASE, + CompilationPhase.TRANSFORM_BUILTINS_PHASE, + CompilationPhase.SPLITTING_PHASE, + CompilationPhase.PROGRAM_POINT_PHASE, + CompilationPhase.SERIALIZE_SPLIT_PHASE + ); - /** Compile all for a rest of method */ - public final static CompilationPhases COMPILE_ALL_RESTOF = - COMPILE_ALL.setDescription("Compile all, rest of").addAfter(CompilationPhase.LOCAL_VARIABLE_TYPE_CALCULATION_PHASE, CompilationPhase.REUSE_COMPILE_UNITS_PHASE); + private final static CompilationPhases COMPILE_SERIALIZABLE_UPTO_BYTECODE = new CompilationPhases( + "After common phases, before bytecode generator", + CompilationPhase.SYMBOL_ASSIGNMENT_PHASE, + CompilationPhase.SCOPE_DEPTH_COMPUTATION_PHASE, + CompilationPhase.OPTIMISTIC_TYPE_ASSIGNMENT_PHASE, + CompilationPhase.LOCAL_VARIABLE_TYPE_CALCULATION_PHASE + ); - /** Singleton that describes a standard eager compilation, but no installation, for example used by --compile-only */ - public final static CompilationPhases COMPILE_ALL_NO_INSTALL = - COMPILE_ALL. - removeLast(). - setDescription("Compile without install"); - - /** Singleton that describes compilation up to the CodeGenerator, but not actually generating code */ - public final static CompilationPhases COMPILE_UPTO_BYTECODE = - COMPILE_ALL. - removeLast(). - removeLast(). - setDescription("Compile upto bytecode"); + /** + * Singleton that describes additional steps to be taken after deserializing, all the way up to (but not + * including) generating and installing code. + */ + public final static CompilationPhases RECOMPILE_SERIALIZED_UPTO_BYTECODE = new CompilationPhases( + "Recompile serialized function up to bytecode", + CompilationPhase.REINITIALIZE_SERIALIZED, + COMPILE_SERIALIZABLE_UPTO_BYTECODE + ); /** * Singleton that describes back end of method generation, given that we have generated the normal * method up to CodeGenerator as in {@link CompilationPhases#COMPILE_UPTO_BYTECODE} */ - public final static CompilationPhases COMPILE_FROM_BYTECODE = new CompilationPhases( + public final static CompilationPhases GENERATE_BYTECODE_AND_INSTALL = new CompilationPhases( "Generate bytecode and install", - new CompilationPhase[] { - CompilationPhase.BYTECODE_GENERATION_PHASE, - CompilationPhase.INSTALL_PHASE - }); + CompilationPhase.BYTECODE_GENERATION_PHASE, + CompilationPhase.INSTALL_PHASE + ); + + /** Singleton that describes compilation up to the CodeGenerator, but not actually generating code */ + public final static CompilationPhases COMPILE_UPTO_BYTECODE = new CompilationPhases( + "Compile upto bytecode", + COMPILE_UPTO_SERIALIZABLE, + COMPILE_SERIALIZABLE_UPTO_BYTECODE); + + /** Singleton that describes a standard eager compilation, but no installation, for example used by --compile-only */ + public final static CompilationPhases COMPILE_ALL_NO_INSTALL = new CompilationPhases( + "Compile without install", + COMPILE_UPTO_BYTECODE, + CompilationPhase.BYTECODE_GENERATION_PHASE); + + /** Singleton that describes a standard eager compilation - this includes code installation */ + public final static CompilationPhases COMPILE_ALL = new CompilationPhases( + "Full eager compilation", + COMPILE_UPTO_BYTECODE, + GENERATE_BYTECODE_AND_INSTALL); + + /** Singleton that describes a full compilation - this includes code installation - from serialized state*/ + public final static CompilationPhases COMPILE_ALL_SERIALIZED = new CompilationPhases( + "Eager compilation from serializaed state", + RECOMPILE_SERIALIZED_UPTO_BYTECODE, + GENERATE_BYTECODE_AND_INSTALL); /** * Singleton that describes restOf method generation, given that we have generated the normal * method up to CodeGenerator as in {@link CompilationPhases#COMPILE_UPTO_BYTECODE} */ - public final static CompilationPhases COMPILE_FROM_BYTECODE_RESTOF = - COMPILE_FROM_BYTECODE. - addFirst(CompilationPhase.REUSE_COMPILE_UNITS_PHASE). - setDescription("Generate bytecode and install - RestOf method"); + public final static CompilationPhases GENERATE_BYTECODE_AND_INSTALL_RESTOF = new CompilationPhases( + "Generate bytecode and install - RestOf method", + CompilationPhase.REUSE_COMPILE_UNITS_PHASE, + GENERATE_BYTECODE_AND_INSTALL); + + /** Compile all for a rest of method */ + public final static CompilationPhases COMPILE_ALL_RESTOF = new CompilationPhases( + "Compile all, rest of", + COMPILE_UPTO_BYTECODE, + GENERATE_BYTECODE_AND_INSTALL_RESTOF); + + /** Compile from serialized for a rest of method */ + public final static CompilationPhases COMPILE_SERIALIZED_RESTOF = new CompilationPhases( + "Compile serialized, rest of", + RECOMPILE_SERIALIZED_UPTO_BYTECODE, + GENERATE_BYTECODE_AND_INSTALL_RESTOF); private final List<CompilationPhase> phases; private final String desc; private CompilationPhases(final String desc, final CompilationPhase... phases) { - this.desc = desc; + this(desc, Arrays.asList(phases)); + } + + private CompilationPhases(final String desc, final CompilationPhases base, final CompilationPhase... phases) { + this(desc, concat(base.phases, Arrays.asList(phases))); + } + + private CompilationPhases(final String desc, final CompilationPhase first, final CompilationPhases rest) { + this(desc, concat(Collections.singletonList(first), rest.phases)); + } + + private CompilationPhases(final String desc, final CompilationPhases base) { + this(desc, base.phases); + } + + private CompilationPhases(final String desc, final CompilationPhases... bases) { + this(desc, concatPhases(bases)); + } - final List<CompilationPhase> newPhases = new LinkedList<>(); - newPhases.addAll(Arrays.asList(phases)); - this.phases = Collections.unmodifiableList(newPhases); + private CompilationPhases(final String desc, final List<CompilationPhase> phases) { + this.desc = desc; + this.phases = phases; + } + + private static List<CompilationPhase> concatPhases(final CompilationPhases[] bases) { + final ArrayList<CompilationPhase> l = new ArrayList<>(); + for(final CompilationPhases base: bases) { + l.addAll(base.phases); + } + l.trimToSize(); + return l; + } + + private static <T> List<T> concat(final List<T> l1, final List<T> l2) { + final ArrayList<T> l = new ArrayList<>(l1); + l.addAll(l2); + l.trimToSize(); + return l; } @Override @@ -235,45 +303,6 @@ return "'" + desc + "' " + phases.toString(); } - private CompilationPhases setDescription(final String desc) { - return new CompilationPhases(desc, phases.toArray(new CompilationPhase[phases.size()])); - } - - private CompilationPhases removeLast() { - final LinkedList<CompilationPhase> list = new LinkedList<>(phases); - list.removeLast(); - return new CompilationPhases(desc, list.toArray(new CompilationPhase[list.size()])); - } - - private CompilationPhases addFirst(final CompilationPhase phase) { - if (phases.contains(phase)) { - return this; - } - final LinkedList<CompilationPhase> list = new LinkedList<>(phases); - list.addFirst(phase); - return new CompilationPhases(desc, list.toArray(new CompilationPhase[list.size()])); - } - - @SuppressWarnings("unused") //TODO I'll use this soon - private CompilationPhases replace(final CompilationPhase phase, final CompilationPhase newPhase) { - final LinkedList<CompilationPhase> list = new LinkedList<>(); - for (final CompilationPhase p : phases) { - list.add(p == phase ? newPhase : p); - } - return new CompilationPhases(desc, list.toArray(new CompilationPhase[list.size()])); - } - - private CompilationPhases addAfter(final CompilationPhase phase, final CompilationPhase newPhase) { - final LinkedList<CompilationPhase> list = new LinkedList<>(); - for (final CompilationPhase p : phases) { - list.add(p); - if (p == phase) { - list.add(newPhase); - } - } - return new CompilationPhases(desc, list.toArray(new CompilationPhase[list.size()])); - } - boolean contains(final CompilationPhase phase) { return phases.contains(phase); } @@ -284,7 +313,7 @@ } boolean isRestOfCompilation() { - return this == COMPILE_ALL_RESTOF || this == COMPILE_FROM_BYTECODE_RESTOF; + return this == COMPILE_ALL_RESTOF || this == GENERATE_BYTECODE_AND_INSTALL_RESTOF || this == COMPILE_SERIALIZED_RESTOF; } String getDesc() { @@ -749,6 +778,14 @@ compileUnits.addAll(newUnits); } + void serializeAst(final FunctionNode fn) { + serializedAsts.put(fn.getId(), AstSerializer.serialize(fn)); + } + + byte[] removeSerializedAst(final int fnId) { + return serializedAsts.remove(fnId); + } + CompileUnit findUnit(final long weight) { for (final CompileUnit unit : compileUnits) { if (unit.canHold(weight)) { @@ -771,7 +808,10 @@ } RecompilableScriptFunctionData getScriptFunctionData(final int functionId) { - return compiledFunction == null ? null : compiledFunction.getScriptFunctionData(functionId); + assert compiledFunction != null; + final RecompilableScriptFunctionData fn = compiledFunction.getScriptFunctionData(functionId); + assert fn != null : functionId; + return fn; } boolean isGlobalSymbol(final FunctionNode fn, final String name) {
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/FindScopeDepths.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/FindScopeDepths.java Thu Oct 23 13:45:22 2014 -0700 @@ -187,7 +187,6 @@ if (compiler.isOnDemandCompilation()) { final RecompilableScriptFunctionData data = compiler.getScriptFunctionData(newFunctionNode.getId()); - assert data != null : newFunctionNode.getName() + " lacks data"; if (data.inDynamicContext()) { log.fine("Reviving scriptfunction ", quote(name), " as defined in previous (now lost) dynamic scope."); newFunctionNode = newFunctionNode.setInDynamicContext(lc); @@ -202,7 +201,7 @@ //create recompilable scriptfunctiondata final int fnId = newFunctionNode.getId(); - final Map<Integer, RecompilableScriptFunctionData> nestedFunctions = fnIdToNestedFunctions.get(fnId); + final Map<Integer, RecompilableScriptFunctionData> nestedFunctions = fnIdToNestedFunctions.remove(fnId); assert nestedFunctions != null; // Generate the object class and property map in case this function is ever used as constructor @@ -212,8 +211,8 @@ new AllocatorDescriptor(newFunctionNode.getThisProperties()), nestedFunctions, externalSymbolDepths.get(fnId), - internalSymbols.get(fnId) - ); + internalSymbols.get(fnId), + compiler.removeSerializedAst(fnId)); if (lc.getOutermostFunction() != newFunctionNode) { final FunctionNode parentFn = lc.getParentFunction(newFunctionNode);
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/Label.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/Label.java Thu Oct 23 13:45:22 2014 -0700 @@ -24,6 +24,7 @@ */ package jdk.nashorn.internal.codegen; +import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; @@ -39,7 +40,9 @@ * * see -Dnashorn.codegen.debug, --log=codegen */ -public final class Label { +public final class Label implements Serializable { + private static final long serialVersionUID = 1L; + //byte code generation evaluation type stack for consistency check //and correct opcode selection. one per label as a label may be a //join point @@ -491,7 +494,7 @@ private final String name; /** Type stack at this label */ - private Label.Stack stack; + private transient Label.Stack stack; /** ASM representation of this label */ private jdk.internal.org.objectweb.asm.Label label; @@ -500,9 +503,9 @@ private final int id; /** Is this label reachable (anything ever jumped to it)? */ - private boolean reachable; + private transient boolean reachable; - private boolean breakTarget; + private transient boolean breakTarget; /** * Constructor
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java Thu Oct 23 13:45:22 2014 -0700 @@ -72,7 +72,7 @@ import jdk.nashorn.internal.ir.ReturnNode; import jdk.nashorn.internal.ir.RuntimeNode; import jdk.nashorn.internal.ir.RuntimeNode.Request; -import jdk.nashorn.internal.ir.SplitNode; +import jdk.nashorn.internal.ir.SplitReturn; import jdk.nashorn.internal.ir.Statement; import jdk.nashorn.internal.ir.SwitchNode; import jdk.nashorn.internal.ir.Symbol; @@ -361,10 +361,6 @@ // Synthetic return node that we must insert at the end of the function if it's end is reachable. private ReturnNode syntheticReturn; - // Topmost current split node (if any) - private SplitNode topSplit; - private boolean split; - private boolean alreadyEnteredTopLevelFunction; // LvarType and conversion information gathered during the top-down pass; applied to nodes in the bottom-up pass. @@ -477,22 +473,7 @@ return false; } final BreakableNode target = jump.getTarget(lc); - return splitAwareJumpToLabel(jump, target, jump.getTargetLabel(target)); - } - - private boolean splitAwareJumpToLabel(final JumpStatement jumpStatement, final BreakableNode target, final Label targetLabel) { - final JoinPredecessor jumpOrigin; - if(topSplit != null && lc.isExternalTarget(topSplit, target)) { - // If the jump target is outside the topmost split node, then we'll create a synthetic jump origin in the - // split node. - jumpOrigin = new JoinPredecessorExpression(); - topSplit.addJump(jumpOrigin, targetLabel); - } else { - // Otherwise, the original jump statement is the jump origin - jumpOrigin = jumpStatement; - } - - jumpToLabel(jumpOrigin, targetLabel, getBreakTargetTypes(target)); + jumpToLabel(jump, jump.getTargetLabel(target), getBreakTargetTypes(target)); doesNotContinueSequentially(); return false; } @@ -703,18 +684,9 @@ } @Override - public boolean enterSplitNode(final SplitNode splitNode) { - if(!reachable) { - return false; - } - // Need to visit inside of split nodes. While it's true that they don't have local variables, we need to visit - // breaks, continues, and returns in them. - if(topSplit == null) { - topSplit = splitNode; - } - split = true; - setType(getCompilerConstantSymbol(lc.getCurrentFunction(), CompilerConstants.RETURN), LvarType.UNDEFINED); - return true; + public boolean enterSplitReturn(final SplitReturn splitReturn) { + doesNotContinueSequentially(); + return false; } @Override @@ -1116,15 +1088,6 @@ if(returnType.isUnknown()) { returnType = Type.OBJECT; } - - if(split) { - // If the function is split, the ":return" symbol is used and needs a slot. Note we can't mark the return - // symbol as used in enterSplitNode, as we don't know the final return type of the function earlier than - // here. - final Symbol retSymbol = getCompilerConstantSymbol(lc.getCurrentFunction(), CompilerConstants.RETURN); - retSymbol.setHasSlotFor(returnType); - retSymbol.setNeedsSlot(true); - } } private void createSyntheticReturn(final Block body) {
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/Lower.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/Lower.java Thu Oct 23 13:45:22 2014 -0700 @@ -352,8 +352,6 @@ private Node spliceFinally(final TryNode tryNode, final List<ThrowNode> rethrows, final Block finallyBody) { assert tryNode.getFinallyBody() == null; - final LexicalContext lowerLc = lc; - final TryNode newTryNode = (TryNode)tryNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { final List<Node> insideTry = new ArrayList<>(); @@ -406,7 +404,6 @@ //still in the try block, store it in a result value and return it afterwards resultNode = new IdentNode(Lower.this.compilerConstant(RETURN)); newStatements.add(new ExpressionStatement(returnNode.getLineNumber(), returnNode.getToken(), returnNode.getFinish(), new BinaryNode(Token.recast(returnNode.getToken(), TokenType.ASSIGN), resultNode, expr))); - lowerLc.setFlag(lowerLc.getCurrentFunction(), FunctionNode.USES_RETURN_SYMBOL); } else { resultNode = null; }
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/MethodEmitter.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/MethodEmitter.java Thu Oct 23 13:45:22 2014 -0700 @@ -71,6 +71,7 @@ import static jdk.nashorn.internal.codegen.ObjectClassGenerator.PRIMITIVE_FIELD_TYPE; import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_OPTIMISTIC; import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_PROGRAM_POINT_SHIFT; + import java.io.PrintStream; import java.lang.reflect.Array; import java.util.Collection; @@ -88,11 +89,9 @@ import jdk.nashorn.internal.codegen.types.BitwiseType; import jdk.nashorn.internal.codegen.types.NumericType; import jdk.nashorn.internal.codegen.types.Type; -import jdk.nashorn.internal.ir.BreakableNode; import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.IdentNode; import jdk.nashorn.internal.ir.JoinPredecessor; -import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.LocalVariableConversion; import jdk.nashorn.internal.ir.RuntimeNode; @@ -1663,19 +1662,6 @@ } /** - * Goto, possibly when splitting is taking place. If - * a splitNode exists, we need to handle the case that the - * jump target is another method - * - * @param label destination label - * @param targetNode the node to which the destination label belongs (the label is normally a break or continue - * label) - */ - void splitAwareGoto(final LexicalContext lc, final Label label, final BreakableNode targetNode) { - _goto(label); - } - - /** * Perform a comparison of two number types that are popped from the stack * * @param isCmpG is this a dcmpg semantic, false if it's a dcmpl semantic
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/OptimisticTypesCalculator.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/OptimisticTypesCalculator.java Thu Oct 23 13:45:22 2014 -0700 @@ -30,7 +30,6 @@ import java.util.ArrayDeque; import java.util.BitSet; import java.util.Deque; -import jdk.nashorn.internal.IntDeque; import jdk.nashorn.internal.ir.AccessNode; import jdk.nashorn.internal.ir.BinaryNode; import jdk.nashorn.internal.ir.CallNode; @@ -49,7 +48,6 @@ import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.Optimistic; import jdk.nashorn.internal.ir.PropertyNode; -import jdk.nashorn.internal.ir.SplitNode; import jdk.nashorn.internal.ir.Symbol; import jdk.nashorn.internal.ir.TernaryNode; import jdk.nashorn.internal.ir.UnaryNode; @@ -70,8 +68,6 @@ // Per-function bit set of program points that must never be optimistic. final Deque<BitSet> neverOptimistic = new ArrayDeque<>(); - // Per-function depth of split nodes - final IntDeque splitDepth = new IntDeque(); OptimisticTypesCalculator(final Compiler compiler) { super(new LexicalContext()); @@ -155,7 +151,6 @@ return false; } neverOptimistic.push(new BitSet()); - splitDepth.push(0); return true; } @@ -190,19 +185,6 @@ } @Override - public boolean enterSplitNode(final SplitNode splitNode) { - splitDepth.getAndIncrement(); - return true; - } - - @Override - public Node leaveSplitNode(final SplitNode splitNode) { - final int depth = splitDepth.decrementAndGet(); - assert depth >= 0; - return splitNode; - } - - @Override public boolean enterVarNode(final VarNode varNode) { tagNeverOptimistic(varNode.getName()); return true; @@ -226,16 +208,11 @@ @Override public Node leaveFunctionNode(final FunctionNode functionNode) { neverOptimistic.pop(); - final int lastSplitDepth = splitDepth.pop(); - assert lastSplitDepth == 0; return functionNode.setState(lc, CompilationState.OPTIMISTIC_TYPES_ASSIGNED); } @Override public Node leaveIdentNode(final IdentNode identNode) { - if(inSplitNode()) { - return identNode; - } final Symbol symbol = identNode.getSymbol(); if(symbol == null) { assert identNode.isPropertyName(); @@ -256,7 +233,7 @@ private Expression leaveOptimistic(final Optimistic opt) { final int pp = opt.getProgramPoint(); - if(isValid(pp) && !inSplitNode() && !neverOptimistic.peek().get(pp)) { + if(isValid(pp) && !neverOptimistic.peek().get(pp)) { return (Expression)opt.setType(compiler.getOptimisticType(opt)); } return (Expression)opt; @@ -277,8 +254,4 @@ tagNeverOptimistic(test.getExpression()); } } - - private boolean inSplitNode() { - return splitDepth.peek() > 0; - } }
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/ProgramPoints.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/ProgramPoints.java Thu Oct 23 13:45:22 2014 -0700 @@ -85,7 +85,7 @@ @Override public boolean enterVarNode(final VarNode varNode) { - noProgramPoint.add(varNode.getAssignmentDest()); + noProgramPoint.add(varNode.getName()); return true; }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/ReplaceCompileUnits.java Thu Oct 23 13:45:22 2014 -0700 @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.nashorn.internal.codegen; + +import java.util.ArrayList; +import java.util.List; +import jdk.nashorn.internal.ir.CompileUnitHolder; +import jdk.nashorn.internal.ir.FunctionNode; +import jdk.nashorn.internal.ir.FunctionNode.CompilationState; +import jdk.nashorn.internal.ir.LexicalContext; +import jdk.nashorn.internal.ir.LiteralNode; +import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; +import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit; +import jdk.nashorn.internal.ir.Node; +import jdk.nashorn.internal.ir.visitor.NodeVisitor; + +/** + * Base class for a node visitor that replaces {@link CompileUnit}s in {@link CompileUnitHolder}s. + */ +abstract class ReplaceCompileUnits extends NodeVisitor<LexicalContext> { + ReplaceCompileUnits() { + super(new LexicalContext()); + } + + /** + * Override to provide a replacement for an old compile unit. + * @param oldUnit the old compile unit to replace + * @return the compile unit's replacement. + */ + abstract CompileUnit getReplacement(final CompileUnit oldUnit); + + CompileUnit getExistingReplacement(final CompileUnitHolder node) { + final CompileUnit oldUnit = node.getCompileUnit(); + assert oldUnit != null; + + final CompileUnit newUnit = getReplacement(oldUnit); + assert newUnit != null; + + return newUnit; + } + + @Override + public Node leaveFunctionNode(final FunctionNode node) { + return node.setCompileUnit(lc, getExistingReplacement(node)).setState(lc, CompilationState.COMPILE_UNITS_REUSED); + } + + @Override + public Node leaveLiteralNode(final LiteralNode<?> node) { + if (node instanceof ArrayLiteralNode) { + final ArrayLiteralNode aln = (ArrayLiteralNode)node; + if (aln.getUnits() == null) { + return node; + } + final List<ArrayUnit> newArrayUnits = new ArrayList<>(); + for (final ArrayUnit au : aln.getUnits()) { + newArrayUnits.add(new ArrayUnit(getExistingReplacement(au), au.getLo(), au.getHi())); + } + return aln.setUnits(lc, newArrayUnits); + } + return node; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/SplitIntoFunctions.java Thu Oct 23 13:45:22 2014 -0700 @@ -0,0 +1,450 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.nashorn.internal.codegen; + +import static jdk.nashorn.internal.ir.Node.NO_FINISH; +import static jdk.nashorn.internal.ir.Node.NO_LINE_NUMBER; +import static jdk.nashorn.internal.ir.Node.NO_TOKEN; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Deque; +import java.util.List; +import java.util.Objects; +import jdk.nashorn.internal.ir.AccessNode; +import jdk.nashorn.internal.ir.BinaryNode; +import jdk.nashorn.internal.ir.Block; +import jdk.nashorn.internal.ir.BlockLexicalContext; +import jdk.nashorn.internal.ir.BreakNode; +import jdk.nashorn.internal.ir.CallNode; +import jdk.nashorn.internal.ir.CaseNode; +import jdk.nashorn.internal.ir.ContinueNode; +import jdk.nashorn.internal.ir.Expression; +import jdk.nashorn.internal.ir.ExpressionStatement; +import jdk.nashorn.internal.ir.FunctionNode; +import jdk.nashorn.internal.ir.FunctionNode.CompilationState; +import jdk.nashorn.internal.ir.GetSplitState; +import jdk.nashorn.internal.ir.IdentNode; +import jdk.nashorn.internal.ir.IfNode; +import jdk.nashorn.internal.ir.JumpStatement; +import jdk.nashorn.internal.ir.LiteralNode; +import jdk.nashorn.internal.ir.Node; +import jdk.nashorn.internal.ir.ReturnNode; +import jdk.nashorn.internal.ir.SetSplitState; +import jdk.nashorn.internal.ir.SplitNode; +import jdk.nashorn.internal.ir.SplitReturn; +import jdk.nashorn.internal.ir.Statement; +import jdk.nashorn.internal.ir.SwitchNode; +import jdk.nashorn.internal.ir.VarNode; +import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.parser.Token; +import jdk.nashorn.internal.parser.TokenType; + +/** + * A node visitor that replaces {@link SplitNode}s with anonymous function invocations and some additional constructs + * to support control flow across splits. By using this transformation, split functions are translated into ordinary + * JavaScript functions with nested anonymous functions. The transformations however introduce several AST nodes that + * have no JavaScript source representations ({@link GetSplitState}, {@link SetSplitState}, and {@link SplitReturn}), + * and therefore such function is no longer reparseable from its source. For that reason, split functions and their + * fragments are serialized in-memory and deserialized when they need to be recompiled either for deoptimization or + * for type specialization. + * NOTE: all {@code leave*()} methods for statements are returning their input nodes. That way, they will not mutate + * the original statement list in the block containing the statement, which is fine, as it'll be replaced by the + * lexical context when the block is left. If we returned something else (e.g. null), we'd cause a mutation in the + * enclosing block's statement list that is otherwise overwritten later anyway. + */ +final class SplitIntoFunctions extends NodeVisitor<BlockLexicalContext> { + private static final int FALLTHROUGH_STATE = -1; + private static final int RETURN_STATE = 0; + private static final int BREAK_STATE = 1; + private static final int FIRST_JUMP_STATE = 2; + + private static final String THIS_NAME = CompilerConstants.THIS.symbolName(); + private static final String RETURN_NAME = CompilerConstants.RETURN.symbolName(); + // Used as the name of the formal parameter for passing the current value of :return symbol into a split fragment. + private static final String RETURN_PARAM_NAME = RETURN_NAME + "-in"; + + private final Deque<FunctionState> functionStates = new ArrayDeque<>(); + private final Deque<SplitState> splitStates = new ArrayDeque<>(); + private final Namespace namespace; + + private boolean artificialBlock = false; + + // -1 is program; we need to use negative ones + private int nextFunctionId = -2; + + public SplitIntoFunctions(final Compiler compiler) { + super(new BlockLexicalContext() { + @Override + protected Block afterSetStatements(Block block) { + for(Statement stmt: block.getStatements()) { + assert !(stmt instanceof SplitNode); + } + return block; + } + }); + namespace = new Namespace(compiler.getScriptEnvironment().getNamespace()); + } + + @Override + public boolean enterFunctionNode(final FunctionNode functionNode) { + functionStates.push(new FunctionState(functionNode)); + return true; + } + + @Override + public Node leaveFunctionNode(final FunctionNode functionNode) { + functionStates.pop(); + return functionNode; + } + + @Override + protected Node leaveDefault(final Node node) { + if (node instanceof Statement) { + appendStatement((Statement)node); + } + return node; + } + + @Override + public boolean enterSplitNode(final SplitNode splitNode) { + getCurrentFunctionState().splitDepth++; + splitStates.push(new SplitState(splitNode)); + return true; + } + + @Override + public Node leaveSplitNode(final SplitNode splitNode) { + // Replace the split node with an anonymous function expression call. + + final FunctionState fnState = getCurrentFunctionState(); + + final String name = splitNode.getName(); + Block body = splitNode.getBody(); + final int firstLineNumber = body.getFirstStatementLineNumber(); + final long token = body.getToken(); + final int finish = body.getFinish(); + + final FunctionNode originalFn = fnState.fn; + assert originalFn == lc.getCurrentFunction(); + final boolean isProgram = originalFn.isProgram(); + + // Change SplitNode({...}) into "function () { ... }", or "function (:return-in) () { ... }" (for program) + final long newFnToken = Token.toDesc(TokenType.FUNCTION, nextFunctionId--, 0); + final FunctionNode fn = new FunctionNode( + originalFn.getSource(), + body.getFirstStatementLineNumber(), + newFnToken, + finish, + newFnToken, + NO_TOKEN, + namespace, + createIdent(name), + originalFn.getName() + "$" + name, + isProgram ? Collections.singletonList(createReturnParamIdent()) : Collections.<IdentNode>emptyList(), + FunctionNode.Kind.NORMAL, + // We only need IS_SPLIT conservatively, in case it contains any array units so that we force + // the :callee's existence, to force :scope to never be in a slot lower than 2. This is actually + // quite a horrible hack to do with CodeGenerator.fixScopeSlot not trampling other parameters + // and should go away once we no longer have array unit handling in codegen. Note however that + // we still use IS_SPLIT as the criteria in CompilationPhase.SERIALIZE_SPLIT_PHASE. + FunctionNode.IS_ANONYMOUS | FunctionNode.USES_ANCESTOR_SCOPE | FunctionNode.IS_SPLIT, + body, + CompilationState.INITIALIZED, + null + ) + .setCompileUnit(lc, splitNode.getCompileUnit()) + .copyCompilationState(lc, originalFn); + + // Call the function: + // either "(function () { ... }).call(this)" + // or "(function (:return-in) { ... }).call(this, :return)" + // NOTE: Function.call() has optimized linking that basically does a pass-through to the function being invoked. + // NOTE: CompilationPhase.PROGRAM_POINT_PHASE happens after this, so these calls are subject to optimistic + // assumptions on their return value (when they return a value), as they should be. + final IdentNode thisIdent = createIdent(THIS_NAME); + final CallNode callNode = new CallNode(firstLineNumber, token, finish, new AccessNode(NO_TOKEN, NO_FINISH, fn, "call"), + isProgram ? Arrays.<Expression>asList(thisIdent, createReturnIdent()) + : Collections.<Expression>singletonList(thisIdent), + false); + + final SplitState splitState = splitStates.pop(); + fnState.splitDepth--; + + final Expression callWithReturn; + final boolean hasReturn = splitState.hasReturn; + if (hasReturn && fnState.splitDepth > 0) { + final SplitState parentSplit = splitStates.peek(); + if (parentSplit != null) { + // Propagate hasReturn to parent split + parentSplit.hasReturn = true; + } + } + if (hasReturn || isProgram) { + // capture return value: ":return = (function () { ... })();" + callWithReturn = new BinaryNode(Token.recast(token, TokenType.ASSIGN), createReturnIdent(), callNode); + } else { + // no return value, just call : "(function () { ... })();" + callWithReturn = callNode; + } + appendStatement(new ExpressionStatement(firstLineNumber, token, finish, callWithReturn)); + + Statement splitStateHandler; + + final List<JumpStatement> jumpStatements = splitState.jumpStatements; + final int jumpCount = jumpStatements.size(); + // There are jumps (breaks or continues) that need to be propagated outside the split node. We need to + // set up a switch statement for them: + // switch(:scope.getScopeState()) { ... } + if (jumpCount > 0) { + final List<CaseNode> cases = new ArrayList<>(jumpCount + (hasReturn ? 1 : 0)); + if (hasReturn) { + // If the split node also contained a return, we'll slip it as a case in the switch statement + addCase(cases, RETURN_STATE, createReturnFromSplit()); + } + int i = FIRST_JUMP_STATE; + for (final JumpStatement jump: jumpStatements) { + addCase(cases, i++, enblockAndVisit(jump)); + } + splitStateHandler = new SwitchNode(NO_LINE_NUMBER, token, finish, GetSplitState.INSTANCE, cases, null); + } else { + splitStateHandler = null; + } + + // As the switch statement itself is breakable, an unlabelled break can't be in the switch statement, + // so we need to test for it separately. + if (splitState.hasBreak) { + // if(:scope.getScopeState() == Scope.BREAK) { break; } + splitStateHandler = makeIfStateEquals(firstLineNumber, token, finish, BREAK_STATE, + enblockAndVisit(new BreakNode(NO_LINE_NUMBER, token, finish, null)), splitStateHandler); + } + + // Finally, if the split node had a return statement, but there were no external jumps, we didn't have + // the switch statement to handle the return, so we need a separate if for it. + if (hasReturn && jumpCount == 0) { + // if (:scope.getScopeState() == Scope.RETURN) { return :return; } + splitStateHandler = makeIfStateEquals(NO_LINE_NUMBER, token, finish, RETURN_STATE, + createReturnFromSplit(), splitStateHandler); + } + + if (splitStateHandler != null) { + appendStatement(splitStateHandler); + } + + return splitNode; + } + + private static void addCase(final List<CaseNode> cases, final int i, final Block body) { + cases.add(new CaseNode(NO_TOKEN, NO_FINISH, intLiteral(i), body)); + } + + private static LiteralNode<Number> intLiteral(final int i) { + return LiteralNode.newInstance(NO_TOKEN, NO_FINISH, i); + } + + private static Block createReturnFromSplit() { + return new Block(NO_TOKEN, NO_FINISH, createReturnReturn()); + } + + private static ReturnNode createReturnReturn() { + return new ReturnNode(NO_LINE_NUMBER, NO_TOKEN, NO_FINISH, createReturnIdent()); + } + + private static IdentNode createReturnIdent() { + return createIdent(RETURN_NAME); + } + + private static IdentNode createReturnParamIdent() { + return createIdent(RETURN_PARAM_NAME); + } + + private static IdentNode createIdent(final String name) { + return new IdentNode(NO_TOKEN, NO_FINISH, name); + } + + private Block enblockAndVisit(final JumpStatement jump) { + artificialBlock = true; + final Block block = (Block)new Block(NO_TOKEN, NO_FINISH, jump).accept(this); + artificialBlock = false; + return block; + } + + private static IfNode makeIfStateEquals(final int lineNumber, final long token, final int finish, + final int value, final Block pass, final Statement fail) { + return new IfNode(lineNumber, token, finish, + new BinaryNode(Token.recast(token, TokenType.EQ_STRICT), + GetSplitState.INSTANCE, intLiteral(value)), + pass, + fail == null ? null : new Block(NO_TOKEN, NO_FINISH, fail)); + } + + @Override + public boolean enterVarNode(VarNode varNode) { + if (!inSplitNode()) { + return super.enterVarNode(varNode); + } + assert !varNode.isBlockScoped(); //TODO: we must handle these too, but we currently don't + + final Expression init = varNode.getInit(); + if (varNode.isAnonymousFunctionDeclaration()) { + // We ain't moving anonymous function declarations. + return super.enterVarNode(varNode); + } + + // Move a declaration-only var statement to the top of the outermost function. + getCurrentFunctionState().varStatements.add(varNode.setInit(null)); + // If it had an initializer, replace it with an assignment expression statement. Note that "var" is a + // statement, so it doesn't contribute to :return of the programs, therefore we are _not_ adding a + // ":return = ..." assignment around the original assignment. + if (init != null) { + final long token = Token.recast(varNode.getToken(), TokenType.ASSIGN); + new ExpressionStatement(varNode.getLineNumber(), token, varNode.getFinish(), + new BinaryNode(token, varNode.getName(), varNode.getInit())).accept(this); + } + + return false; + } + + @Override + public Node leaveBlock(final Block block) { + if (!artificialBlock) { + if (lc.isFunctionBody()) { + // Prepend declaration-only var statements to the top of the statement list. + lc.prependStatements(getCurrentFunctionState().varStatements); + } else if (lc.isSplitBody()) { + appendSplitReturn(FALLTHROUGH_STATE, NO_LINE_NUMBER); + if (getCurrentFunctionState().fn.isProgram()) { + // If we're splitting the program, make sure every shard ends with "return :return" and + // begins with ":return = :return-in;". + lc.prependStatement(new ExpressionStatement(NO_LINE_NUMBER, NO_TOKEN, NO_FINISH, + new BinaryNode(Token.toDesc(TokenType.ASSIGN, 0, 0), createReturnIdent(), createReturnParamIdent()))); + } + } + } + return block; + } + + @Override + public Node leaveBreakNode(final BreakNode breakNode) { + return leaveJumpNode(breakNode); + } + + @Override + public Node leaveContinueNode(final ContinueNode continueNode) { + return leaveJumpNode(continueNode); + } + + private JumpStatement leaveJumpNode(final JumpStatement jump) { + if (inSplitNode()) { + final SplitState splitState = getCurrentSplitState(); + final SplitNode splitNode = splitState.splitNode; + if (lc.isExternalTarget(splitNode, jump.getTarget(lc))) { + appendSplitReturn(splitState.getSplitStateIndex(jump), jump.getLineNumber()); + return jump; + } + } + appendStatement(jump); + return jump; + } + + private void appendSplitReturn(final int splitState, final int lineNumber) { + appendStatement(new SetSplitState(splitState, lineNumber)); + if (getCurrentFunctionState().fn.isProgram()) { + // If we're splitting the program, make sure every fragment passes back :return + appendStatement(createReturnReturn()); + } else { + appendStatement(SplitReturn.INSTANCE); + } + } + + @Override + public Node leaveReturnNode(final ReturnNode returnNode) { + if(inSplitNode()) { + appendStatement(new SetSplitState(RETURN_STATE, returnNode.getLineNumber())); + getCurrentSplitState().hasReturn = true; + } + appendStatement(returnNode); + return returnNode; + } + + private void appendStatement(final Statement statement) { + lc.appendStatement(statement); + } + + private boolean inSplitNode() { + return getCurrentFunctionState().splitDepth > 0; + } + + private FunctionState getCurrentFunctionState() { + return functionStates.peek(); + } + + private SplitState getCurrentSplitState() { + return splitStates.peek(); + } + + private static class FunctionState { + final FunctionNode fn; + final List<Statement> varStatements = new ArrayList<>(); + int splitDepth; + + FunctionState(final FunctionNode fn) { + this.fn = fn; + } + } + + private static class SplitState { + final SplitNode splitNode; + boolean hasReturn; + boolean hasBreak; + + final List<JumpStatement> jumpStatements = new ArrayList<>(); + + int getSplitStateIndex(final JumpStatement jump) { + if (jump instanceof BreakNode && jump.getLabelName() == null) { + // Unlabelled break is a special case + hasBreak = true; + return BREAK_STATE; + } + + int i = 0; + for(final JumpStatement exJump: jumpStatements) { + if (jump.getClass() == exJump.getClass() && Objects.equals(jump.getLabelName(), exJump.getLabelName())) { + return i + FIRST_JUMP_STATE; + } + ++i; + } + jumpStatements.add(jump); + return i + FIRST_JUMP_STATE; + } + + SplitState(final SplitNode splitNode) { + this.splitNode = splitNode; + } + } +}
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/SplitMethodEmitter.java Thu Oct 23 11:19:29 2014 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.nashorn.internal.codegen; - -import java.util.ArrayList; -import java.util.List; -import jdk.internal.org.objectweb.asm.MethodVisitor; -import jdk.nashorn.internal.codegen.types.Type; -import jdk.nashorn.internal.ir.BreakableNode; -import jdk.nashorn.internal.ir.LexicalContext; -import jdk.nashorn.internal.ir.SplitNode; - -/** - * Emitter used for splitting methods. Needs to keep track of if there are jump targets - * outside the current split node. All external jump targets encountered at method - * emission are logged, and {@code CodeGenerator#leaveSplitNode(SplitNode)} creates - * an appropriate jump table when the SplitNode has been iterated through - */ -public class SplitMethodEmitter extends MethodEmitter { - - private final SplitNode splitNode; - - private final List<Label> externalTargets = new ArrayList<>(); - /** - * In addition to external target labels, we need to track the target breakables too as the code generator needs to - * be able to correctly pop the scopes to the target, see {@link CodeGenerator#leaveSplitNode(SplitNode)}. Note that - * this is only used within CodeGenerator, which doesn't mutate the AST, so keeping pointers to other nodes is not - * incorrect. - */ - private final List<BreakableNode> externalTargetNodes = new ArrayList<>(); - - SplitMethodEmitter(final ClassEmitter classEmitter, final MethodVisitor mv, final SplitNode splitNode) { - super(classEmitter, mv); - this.splitNode = splitNode; - } - - @Override - void splitAwareGoto(final LexicalContext lc, final Label label, final BreakableNode targetNode) { - assert splitNode != null; - final int index = findExternalTarget(lc, label, targetNode); - if (index >= 0) { - setSplitState(index + 1); // 0 is ordinary return - final Type retType = functionNode.getReturnType(); - loadUndefined(retType); - _return(retType); - } else { - super.splitAwareGoto(lc, label, targetNode); - } - } - - private int findExternalTarget(final LexicalContext lc, final Label label, final BreakableNode targetNode) { - final int index = externalTargets.indexOf(label); - - if (index >= 0) { - return index; - } - - if (lc.isExternalTarget(splitNode, targetNode)) { - externalTargets.add(label); - externalTargetNodes.add(targetNode); - return externalTargets.size() - 1; - } - return -1; - } - - @Override - MethodEmitter registerReturn() { - setHasReturn(); - return setSplitState(0); - } - - final List<Label> getExternalTargets() { - return externalTargets; - } - - final List<BreakableNode> getExternalTargetNodes() { - return externalTargetNodes; - } -}
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/types/ArrayType.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/types/ArrayType.java Thu Oct 23 13:45:22 2014 -0700 @@ -37,6 +37,7 @@ * This is an array type, i.e. OBJECT_ARRAY, NUMBER_ARRAY. */ public class ArrayType extends ObjectType implements BytecodeArrayOps { + private static final long serialVersionUID = 1L; /** * Constructor
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/types/BitwiseType.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/types/BitwiseType.java Thu Oct 23 13:45:22 2014 -0700 @@ -29,6 +29,7 @@ * This class represents a numeric type that can be used for bit operations. */ public abstract class BitwiseType extends NumericType implements BytecodeBitwiseOps { + private static final long serialVersionUID = 1L; /** * Constructor
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/types/BooleanType.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/types/BooleanType.java Thu Oct 23 13:45:22 2014 -0700 @@ -69,6 +69,7 @@ * The boolean type class */ public final class BooleanType extends Type { + private static final long serialVersionUID = 1L; private static final CompilerConstants.Call VALUE_OF = staticCallNoLookup(Boolean.class, "valueOf", Boolean.class, boolean.class); private static final CompilerConstants.Call TO_STRING = staticCallNoLookup(Boolean.class, "toString", String.class, boolean.class);
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/types/IntType.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/types/IntType.java Thu Oct 23 13:45:22 2014 -0700 @@ -60,6 +60,7 @@ * Type class: INT */ class IntType extends BitwiseType { + private static final long serialVersionUID = 1L; private static final CompilerConstants.Call TO_STRING = staticCallNoLookup(Integer.class, "toString", String.class, int.class); private static final CompilerConstants.Call VALUE_OF = staticCallNoLookup(Integer.class, "valueOf", Integer.class, int.class);
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/types/LongType.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/types/LongType.java Thu Oct 23 13:45:22 2014 -0700 @@ -54,6 +54,7 @@ * Type class: LONG */ class LongType extends BitwiseType { + private static final long serialVersionUID = 1L; private static final CompilerConstants.Call VALUE_OF = staticCallNoLookup(Long.class, "valueOf", Long.class, long.class);
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/types/NumberType.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/types/NumberType.java Thu Oct 23 13:45:22 2014 -0700 @@ -46,6 +46,7 @@ import jdk.nashorn.internal.runtime.JSType; class NumberType extends NumericType { + private static final long serialVersionUID = 1L; private static final CompilerConstants.Call VALUE_OF = staticCallNoLookup(Double.class, "valueOf", Double.class, double.class);
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/types/NumericType.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/types/NumericType.java Thu Oct 23 13:45:22 2014 -0700 @@ -29,6 +29,8 @@ * This is a numeric type, i.e. NUMBER, LONG, INT, INT32. */ public abstract class NumericType extends Type implements BytecodeNumericOps { + private static final long serialVersionUID = 1L; + /** * Constructor *
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/types/ObjectType.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/types/ObjectType.java Thu Oct 23 13:45:22 2014 -0700 @@ -47,6 +47,7 @@ * contain a class that is a more specialized object */ class ObjectType extends Type { + private static final long serialVersionUID = 1L; protected ObjectType() { this(Object.class);
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/types/Type.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/types/Type.java Thu Oct 23 13:45:22 2014 -0700 @@ -47,9 +47,11 @@ import static jdk.internal.org.objectweb.asm.Opcodes.T_INT; import static jdk.internal.org.objectweb.asm.Opcodes.T_LONG; import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup; + import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; +import java.io.Serializable; import java.lang.invoke.CallSite; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; @@ -88,19 +90,20 @@ * INTs rather than OBJECTs */ -public abstract class Type implements Comparable<Type>, BytecodeOps { +public abstract class Type implements Comparable<Type>, BytecodeOps, Serializable { + private static final long serialVersionUID = 1L; /** Human readable name for type */ - private final String name; + private transient final String name; /** Descriptor for type */ - private final String descriptor; + private transient final String descriptor; /** The "weight" of the type. Used for picking widest/least specific common type */ - private final int weight; + private transient final int weight; /** How many bytecode slots does this type occupy */ - private final int slots; + private transient final int slots; /** The class for this type */ private final Class<?> clazz; @@ -113,7 +116,7 @@ Collections.synchronizedMap(new WeakHashMap<Class<?>, jdk.internal.org.objectweb.asm.Type>()); /** Internal ASM type for this Type - computed once at construction */ - private final jdk.internal.org.objectweb.asm.Type internalType; + private transient final jdk.internal.org.objectweb.asm.Type internalType; /** Weights are used to decide which types are "wider" than other types */ protected static final int MIN_WEIGHT = -1; @@ -583,6 +586,7 @@ public int getSlots() { return slots; } + /** * Returns the widest or most common of two types * @@ -606,6 +610,18 @@ } /** + * Returns the widest or most common of two types, given as classes + * + * @param type0 type one + * @param type1 type two + * + * @return the widest type + */ + public static Class<?> widest(final Class<?> type0, final Class<?> type1) { + return widest(Type.typeFor(type0), Type.typeFor(type1)).getTypeClass(); + } + + /** * When doing widening for return types of a function or a ternary operator, it is not valid to widen a boolean to * anything other than object. Note that this wouldn't be necessary if {@code Type.widest} did not allow * boolean-to-number widening. Eventually, we should address it there, but it affects too many other parts of the @@ -934,6 +950,8 @@ * This is the singleton for integer arrays */ public static final ArrayType INT_ARRAY = new ArrayType(int[].class) { + private static final long serialVersionUID = 1L; + @Override public void astore(final MethodVisitor method) { method.visitInsn(IASTORE); @@ -961,6 +979,8 @@ * This is the singleton for long arrays */ public static final ArrayType LONG_ARRAY = new ArrayType(long[].class) { + private static final long serialVersionUID = 1L; + @Override public void astore(final MethodVisitor method) { method.visitInsn(LASTORE); @@ -988,6 +1008,8 @@ * This is the singleton for numeric arrays */ public static final ArrayType NUMBER_ARRAY = new ArrayType(double[].class) { + private static final long serialVersionUID = 1L; + @Override public void astore(final MethodVisitor method) { method.visitInsn(DASTORE); @@ -1022,6 +1044,8 @@ /** This type, always an object type, just a toString override */ public static final Type THIS = new ObjectType() { + private static final long serialVersionUID = 1L; + @Override public String toString() { return "this"; @@ -1030,6 +1054,8 @@ /** Scope type, always an object type, just a toString override */ public static final Type SCOPE = new ObjectType() { + private static final long serialVersionUID = 1L; + @Override public String toString() { return "scope"; @@ -1041,6 +1067,7 @@ } private abstract static class ValueLessType extends Type { + private static final long serialVersionUID = 1L; ValueLessType(final String name) { super(name, Unknown.class, MIN_WEIGHT, 1); @@ -1092,6 +1119,8 @@ * inference. It has the minimum type width */ public static final Type UNKNOWN = new ValueLessType("<unknown>") { + private static final long serialVersionUID = 1L; + @Override public String getDescriptor() { return "<unknown>"; @@ -1108,6 +1137,7 @@ * inference. It has the minimum type width */ public static final Type SLOT_2 = new ValueLessType("<slot_2>") { + private static final long serialVersionUID = 1L; @Override public String getDescriptor() { @@ -1124,4 +1154,8 @@ cache.put(type.getTypeClass(), type); return type; } + + protected final Object readResolve() { + return Type.typeFor(clazz); + } }
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/AccessNode.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/AccessNode.java Thu Oct 23 13:45:22 2014 -0700 @@ -34,6 +34,8 @@ */ @Immutable public final class AccessNode extends BaseNode { + private static final long serialVersionUID = 1L; + /** Property name. */ private final String property;
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/BaseNode.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/BaseNode.java Thu Oct 23 13:45:22 2014 -0700 @@ -39,6 +39,7 @@ */ @Immutable public abstract class BaseNode extends Expression implements FunctionCall, Optimistic { + private static final long serialVersionUID = 1L; /** Base Node. */ protected final Expression base;
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/BinaryNode.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/BinaryNode.java Thu Oct 23 13:45:22 2014 -0700 @@ -43,6 +43,8 @@ */ @Immutable public final class BinaryNode extends Expression implements Assignment<Expression>, Optimistic { + private static final long serialVersionUID = 1L; + // Placeholder for "undecided optimistic ADD type". Unfortunately, we can't decide the type of ADD during optimistic // type calculation as it can have local variables as its operands that will decide its ultimate type. private static final Type OPTIMISTIC_UNDECIDED_TYPE = Type.typeFor(new Object(){/*empty*/}.getClass()); @@ -56,8 +58,8 @@ private final Type type; - private Type cachedType; - private Object cachedTypeFunction; + private transient Type cachedType; + private transient Object cachedTypeFunction; @Ignore private static final Set<TokenType> CAN_OVERFLOW =
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Block.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Block.java Thu Oct 23 13:45:22 2014 -0700 @@ -42,6 +42,8 @@ */ @Immutable public class Block extends Node implements BreakableNode, Terminal, Flags<Block> { + private static final long serialVersionUID = 1L; + /** List of statements */ protected final List<Statement> statements;
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/BlockLexicalContext.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/BlockLexicalContext.java Thu Oct 23 13:45:22 2014 -0700 @@ -109,6 +109,16 @@ } /** + * Prepend a list of statement to the block being generated + * @param statements a list of statements to prepend + */ + public void prependStatements(final List<Statement> statements) { + assert statements != null; + sstack.peek().addAll(0, statements); + } + + + /** * Get the last statement that was emitted into a block * @return the last statement emitted */
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/BlockStatement.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/BlockStatement.java Thu Oct 23 13:45:22 2014 -0700 @@ -32,6 +32,8 @@ * Represents a block used as a statement. */ public class BlockStatement extends Statement { + private static final long serialVersionUID = 1L; + /** Block to execute. */ private final Block block;
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/BreakNode.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/BreakNode.java Thu Oct 23 13:45:22 2014 -0700 @@ -34,6 +34,7 @@ */ @Immutable public final class BreakNode extends JumpStatement { + private static final long serialVersionUID = 1L; /** * Constructor
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/BreakableStatement.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/BreakableStatement.java Thu Oct 23 13:45:22 2014 -0700 @@ -32,6 +32,7 @@ @Immutable abstract class BreakableStatement extends LexicalContextStatement implements BreakableNode { + private static final long serialVersionUID = 1L; /** break label. */ protected final Label breakLabel;
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/CallNode.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/CallNode.java Thu Oct 23 13:45:22 2014 -0700 @@ -27,6 +27,7 @@ import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT; +import java.io.Serializable; import java.util.Collections; import java.util.List; import java.util.function.Function; @@ -40,6 +41,7 @@ */ @Immutable public final class CallNode extends LexicalContextExpression implements Optimistic { + private static final long serialVersionUID = 1L; /** Function identifier or function body. */ private final Expression function; @@ -64,7 +66,8 @@ /** * Arguments to be passed to builtin {@code eval} function */ - public static class EvalArgs { + public static class EvalArgs implements Serializable { + private static final long serialVersionUID = 1L; private final List<Expression> args; /** location string for the eval call */
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/CaseNode.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/CaseNode.java Thu Oct 23 13:45:22 2014 -0700 @@ -37,6 +37,8 @@ */ @Immutable public final class CaseNode extends Node implements JoinPredecessor, Labels, Terminal { + private static final long serialVersionUID = 1L; + /** Test expression. */ private final Expression test;
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/CatchNode.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/CatchNode.java Thu Oct 23 13:45:22 2014 -0700 @@ -33,6 +33,8 @@ */ @Immutable public final class CatchNode extends Statement { + private static final long serialVersionUID = 1L; + /** Exception identifier. */ private final IdentNode exception;
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ContinueNode.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ContinueNode.java Thu Oct 23 13:45:22 2014 -0700 @@ -34,6 +34,8 @@ */ @Immutable public class ContinueNode extends JumpStatement { + private static final long serialVersionUID = 1L; + /** * Constructor * @@ -80,4 +82,3 @@ return ((LoopNode)target).getContinueLabel(); } } -
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/EmptyNode.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/EmptyNode.java Thu Oct 23 13:45:22 2014 -0700 @@ -33,6 +33,7 @@ */ @Immutable public final class EmptyNode extends Statement { + private static final long serialVersionUID = 1L; /** * Constructor
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Expression.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Expression.java Thu Oct 23 13:45:22 2014 -0700 @@ -35,6 +35,8 @@ * */ public abstract class Expression extends Node { + private static final long serialVersionUID = 1L; + static final String OPT_IDENTIFIER = "%"; private static final Function<Symbol, Type> UNKNOWN_LOCALS = new Function<Symbol, Type>() {
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ExpressionStatement.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ExpressionStatement.java Thu Oct 23 13:45:22 2014 -0700 @@ -35,6 +35,8 @@ */ @Immutable public final class ExpressionStatement extends Statement { + private static final long serialVersionUID = 1L; + /** Expression to execute. */ private final Expression expression;
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ForNode.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ForNode.java Thu Oct 23 13:45:22 2014 -0700 @@ -33,6 +33,8 @@ */ @Immutable public final class ForNode extends LoopNode { + private static final long serialVersionUID = 1L; + /** Initialize expression for an ordinary for statement, or the LHS expression receiving iterated-over values in a * for-in statement. */ private final Expression init;
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/FunctionNode.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/FunctionNode.java Thu Oct 23 13:45:22 2014 -0700 @@ -31,6 +31,7 @@ import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_TRACE_ENTEREXIT; import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_TRACE_MISSES; import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_TRACE_VALUES; + import java.util.Collections; import java.util.EnumSet; import java.util.Iterator; @@ -57,6 +58,8 @@ */ @Immutable public final class FunctionNode extends LexicalContextExpression implements Flags<FunctionNode>, CompileUnitHolder { + private static final long serialVersionUID = 1L; + /** Type used for all FunctionNodes */ public static final Type FUNCTION_TYPE = Type.typeFor(ScriptFunction.class); @@ -107,7 +110,7 @@ } /** Source of entity. */ - private final Source source; + private transient final Source source; /** * Opaque object representing parser state at the end of the function. Used when reparsing outer functions @@ -141,7 +144,7 @@ private final long lastToken; /** Method's namespace. */ - private final Namespace namespace; + private transient final Namespace namespace; /** Current compilation state */ @Ignore @@ -207,31 +210,23 @@ /** Are we vararg, but do we just pass the arguments along to apply or call */ public static final int HAS_APPLY_TO_CALL_SPECIALIZATION = 1 << 12; - /** Does this function explicitly use the {@link CompilerConstants#RETURN} symbol? Some functions are known to - * always use the return symbol, namely a function that is a program (as it must track its last executed expression - * statement's value) as well as functions that are split (to communicate return values from inner to outer - * partitions). Other functions normally don't use the return symbol (so we optimize away its slot), except in some - * very special cases, e.g. when containing a return statement in a finally block. These special cases set this - * flag. */ - public static final int USES_RETURN_SYMBOL = 1 << 13; - /** * Is this function the top-level program? */ - public static final int IS_PROGRAM = 1 << 14; + public static final int IS_PROGRAM = 1 << 13; /** * Flag indicating whether this function uses the local variable symbol for itself. Only named function expressions * can have this flag set if they reference themselves (e.g. "(function f() { return f })". Declared functions will * use the symbol in their parent scope instead when they reference themselves by name. */ - public static final int USES_SELF_SYMBOL = 1 << 15; + public static final int USES_SELF_SYMBOL = 1 << 14; /** Does this function use the "this" keyword? */ - public static final int USES_THIS = 1 << 16; + public static final int USES_THIS = 1 << 15; /** Is this declared in a dynamic context */ - public static final int IN_DYNAMIC_CONTEXT = 1 << 17; + public static final int IN_DYNAMIC_CONTEXT = 1 << 16; /** * The following flags are derived from directive comments within this function. @@ -239,28 +234,28 @@ */ /** parser, print parse tree */ - public static final int IS_PRINT_PARSE = 1 << 18; + public static final int IS_PRINT_PARSE = 1 << 17; /** parser, print lower parse tree */ - public static final int IS_PRINT_LOWER_PARSE = 1 << 19; + public static final int IS_PRINT_LOWER_PARSE = 1 << 18; /** parser, print AST */ - public static final int IS_PRINT_AST = 1 << 20; + public static final int IS_PRINT_AST = 1 << 19; /** parser, print lower AST */ - public static final int IS_PRINT_LOWER_AST = 1 << 21; + public static final int IS_PRINT_LOWER_AST = 1 << 20; /** parser, print symbols */ - public static final int IS_PRINT_SYMBOLS = 1 << 22; + public static final int IS_PRINT_SYMBOLS = 1 << 21; // callsite tracing, profiling within this function /** profile callsites in this function? */ - public static final int IS_PROFILE = 1 << 23; + public static final int IS_PROFILE = 1 << 22; /** trace callsite enterexit in this function? */ - public static final int IS_TRACE_ENTEREXIT = 1 << 24; + public static final int IS_TRACE_ENTEREXIT = 1 << 23; /** trace callsite misses in this function? */ - public static final int IS_TRACE_MISSES = 1 << 25; + public static final int IS_TRACE_MISSES = 1 << 24; /** trace callsite values in this function? */ - public static final int IS_TRACE_VALUES = 1 << 26; + public static final int IS_TRACE_VALUES = 1 << 25; /** * Whether this function needs the callee {@link ScriptFunction} instance passed to its code as a @@ -268,7 +263,7 @@ * Rather, it is always calculated (see {@link #needsCallee()}). {@link RecompilableScriptFunctionData} * will, however, cache the value of this flag. */ - public static final int NEEDS_CALLEE = 1 << 27; + public static final int NEEDS_CALLEE = 1 << 26; /** extension callsite flags mask */ public static final int EXTENSION_CALLSITE_FLAGS = IS_PRINT_PARSE | @@ -285,8 +280,8 @@ /** Does this function potentially need "arguments"? Note that this is not a full test, as further negative check of REDEFINES_ARGS is needed. */ private static final int MAYBE_NEEDS_ARGUMENTS = USES_ARGUMENTS | HAS_EVAL; - /** Does this function need the parent scope? It needs it if either it or its descendants use variables from it, or have a deep eval. */ - private static final int NEEDS_PARENT_SCOPE = USES_ANCESTOR_SCOPE | HAS_DEEP_EVAL; + /** Does this function need the parent scope? It needs it if either it or its descendants use variables from it, or have a deep eval, or it's the program. */ + public static final int NEEDS_PARENT_SCOPE = USES_ANCESTOR_SCOPE | HAS_DEEP_EVAL | IS_PROGRAM; /** What is the return type of this function? */ private Type returnType = Type.UNKNOWN; @@ -358,7 +353,8 @@ final Block body, final List<IdentNode> parameters, final int thisProperties, - final Class<?> rootClass) { + final Class<?> rootClass, + final Source source, Namespace namespace) { super(functionNode); this.endParserState = endParserState; @@ -373,11 +369,11 @@ this.parameters = parameters; this.thisProperties = thisProperties; this.rootClass = rootClass; + this.source = source; + this.namespace = namespace; // the fields below never change - they are final and assigned in constructor - this.source = functionNode.source; this.ident = functionNode.ident; - this.namespace = functionNode.namespace; this.kind = functionNode.kind; this.firstToken = functionNode.firstToken; } @@ -443,6 +439,39 @@ } /** + * Sets the source and namespace for this function. It can only set a non-null source and namespace for a function + * that currently has both a null source and a null namespace. This is used to re-set the source and namespace for + * a deserialized function node. + * @param source the source for the function. + * @param namespace the namespace for the function + * @return a new function node with the set source and namespace + * @throws IllegalArgumentException if the specified source or namespace is null + * @throws IllegalStateException if the function already has either a source or namespace set. + */ + public FunctionNode initializeDeserialized(final Source source, final Namespace namespace) { + if (source == null || namespace == null) { + throw new IllegalArgumentException(); + } else if (this.source == source && this.namespace == namespace) { + return this; + } else if (this.source != null || this.namespace != null) { + throw new IllegalStateException(); + } + return new FunctionNode( + this, + lastToken, + endParserState, + flags, + name, + returnType, + compileUnit, + compilationState, + body, + parameters, + thisProperties, + rootClass, source, namespace); + } + + /** * Get the unique ID for this function within the script file. * @return the id */ @@ -543,6 +572,28 @@ } final EnumSet<CompilationState> newState = EnumSet.copyOf(this.compilationState); newState.add(state); + return setCompilationState(lc, newState); + } + + /** + * Copy a compilation state from an original function to this function. Used when creating synthetic + * function nodes by the splitter. + * + * @param lc lexical context + * @param original the original function node to copy compilation state from + * @return function node or a new one if state was changed + */ + public FunctionNode copyCompilationState(final LexicalContext lc, final FunctionNode original) { + final EnumSet<CompilationState> origState = original.compilationState; + if (!AssertsEnabled.assertsEnabled() || this.compilationState.containsAll(origState)) { + return this; + } + final EnumSet<CompilationState> newState = EnumSet.copyOf(this.compilationState); + newState.addAll(origState); + return setCompilationState(lc, newState); + } + + private FunctionNode setCompilationState(final LexicalContext lc, final EnumSet<CompilationState> compilationState) { return Node.replaceInLexicalContext( lc, this, @@ -554,13 +605,14 @@ name, returnType, compileUnit, - newState, + compilationState, body, parameters, thisProperties, - rootClass)); + rootClass, source, namespace)); } + /** * Create a unique name in the namespace of this FunctionNode * @param base prefix for name @@ -630,7 +682,7 @@ body, parameters, thisProperties, - rootClass)); + rootClass, source, namespace)); } @Override @@ -705,18 +757,11 @@ * @return true if the function's generated Java method needs a {@code callee} parameter. */ public boolean needsCallee() { + // NOTE: we only need isSplit() here to ensure that :scope can never drop below slot 2 for splitting array units. return needsParentScope() || usesSelfSymbol() || isSplit() || (needsArguments() && !isStrict()) || hasOptimisticApplyToCall(); } /** - * Check if this function uses the return symbol - * @return true if uses the return symbol - */ - public boolean usesReturnSymbol() { - return isProgram() || isSplit() || getFlag(USES_RETURN_SYMBOL); - } - - /** * Return {@code true} if this function makes use of the {@code this} object. * * @return true if function uses {@code this} object @@ -778,7 +823,7 @@ body, parameters, thisProperties, - rootClass)); + rootClass, source, namespace)); } /** @@ -846,7 +891,7 @@ * @return true if the function needs parent scope. */ public boolean needsParentScope() { - return getFlag(NEEDS_PARENT_SCOPE) || isProgram(); + return getFlag(NEEDS_PARENT_SCOPE); } /** @@ -874,7 +919,7 @@ body, parameters, thisProperties, - rootClass)); + rootClass, source, namespace)); } /** @@ -951,7 +996,7 @@ body, parameters, thisProperties, - rootClass)); + rootClass, source, namespace)); } /** @@ -965,9 +1010,9 @@ } /** - * Checks if this function is a sub-function generated by splitting a larger one + * Checks if this function is split into several smaller fragments. * - * @return true if this function is split from a larger one + * @return true if this function is split into several smaller fragments. */ public boolean isSplit() { return getFlag(IS_SPLIT); @@ -1017,7 +1062,7 @@ body, parameters, thisProperties, - rootClass)); + rootClass, source, namespace)); } /** @@ -1096,7 +1141,7 @@ body, parameters, thisProperties, - rootClass + rootClass, source, namespace )); } @@ -1144,7 +1189,7 @@ body, parameters, thisProperties, - rootClass)); + rootClass, source, namespace)); } /** @@ -1200,6 +1245,6 @@ body, parameters, thisProperties, - rootClass)); + rootClass, source, namespace)); } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/GetSplitState.java Thu Oct 23 13:45:22 2014 -0700 @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.nashorn.internal.ir; + +import java.util.function.Function; +import jdk.nashorn.internal.codegen.CompilerConstants; +import jdk.nashorn.internal.codegen.types.Type; +import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.runtime.Scope; + +/** + * Synthetic AST node that represents loading of the scope object and invocation of the {@link Scope#getSplitState()} + * method on it. It has no JavaScript source representation and only occurs in synthetic functions created by + * the split-into-functions transformation. + */ +public final class GetSplitState extends Expression { + private static final long serialVersionUID = 1L; + + /** The sole instance of this AST node. */ + public final static GetSplitState INSTANCE = new GetSplitState(); + + private GetSplitState() { + super(NO_TOKEN, NO_FINISH); + } + + @Override + public Type getType(final Function<Symbol, Type> localVariableTypes) { + return Type.INT; + } + + @Override + public Node accept(final NodeVisitor<? extends LexicalContext> visitor) { + return visitor.enterGetSplitState(this) ? visitor.leaveGetSplitState(this) : this; + } + + @Override + public void toString(final StringBuilder sb, final boolean printType) { + if (printType) { + sb.append("{I}"); + } + sb.append(CompilerConstants.SCOPE.symbolName()).append('.').append(Scope.GET_SPLIT_STATE.name()).append("()"); + } + + private Object readResolve() { + return INSTANCE; + } +}
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/IdentNode.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/IdentNode.java Thu Oct 23 13:45:22 2014 -0700 @@ -42,6 +42,8 @@ */ @Immutable public final class IdentNode extends Expression implements PropertyKey, FunctionCall, Optimistic, JoinPredecessor { + private static final long serialVersionUID = 1L; + private static final int PROPERTY_NAME = 1 << 0; private static final int INITIALIZED_HERE = 1 << 1; private static final int FUNCTION = 1 << 2;
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/IfNode.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/IfNode.java Thu Oct 23 13:45:22 2014 -0700 @@ -33,6 +33,8 @@ */ @Immutable public final class IfNode extends Statement implements JoinPredecessor { + private static final long serialVersionUID = 1L; + /** Test expression. */ private final Expression test;
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/IndexNode.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/IndexNode.java Thu Oct 23 13:45:22 2014 -0700 @@ -33,6 +33,8 @@ */ @Immutable public final class IndexNode extends BaseNode { + private static final long serialVersionUID = 1L; + /** Property index. */ private final Expression index;
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/JoinPredecessorExpression.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/JoinPredecessorExpression.java Thu Oct 23 13:45:22 2014 -0700 @@ -33,6 +33,7 @@ * A wrapper for an expression that is in a position to be a join predecessor. */ public class JoinPredecessorExpression extends Expression implements JoinPredecessor { + private static final long serialVersionUID = 1L; private final Expression expression; private final LocalVariableConversion conversion;
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/JumpStatement.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/JumpStatement.java Thu Oct 23 13:45:22 2014 -0700 @@ -31,6 +31,7 @@ * Common base class for jump statements (e.g. {@code break} and {@code continue}). */ public abstract class JumpStatement extends Statement implements JoinPredecessor { + private static final long serialVersionUID = 1L; private final String labelName; private final LocalVariableConversion conversion;
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LabelNode.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LabelNode.java Thu Oct 23 13:45:22 2014 -0700 @@ -35,6 +35,8 @@ */ @Immutable public final class LabelNode extends LexicalContextStatement implements JoinPredecessor { + private static final long serialVersionUID = 1L; + /** Label ident. */ private final String labelName;
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LexicalContext.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LexicalContext.java Thu Oct 23 13:45:22 2014 -0700 @@ -440,6 +440,14 @@ } /** + * Is the topmost lexical context element body of a SplitNode? + * @return true if it's the body of a split node. + */ + public boolean isSplitBody() { + return sp >= 2 && stack[sp - 1] instanceof Block && stack[sp - 2] instanceof SplitNode; + } + + /** * Get the parent function for a function in the lexical context * @param functionNode function for which to get parent * @return parent function of functionNode or null if none (e.g. if functionNode is the program) @@ -472,9 +480,6 @@ final LexicalContextNode node = iter.next(); if (node == until) { break; - } else if (node instanceof SplitNode) { - // Don't bother popping scopes if we're going to do a return from a split method anyway. - return 0; } assert !(node instanceof FunctionNode); // Can't go outside current function if (node instanceof WithNode || node instanceof Block && ((Block)node).needsScope()) {
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LexicalContextExpression.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LexicalContextExpression.java Thu Oct 23 13:45:22 2014 -0700 @@ -28,6 +28,7 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor; abstract class LexicalContextExpression extends Expression implements LexicalContextNode { + private static final long serialVersionUID = 1L; LexicalContextExpression(final LexicalContextExpression expr) { super(expr);
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LexicalContextStatement.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LexicalContextStatement.java Thu Oct 23 13:45:22 2014 -0700 @@ -28,6 +28,8 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor; abstract class LexicalContextStatement extends Statement implements LexicalContextNode { + private static final long serialVersionUID = 1L; + /** * Constructor *
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LiteralNode.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LiteralNode.java Thu Oct 23 13:45:22 2014 -0700 @@ -25,6 +25,7 @@ package jdk.nashorn.internal.ir; +import java.io.Serializable; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -49,6 +50,8 @@ */ @Immutable public abstract class LiteralNode<T> extends Expression implements PropertyKey { + private static final long serialVersionUID = 1L; + /** Literal value */ protected final T value; @@ -270,6 +273,8 @@ * @param <T> the literal type */ public static class PrimitiveLiteralNode<T> extends LiteralNode<T> { + private static final long serialVersionUID = 1L; + private PrimitiveLiteralNode(final long token, final int finish, final T value) { super(token, finish, value); } @@ -304,6 +309,7 @@ @Immutable private static final class BooleanLiteralNode extends PrimitiveLiteralNode<Boolean> { + private static final long serialVersionUID = 1L; private BooleanLiteralNode(final long token, final int finish, final boolean value) { super(Token.recast(token, value ? TokenType.TRUE : TokenType.FALSE), finish, value); @@ -356,6 +362,7 @@ @Immutable private static final class NumberLiteralNode extends PrimitiveLiteralNode<Number> { + private static final long serialVersionUID = 1L; private final Type type = numberGetType(value); @@ -418,6 +425,8 @@ } private static class UndefinedLiteralNode extends PrimitiveLiteralNode<Undefined> { + private static final long serialVersionUID = 1L; + private UndefinedLiteralNode(final long token, final int finish) { super(Token.recast(token, TokenType.OBJECT), finish, ScriptRuntime.UNDEFINED); } @@ -454,6 +463,8 @@ @Immutable private static class StringLiteralNode extends PrimitiveLiteralNode<String> { + private static final long serialVersionUID = 1L; + private StringLiteralNode(final long token, final int finish, final String value) { super(Token.recast(token, TokenType.STRING), finish, value); } @@ -497,6 +508,8 @@ @Immutable private static class LexerTokenLiteralNode extends LiteralNode<LexerToken> { + private static final long serialVersionUID = 1L; + private LexerTokenLiteralNode(final long token, final int finish, final LexerToken value) { super(Token.recast(token, TokenType.STRING), finish, value); //TODO is string the correct token type here? } @@ -560,6 +573,7 @@ } private static final class NullLiteralNode extends PrimitiveLiteralNode<Object> { + private static final long serialVersionUID = 1L; private NullLiteralNode(final long token, final int finish) { super(Token.recast(token, TokenType.OBJECT), finish, null); @@ -590,6 +604,7 @@ */ @Immutable public static final class ArrayLiteralNode extends LiteralNode<Expression[]> implements LexicalContextNode { + private static final long serialVersionUID = 1L; /** Array element type. */ private final Type elementType; @@ -607,7 +622,9 @@ * An ArrayUnit is a range in an ArrayLiteral. ArrayLiterals can * be split if they are too large, for bytecode generation reasons */ - public static final class ArrayUnit implements CompileUnitHolder { + public static final class ArrayUnit implements CompileUnitHolder, Serializable { + private static final long serialVersionUID = 1L; + /** Compile unit associated with the postsets range. */ private final CompileUnit compileUnit; @@ -655,13 +672,13 @@ private static final class ArrayLiteralInitializer { static ArrayLiteralNode initialize(final ArrayLiteralNode node) { - final Type elementType = computeElementType(node.value, node.elementType); + final Type elementType = computeElementType(node.value); final int[] postsets = computePostsets(node.value); final Object presets = computePresets(node.value, elementType, postsets); return new ArrayLiteralNode(node, node.value, elementType, postsets, presets, node.units); } - private static Type computeElementType(final Expression[] value, final Type elementType) { + private static Type computeElementType(final Expression[] value) { Type widestElementType = Type.INT; for (final Expression elem : value) {
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LoopNode.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LoopNode.java Thu Oct 23 13:45:22 2014 -0700 @@ -34,6 +34,8 @@ * A loop node, for example a while node, do while node or for node */ public abstract class LoopNode extends BreakableStatement { + private static final long serialVersionUID = 1L; + /** loop continue label. */ protected final Label continueLabel;
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Node.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Node.java Thu Oct 23 13:45:22 2014 -0700 @@ -25,6 +25,7 @@ package jdk.nashorn.internal.ir; +import java.io.Serializable; import java.util.ArrayList; import java.util.List; import jdk.nashorn.internal.ir.visitor.NodeVisitor; @@ -34,7 +35,18 @@ /** * Nodes are used to compose Abstract Syntax Trees. */ -public abstract class Node implements Cloneable { +public abstract class Node implements Cloneable, Serializable { + private static final long serialVersionUID = 1L; + + /** Constant used for synthetic AST nodes that have no line number. */ + public static final int NO_LINE_NUMBER = -1; + + /** Constant used for synthetic AST nodes that have no token. */ + public static final long NO_TOKEN = 0L; + + /** Constant used for synthetic AST nodes that have no finish. */ + public static final int NO_FINISH = 0; + /** Start of source range. */ protected final int start;
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ObjectNode.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ObjectNode.java Thu Oct 23 13:45:22 2014 -0700 @@ -37,6 +37,7 @@ */ @Immutable public final class ObjectNode extends Expression { + private static final long serialVersionUID = 1L; /** Literal elements. */ private final List<PropertyNode> elements;
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/PropertyNode.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/PropertyNode.java Thu Oct 23 13:45:22 2014 -0700 @@ -33,6 +33,7 @@ */ @Immutable public final class PropertyNode extends Node { + private static final long serialVersionUID = 1L; /** Property key. */ private final PropertyKey key;
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ReturnNode.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ReturnNode.java Thu Oct 23 13:45:22 2014 -0700 @@ -36,6 +36,8 @@ */ @Immutable public class ReturnNode extends Statement { + private static final long serialVersionUID = 1L; + /** Optional expression. */ private final Expression expression;
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/RuntimeNode.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/RuntimeNode.java Thu Oct 23 13:45:22 2014 -0700 @@ -42,6 +42,7 @@ */ @Immutable public class RuntimeNode extends Expression implements Optimistic { + private static final long serialVersionUID = 1L; /** * Request enum used for meta-information about the runtime request
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/SetSplitState.java Thu Oct 23 13:45:22 2014 -0700 @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.nashorn.internal.ir; + +import jdk.nashorn.internal.codegen.CompilerConstants; +import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.runtime.Scope; + +/** + * Synthetic AST node that represents loading of the scope object and invocation of the {@link Scope#setSplitState(int)} + * method on it. It has no JavaScript source representation and only occurs in synthetic functions created by + * the split-into-functions transformation. + */ +public final class SetSplitState extends Statement { + private static final long serialVersionUID = 1L; + + private final int state; + + /** + * Creates a new split state setter + * @param state the state to set + * @param lineNumber the line number where it is inserted + */ + public SetSplitState(final int state, final int lineNumber) { + super(lineNumber, NO_TOKEN, NO_FINISH); + this.state = state; + } + + /** + * Returns the state this setter sets. + * @return the state this setter sets. + */ + public int getState() { + return state; + } + + @Override + public Node accept(final NodeVisitor<? extends LexicalContext> visitor) { + return visitor.enterSetSplitState(this) ? visitor.leaveSetSplitState(this) : this; + } + + @Override + public void toString(final StringBuilder sb, final boolean printType) { + sb.append(CompilerConstants.SCOPE.symbolName()).append('.').append(Scope.SET_SPLIT_STATE.name()) + .append('(').append(state).append(");"); + } +}
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/SplitNode.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/SplitNode.java Thu Oct 23 13:45:22 2014 -0700 @@ -25,13 +25,10 @@ package jdk.nashorn.internal.ir; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.io.IOException; +import java.io.NotSerializableException; +import java.io.ObjectOutputStream; import jdk.nashorn.internal.codegen.CompileUnit; -import jdk.nashorn.internal.codegen.Label; import jdk.nashorn.internal.ir.annotations.Immutable; import jdk.nashorn.internal.ir.visitor.NodeVisitor; @@ -39,7 +36,9 @@ * Node indicating code is split across classes. */ @Immutable -public class SplitNode extends LexicalContextStatement implements Labels, CompileUnitHolder { +public class SplitNode extends LexicalContextStatement implements CompileUnitHolder { + private static final long serialVersionUID = 1L; + /** Split node method name. */ private final String name; @@ -49,8 +48,6 @@ /** Body of split code. */ private final Block body; - private Map<Label, JoinPredecessor> jumps; - /** * Constructor * @@ -65,19 +62,18 @@ this.compileUnit = compileUnit; } - private SplitNode(final SplitNode splitNode, final Block body, final CompileUnit compileUnit, final Map<Label, JoinPredecessor> jumps) { + private SplitNode(final SplitNode splitNode, final Block body, final CompileUnit compileUnit) { super(splitNode); this.name = splitNode.name; this.body = body; this.compileUnit = compileUnit; - this.jumps = jumps; } /** * Get the body for this split node - i.e. the actual code it encloses * @return body for split node */ - public Node getBody() { + public Block getBody() { return body; } @@ -85,7 +81,7 @@ if (this.body == body) { return this; } - return Node.replaceInLexicalContext(lc, this, new SplitNode(this, body, compileUnit, jumps)); + return Node.replaceInLexicalContext(lc, this, new SplitNode(this, body, compileUnit)); } @Override @@ -131,33 +127,12 @@ if (this.compileUnit == compileUnit) { return this; } - return Node.replaceInLexicalContext(lc, this, new SplitNode(this, body, compileUnit, jumps)); + return Node.replaceInLexicalContext(lc, this, new SplitNode(this, body, compileUnit)); } - /** - * Adds a jump that crosses this split node's boundary (it originates within the split node, and goes to a target - * outside of it). - * @param jumpOrigin the join predecessor that's the origin of the jump - * @param targetLabel the label that's the target of the jump. - */ - public void addJump(final JoinPredecessor jumpOrigin, final Label targetLabel) { - if (jumps == null) { - jumps = new HashMap<>(); - } - jumps.put(targetLabel, jumpOrigin); - } - - /** - * Returns the jump origin within this split node for a target. - * @param targetLabel the target for which a jump origin is sought. - * @return the jump origin, or null. - */ - public JoinPredecessor getJumpOrigin(final Label targetLabel) { - return jumps == null ? null : jumps.get(targetLabel); - } - - @Override - public List<Label> getLabels() { - return Collections.unmodifiableList(new ArrayList<>(jumps.keySet())); + private void writeObject(final ObjectOutputStream out) throws IOException { + // We are only serializing the AST after we run SplitIntoFunctions; no SplitNodes can remain for the + // serialization. + throw new NotSerializableException(getClass().getName()); } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/SplitReturn.java Thu Oct 23 13:45:22 2014 -0700 @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.nashorn.internal.ir; + +import jdk.nashorn.internal.ir.visitor.NodeVisitor; + +/** + * Synthetic AST node that represents return from a split fragment of a split function for control flow reasons (break + * or continue into a target outside the current fragment). It has no JavaScript source representation and only occurs + * in synthetic functions created by the split-into-functions transformation. It is different from a return node in + * that the return value is irrelevant, and doesn't affect the function's return type calculation. + */ +public final class SplitReturn extends Statement { + private static final long serialVersionUID = 1L; + + /** The sole instance of this AST node. */ + public static final SplitReturn INSTANCE = new SplitReturn(); + + private SplitReturn() { + super(NO_LINE_NUMBER, NO_TOKEN, NO_FINISH); + } + + @Override + public boolean isTerminal() { + return true; + } + + @Override + public Node accept(final NodeVisitor<? extends LexicalContext> visitor) { + return visitor.enterSplitReturn(this) ? visitor.leaveSplitReturn(this) : this; + } + + @Override + public void toString(StringBuilder sb, boolean printType) { + sb.append(":splitreturn;"); + } + + private Object readResolve() { + return INSTANCE; + } +}
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Statement.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Statement.java Thu Oct 23 13:45:22 2014 -0700 @@ -31,6 +31,7 @@ * location information is the Statement */ public abstract class Statement extends Node implements Terminal { + private static final long serialVersionUID = 1L; private final int lineNumber;
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/SwitchNode.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/SwitchNode.java Thu Oct 23 13:45:22 2014 -0700 @@ -37,6 +37,8 @@ */ @Immutable public final class SwitchNode extends BreakableStatement { + private static final long serialVersionUID = 1L; + /** Switch expression. */ private final Expression expression;
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Symbol.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Symbol.java Thu Oct 23 13:45:22 2014 -0700 @@ -97,7 +97,7 @@ private int firstSlot = -1; /** Field number in scope or property; array index in varargs when not using arguments object. */ - private int fieldIndex; + private int fieldIndex = -1; /** Number of times this symbol is used in code */ private int useCount; @@ -135,28 +135,15 @@ * * @param name name of symbol * @param flags symbol flags - * @param slot bytecode slot for this symbol */ - protected Symbol(final String name, final int flags, final int slot) { + public Symbol(final String name, final int flags) { this.name = name; this.flags = flags; - this.firstSlot = slot; - this.fieldIndex = -1; if(shouldTrace()) { trace("CREATE SYMBOL " + name); } } - /** - * Constructor - * - * @param name name of symbol - * @param flags symbol flags - */ - public Symbol(final String name, final int flags) { - this(name, flags, -1); - } - private static String align(final String string, final int max) { final StringBuilder sb = new StringBuilder(); sb.append(string.substring(0, Math.min(string.length(), max)));
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/TernaryNode.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/TernaryNode.java Thu Oct 23 13:45:22 2014 -0700 @@ -37,6 +37,8 @@ */ @Immutable public final class TernaryNode extends Expression { + private static final long serialVersionUID = 1L; + private final Expression test; private final JoinPredecessorExpression trueExpr; private final JoinPredecessorExpression falseExpr;
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ThrowNode.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ThrowNode.java Thu Oct 23 13:45:22 2014 -0700 @@ -33,6 +33,8 @@ */ @Immutable public final class ThrowNode extends Statement implements JoinPredecessor { + private static final long serialVersionUID = 1L; + /** Exception expression. */ private final Expression expression;
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/TryNode.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/TryNode.java Thu Oct 23 13:45:22 2014 -0700 @@ -36,6 +36,8 @@ */ @Immutable public final class TryNode extends Statement implements JoinPredecessor { + private static final long serialVersionUID = 1L; + /** Try statements. */ private final Block body;
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/UnaryNode.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/UnaryNode.java Thu Oct 23 13:45:22 2014 -0700 @@ -46,6 +46,8 @@ */ @Immutable public final class UnaryNode extends Expression implements Assignment<Expression>, Optimistic { + private static final long serialVersionUID = 1L; + /** Right hand side argument. */ private final Expression expression;
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/VarNode.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/VarNode.java Thu Oct 23 13:45:22 2014 -0700 @@ -34,6 +34,8 @@ */ @Immutable public final class VarNode extends Statement implements Assignment<IdentNode> { + private static final long serialVersionUID = 1L; + /** Var name. */ private final IdentNode name;
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/WhileNode.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/WhileNode.java Thu Oct 23 13:45:22 2014 -0700 @@ -34,6 +34,8 @@ */ @Immutable public final class WhileNode extends LoopNode { + private static final long serialVersionUID = 1L; + /** is this a do while node ? */ private final boolean isDoWhile;
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/WithNode.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/WithNode.java Thu Oct 23 13:45:22 2014 -0700 @@ -33,6 +33,8 @@ */ @Immutable public final class WithNode extends LexicalContextStatement { + private static final long serialVersionUID = 1L; + /** This expression. */ private final Expression expression;
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/visitor/NodeVisitor.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/visitor/NodeVisitor.java Thu Oct 23 13:45:22 2014 -0700 @@ -38,6 +38,7 @@ import jdk.nashorn.internal.ir.ExpressionStatement; import jdk.nashorn.internal.ir.ForNode; import jdk.nashorn.internal.ir.FunctionNode; +import jdk.nashorn.internal.ir.GetSplitState; import jdk.nashorn.internal.ir.IdentNode; import jdk.nashorn.internal.ir.IfNode; import jdk.nashorn.internal.ir.IndexNode; @@ -50,7 +51,9 @@ import jdk.nashorn.internal.ir.PropertyNode; import jdk.nashorn.internal.ir.ReturnNode; import jdk.nashorn.internal.ir.RuntimeNode; +import jdk.nashorn.internal.ir.SetSplitState; import jdk.nashorn.internal.ir.SplitNode; +import jdk.nashorn.internal.ir.SplitReturn; import jdk.nashorn.internal.ir.SwitchNode; import jdk.nashorn.internal.ir.TernaryNode; import jdk.nashorn.internal.ir.ThrowNode; @@ -390,6 +393,26 @@ } /** + * Callback for entering a {@link GetSplitState}. + * + * @param getSplitState the get split state expression + * @return true if traversal should continue and node children be traversed, false otherwise + */ + public boolean enterGetSplitState(final GetSplitState getSplitState) { + return enterDefault(getSplitState); + } + + /** + * Callback for leaving a {@link GetSplitState}. + * + * @param getSplitState the get split state expression + * @return processed node, which will replace the original one, or the original node + */ + public Node leaveGetSplitState(final GetSplitState getSplitState) { + return leaveDefault(getSplitState); + } + + /** * Callback for entering an IdentNode * * @param identNode the node @@ -570,6 +593,26 @@ } /** + * Callback for entering a {@link SetSplitState}. + * + * @param setSplitState the set split state statement + * @return true if traversal should continue and node children be traversed, false otherwise + */ + public boolean enterSetSplitState(final SetSplitState setSplitState) { + return enterDefault(setSplitState); + } + + /** + * Callback for leaving a {@link SetSplitState}. + * + * @param setSplitState the set split state expression + * @return processed node, which will replace the original one, or the original node + */ + public Node leaveSetSplitState(final SetSplitState setSplitState) { + return leaveDefault(setSplitState); + } + + /** * Callback for entering a SplitNode * * @param splitNode the node @@ -590,6 +633,26 @@ } /** + * Callback for entering a SplitReturn + * + * @param splitReturn the node + * @return true if traversal should continue and node children be traversed, false otherwise + */ + public boolean enterSplitReturn(final SplitReturn splitReturn) { + return enterDefault(splitReturn); + } + + /** + * Callback for leaving a SplitReturn + * + * @param splitReturn the node + * @return processed node, which will replace the original one, or the original node + */ + public Node leaveSplitReturn(final SplitReturn splitReturn) { + return leaveDefault(splitReturn); + } + + /** * Callback for entering a SwitchNode * * @param switchNode the node
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/lookup/MethodHandleFactory.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/lookup/MethodHandleFactory.java Thu Oct 23 13:45:22 2014 -0700 @@ -48,7 +48,7 @@ /** * This class is abstraction for all method handle, switchpoint and method type * operations. This enables the functionality interface to be subclassed and - * intrumensted, as it has been proven vital to keep the number of method + * instrumented, as it has been proven vital to keep the number of method * handles in the system down. * * All operations of the above type should go through this class, and not
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeArray.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeArray.java Thu Oct 23 13:45:22 2014 -0700 @@ -92,9 +92,10 @@ private static final Object CALL_CMP = new Object(); private static final Object TO_LOCALE_STRING = new Object(); - private SwitchPoint lengthMadeNotWritableSwitchPoint; - private PushLinkLogic pushLinkLogic; - private PopLinkLogic popLinkLogic; + private SwitchPoint lengthMadeNotWritableSwitchPoint; + private PushLinkLogic pushLinkLogic; + private PopLinkLogic popLinkLogic; + private ConcatLinkLogic concatLinkLogic; /** * Index for the modification SwitchPoint that triggers when length @@ -130,7 +131,9 @@ this(ArrayData.allocate(array.length)); ArrayData arrayData = this.getArray(); - arrayData.ensure(array.length - 1); + if (array.length > 0) { + arrayData.ensure(array.length - 1); + } for (int index = 0; index < array.length; index++) { final Object value = array[index]; @@ -757,12 +760,86 @@ * ECMA 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ... ] ] ] ) * * @param self self reference + * @param arg argument + * @return resulting NativeArray + */ + @SpecializedFunction(linkLogic=ConcatLinkLogic.class) + public static NativeArray concat(final Object self, final int arg) { + final ContinuousArrayData newData = getContinuousArrayDataCCE(self, Integer.class).copy(); //get at least an integer data copy of this data + newData.fastPush(arg); //add an integer to its end + return new NativeArray(newData); + } + + /** + * ECMA 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ... ] ] ] ) + * + * @param self self reference + * @param arg argument + * @return resulting NativeArray + */ + @SpecializedFunction(linkLogic=ConcatLinkLogic.class) + public static NativeArray concat(final Object self, final long arg) { + final ContinuousArrayData newData = getContinuousArrayDataCCE(self, Long.class).copy(); //get at least a long array data copy of this data + newData.fastPush(arg); //add a long at the end + return new NativeArray(newData); + } + + /** + * ECMA 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ... ] ] ] ) + * + * @param self self reference + * @param arg argument + * @return resulting NativeArray + */ + @SpecializedFunction(linkLogic=ConcatLinkLogic.class) + public static NativeArray concat(final Object self, final double arg) { + final ContinuousArrayData newData = getContinuousArrayDataCCE(self, Double.class).copy(); //get at least a number array data copy of this data + newData.fastPush(arg); //add a double at the end + return new NativeArray(newData); + } + + /** + * ECMA 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ... ] ] ] ) + * + * @param self self reference + * @param arg argument + * @return resulting NativeArray + */ + @SpecializedFunction(linkLogic=ConcatLinkLogic.class) + public static NativeArray concat(final Object self, final Object arg) { + //arg is [NativeArray] of same type. + final ContinuousArrayData selfData = getContinuousArrayDataCCE(self); + final ContinuousArrayData newData; + + if (arg instanceof NativeArray) { + final ContinuousArrayData argData = (ContinuousArrayData)((NativeArray)arg).getArray(); + if (argData.isEmpty()) { + newData = selfData.copy(); + } else if (selfData.isEmpty()) { + newData = argData.copy(); + } else { + final Class<?> widestElementType = selfData.widest(argData).getBoxedElementType(); + newData = ((ContinuousArrayData)selfData.convert(widestElementType)).fastConcat((ContinuousArrayData)argData.convert(widestElementType)); + } + } else { + newData = getContinuousArrayDataCCE(self, Object.class).copy(); + newData.fastPush(arg); + } + + return new NativeArray(newData); + } + + /** + * ECMA 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ... ] ] ] ) + * + * @param self self reference * @param args arguments * @return resulting NativeArray */ @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) public static NativeArray concat(final Object self, final Object... args) { final ArrayList<Object> list = new ArrayList<>(); + concatToList(list, Global.toObject(self)); for (final Object obj : args) { @@ -1692,13 +1769,15 @@ return pushLinkLogic == null ? new PushLinkLogic(this) : pushLinkLogic; } else if (clazz == PopLinkLogic.class) { return popLinkLogic == null ? new PopLinkLogic(this) : pushLinkLogic; + } else if (clazz == ConcatLinkLogic.class) { + return concatLinkLogic == null ? new ConcatLinkLogic(this) : concatLinkLogic; } return null; } @Override public boolean hasPerInstanceAssumptions() { - return true; //length switchpoint + return true; //length writable switchpoint } /** @@ -1798,6 +1877,40 @@ } /** + * This is linker logic for optimistic concatenations + */ + private static final class ConcatLinkLogic extends ArrayLinkLogic { + private ConcatLinkLogic(final NativeArray array) { + super(array); + } + + @Override + public boolean canLink(final Object self, final CallSiteDescriptor desc, final LinkRequest request) { + final Object[] args = request.getArguments(); + + if (args.length != 3) { //single argument check + return false; + } + + final ContinuousArrayData selfData = getContinuousArrayData(self); + if (selfData == null) { + return false; + } + + final Object arg = args[2]; + //args[2] continuousarray or non arraydata, let past non array datas + if (arg instanceof NativeArray) { + final ContinuousArrayData argData = getContinuousArrayData(arg); + if (argData == null) { + return false; + } + } + + return true; + } + } + + /** * This is linker logic for optimistic pushes */ private static final class PushLinkLogic extends ArrayLinkLogic { @@ -1864,6 +1977,14 @@ throw new ClassCastException(); } + private static final ContinuousArrayData getContinuousArrayDataCCE(final Object self) { + try { + return (ContinuousArrayData)((NativeArray)self).getArray(); + } catch (final NullPointerException e) { + throw new ClassCastException(); + } + } + private static final ContinuousArrayData getContinuousArrayDataCCE(final Object self, final Class<?> elementType) { try { return (ContinuousArrayData)((NativeArray)self).getArray(elementType); //ensure element type can fit "elementType"
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeFloat32Array.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeFloat32Array.java Thu Oct 23 13:45:22 2014 -0700 @@ -90,6 +90,11 @@ } @Override + public Class<?> getBoxedElementType() { + return Double.class; + } + + @Override protected MethodHandle getGetElem() { return GET_ELEM; }
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeFloat64Array.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeFloat64Array.java Thu Oct 23 13:45:22 2014 -0700 @@ -99,6 +99,11 @@ return double.class; } + @Override + public Class<?> getBoxedElementType() { + return Double.class; + } + private double getElem(final int index) { try { return nb.get(index);
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeInt16Array.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeInt16Array.java Thu Oct 23 13:45:22 2014 -0700 @@ -100,6 +100,11 @@ return int.class; } + @Override + public Class<?> getBoxedElementType() { + return Integer.class; + } + private int getElem(final int index) { try { return nb.get(index);
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeInt32Array.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeInt32Array.java Thu Oct 23 13:45:22 2014 -0700 @@ -118,6 +118,11 @@ } @Override + public Class<?> getBoxedElementType() { + return Integer.class; + } + + @Override public int getInt(final int index) { return getElem(index); }
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeInt8Array.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeInt8Array.java Thu Oct 23 13:45:22 2014 -0700 @@ -98,6 +98,11 @@ return int.class; } + @Override + public Class<?> getBoxedElementType() { + return Integer.class; + } + private int getElem(final int index) { try { return nb.get(index);
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeObject.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeObject.java Thu Oct 23 13:45:22 2014 -0700 @@ -28,7 +28,6 @@ import static jdk.nashorn.internal.lookup.Lookup.MH; import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; - import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType;
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeUint16Array.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeUint16Array.java Thu Oct 23 13:45:22 2014 -0700 @@ -124,6 +124,11 @@ } @Override + public Class<?> getBoxedElementType() { + return Integer.class; + } + + @Override public int getInt(final int index) { return getElem(index); }
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeUint32Array.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeUint32Array.java Thu Oct 23 13:45:22 2014 -0700 @@ -133,6 +133,11 @@ } @Override + public Class<?> getBoxedElementType() { + return Integer.class; + } + + @Override public int getInt(final int index) { return (int)getLong(index); }
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeUint8Array.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeUint8Array.java Thu Oct 23 13:45:22 2014 -0700 @@ -124,6 +124,11 @@ } @Override + public Class<?> getBoxedElementType() { + return Integer.class; + } + + @Override public int getInt(final int index) { return getElem(index); }
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java Thu Oct 23 13:45:22 2014 -0700 @@ -103,6 +103,11 @@ return int.class; } + @Override + public Class<?> getBoxedElementType() { + return int.class; + } + private int getElem(final int index) { try { return nb.get(index) & 0xff;
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/JSONParser.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/JSONParser.java Thu Oct 23 13:45:22 2014 -0700 @@ -32,7 +32,6 @@ import static jdk.nashorn.internal.parser.TokenType.RBRACE; import static jdk.nashorn.internal.parser.TokenType.RBRACKET; import static jdk.nashorn.internal.parser.TokenType.STRING; - import java.util.ArrayList; import java.util.List; import jdk.nashorn.internal.ir.Expression;
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Lexer.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Lexer.java Thu Oct 23 13:45:22 2014 -0700 @@ -46,6 +46,7 @@ import static jdk.nashorn.internal.parser.TokenType.STRING; import static jdk.nashorn.internal.parser.TokenType.XML; +import java.io.Serializable; import jdk.nashorn.internal.runtime.ECMAErrors; import jdk.nashorn.internal.runtime.ErrorManager; import jdk.nashorn.internal.runtime.JSErrorType; @@ -1717,7 +1718,9 @@ * Helper class for Lexer tokens, e.g XML or RegExp tokens. * This is the abstract superclass */ - public static abstract class LexerToken { + public static abstract class LexerToken implements Serializable { + private static final long serialVersionUID = 1L; + private final String expression; /** @@ -1741,6 +1744,8 @@ * Temporary container for regular expressions. */ public static class RegexToken extends LexerToken { + private static final long serialVersionUID = 1L; + /** Options. */ private final String options; @@ -1773,6 +1778,7 @@ * Temporary container for XML expression. */ public static class XMLToken extends LexerToken { + private static final long serialVersionUID = 1L; /** * Constructor.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/AstDeserializer.java Thu Oct 23 13:45:22 2014 -0700 @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.nashorn.internal.runtime; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.util.zip.InflaterInputStream; +import jdk.nashorn.internal.ir.FunctionNode; + +/** + * This static utility class performs deserialization of FunctionNode ASTs from a byte array. + * The format is a standard Java serialization stream, deflated. + */ +final class AstDeserializer { + static FunctionNode deserialize(final byte[] serializedAst) { + try { + return (FunctionNode)new ObjectInputStream(new InflaterInputStream(new ByteArrayInputStream( + serializedAst))).readObject(); + } catch (final ClassNotFoundException | IOException e) { + // This is internal, can't happen + throw new AssertionError("Unexpected exception deserializing function", e); + } + } +}
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/CompiledFunction.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/CompiledFunction.java Thu Oct 23 13:45:22 2014 -0700 @@ -27,6 +27,7 @@ import static jdk.nashorn.internal.lookup.Lookup.MH; import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT; import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid; + import java.lang.invoke.CallSite; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; @@ -786,6 +787,7 @@ // isn't available, we'll use the old one bound into the call site. final OptimismInfo effectiveOptInfo = currentOptInfo != null ? currentOptInfo : oldOptInfo; FunctionNode fn = effectiveOptInfo.reparse(); + final boolean serialized = effectiveOptInfo.isSerialized(); final Compiler compiler = effectiveOptInfo.getCompiler(fn, callSiteType, re); //set to non rest-of if (!shouldRecompile) { @@ -793,17 +795,17 @@ // recompiled a deoptimized version for an inner invocation. // We still need to do the rest of from the beginning logRecompile("Rest-of compilation [STANDALONE] ", fn, callSiteType, effectiveOptInfo.invalidatedProgramPoints); - return restOfHandle(effectiveOptInfo, compiler.compile(fn, CompilationPhases.COMPILE_ALL_RESTOF), currentOptInfo != null); + return restOfHandle(effectiveOptInfo, compiler.compile(fn, serialized ? CompilationPhases.COMPILE_SERIALIZED_RESTOF : CompilationPhases.COMPILE_ALL_RESTOF), currentOptInfo != null); } logRecompile("Deoptimizing recompilation (up to bytecode) ", fn, callSiteType, effectiveOptInfo.invalidatedProgramPoints); - fn = compiler.compile(fn, CompilationPhases.COMPILE_UPTO_BYTECODE); + fn = compiler.compile(fn, serialized ? CompilationPhases.RECOMPILE_SERIALIZED_UPTO_BYTECODE : CompilationPhases.COMPILE_UPTO_BYTECODE); log.info("Reusable IR generated"); // compile the rest of the function, and install it log.info("Generating and installing bytecode from reusable IR..."); logRecompile("Rest-of compilation [CODE PIPELINE REUSE] ", fn, callSiteType, effectiveOptInfo.invalidatedProgramPoints); - final FunctionNode normalFn = compiler.compile(fn, CompilationPhases.COMPILE_FROM_BYTECODE); + final FunctionNode normalFn = compiler.compile(fn, CompilationPhases.GENERATE_BYTECODE_AND_INSTALL); if (effectiveOptInfo.data.usePersistentCodeCache()) { final RecompilableScriptFunctionData data = effectiveOptInfo.data; @@ -829,7 +831,7 @@ constructor = null; // Will be regenerated when needed log.info("Done: ", invoker); - final MethodHandle restOf = restOfHandle(effectiveOptInfo, compiler.compile(fn, CompilationPhases.COMPILE_FROM_BYTECODE_RESTOF), canBeDeoptimized); + final MethodHandle restOf = restOfHandle(effectiveOptInfo, compiler.compile(fn, CompilationPhases.GENERATE_BYTECODE_AND_INSTALL_RESTOF), canBeDeoptimized); // Note that we only adjust the switch point after we set the invoker/constructor. This is important. if (canBeDeoptimized) { @@ -921,6 +923,10 @@ FunctionNode reparse() { return data.reparse(); } + + boolean isSerialized() { + return data.isSerialized(); + } } @SuppressWarnings("unused")
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Context.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Context.java Thu Oct 23 13:45:22 2014 -0700 @@ -150,6 +150,13 @@ private final Context context; private final ScriptLoader loader; private final CodeSource codeSource; + private int usageCount = 0; + private int bytesDefined = 0; + + // We reuse this installer for 10 compilations or 200000 defined bytes. Usually the first condition + // will occur much earlier, the second is a safety measure for very large scripts/functions. + private final static int MAX_USAGES = 10; + private final static int MAX_BYTES_DEFINED = 200_000; private ContextCodeInstaller(final Context context, final ScriptLoader loader, final CodeSource codeSource) { this.context = context; @@ -168,6 +175,8 @@ @Override public Class<?> install(final String className, final byte[] bytecode) { + usageCount++; + bytesDefined += bytecode.length; final String binaryName = Compiler.binaryName(className); return loader.installClass(binaryName, bytecode, codeSource); } @@ -225,6 +234,10 @@ @Override public CodeInstaller<ScriptEnvironment> withNewLoader() { + // Reuse this installer if we're within our limits. + if (usageCount < MAX_USAGES && bytesDefined < MAX_BYTES_DEFINED) { + return this; + } return new ContextCodeInstaller(context, context.createNewLoader(), codeSource); }
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/JSType.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/JSType.java Thu Oct 23 13:45:22 2014 -0700 @@ -29,7 +29,6 @@ import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY; import static jdk.nashorn.internal.lookup.Lookup.MH; import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; - import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.reflect.Array; @@ -1776,6 +1775,23 @@ } /** + * Returns the boxed version of a primitive class + * @param clazz the class + * @return the boxed type of clazz, or unchanged if not primitive + */ + public static Class<?> getBoxedClass(final Class<?> clazz) { + if (clazz == int.class) { + return Integer.class; + } else if (clazz == long.class) { + return Long.class; + } else if (clazz == double.class) { + return Double.class; + } + assert !clazz.isPrimitive(); + return clazz; + } + + /** * Create a method handle constant of the correct primitive type * for a constant object * @param o object
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Thu Oct 23 13:45:22 2014 -0700 @@ -43,6 +43,7 @@ import jdk.nashorn.internal.codegen.Compiler.CompilationPhases; import jdk.nashorn.internal.codegen.CompilerConstants; import jdk.nashorn.internal.codegen.FunctionSignature; +import jdk.nashorn.internal.codegen.Namespace; import jdk.nashorn.internal.codegen.ObjectClassGenerator.AllocatorDescriptor; import jdk.nashorn.internal.codegen.OptimisticTypesPersistence; import jdk.nashorn.internal.codegen.TypeMap; @@ -79,6 +80,9 @@ /** Source from which FunctionNode was parsed. */ private transient Source source; + /** Serialized, compressed form of the AST. Used by split functions as they can't be reparsed from source. */ + private final byte[] serializedAst; + /** Token of this function within the source. */ private final long token; @@ -127,6 +131,7 @@ * @param nestedFunctions nested function map * @param externalScopeDepths external scope depths * @param internalSymbols internal symbols to method, defined in its scope + * @param serializedAst a serialized AST representation. Normally only used for split functions. */ public RecompilableScriptFunctionData( final FunctionNode functionNode, @@ -134,7 +139,8 @@ final AllocatorDescriptor allocationDescriptor, final Map<Integer, RecompilableScriptFunctionData> nestedFunctions, final Map<String, Integer> externalScopeDepths, - final Set<String> internalSymbols) { + final Set<String> internalSymbols, + final byte[] serializedAst) { super(functionName(functionNode), Math.min(functionNode.getParameters().size(), MAX_ARITY), @@ -158,6 +164,7 @@ nfn.setParent(this); } + this.serializedAst = serializedAst; createLogger(); } @@ -212,10 +219,7 @@ */ public int getExternalSymbolDepth(final String symbolName) { final Integer depth = externalScopeDepths.get(symbolName); - if (depth == null) { - return -1; - } - return depth; + return depth == null ? -1 : depth; } /** @@ -354,8 +358,15 @@ return allocationStrategy.allocate(map); } + boolean isSerialized() { + return serializedAst != null; + } + FunctionNode reparse() { - // NOTE: If we aren't recompiling the top-level program, we decrease functionNodeId 'cause we'll have a synthetic program node + if (isSerialized()) { + return deserialize(); + } + final int descPosition = Token.descPosition(token); final Context context = Context.getContextTrusted(); final Parser parser = new Parser( @@ -363,8 +374,10 @@ source, new Context.ThrowErrorManager(), isStrict(), + // source starts at line 0, so even though lineNumber is the correct declaration line, back off + // one to make it exclusive lineNumber - 1, - context.getLogger(Parser.class)); // source starts at line 0, so even though lineNumber is the correct declaration line, back off one to make it exclusive + context.getLogger(Parser.class)); if (getFunctionFlag(FunctionNode.IS_ANONYMOUS)) { parser.setFunctionName(functionName); @@ -378,6 +391,17 @@ return (isProgram() ? program : extractFunctionFromScript(program)).setName(null, functionName); } + private FunctionNode deserialize() { + final ScriptEnvironment env = installer.getOwner(); + final Timing timing = env._timing; + final long t1 = System.nanoTime(); + try { + return AstDeserializer.deserialize(serializedAst).initializeDeserialized(source, new Namespace(env.getNamespace())); + } finally { + timing.accumulateTime("'Deserialize'", System.nanoTime() - t1); + } + } + private boolean getFunctionFlag(final int flag) { return (functionFlags & flag) != 0; } @@ -486,7 +510,8 @@ final FunctionNode fn = reparse(); final Compiler compiler = getCompiler(fn, actualCallSiteType, runtimeScope); - final FunctionNode compiledFn = compiler.compile(fn, CompilationPhases.COMPILE_ALL); + final FunctionNode compiledFn = compiler.compile(fn, + isSerialized() ? CompilationPhases.COMPILE_ALL_SERIALIZED : CompilationPhases.COMPILE_ALL); if (persist && !compiledFn.getFlag(FunctionNode.HAS_APPLY_TO_CALL_SPECIALIZATION)) { compiler.persistClassInfo(cacheKey, compiledFn); @@ -606,7 +631,7 @@ MethodHandle lookupCodeMethod(final Class<?> codeClass, final MethodType targetType) { if (log.isEnabled()) { - log.info("Looking up ", DebugLogger.quote(name), " type=", targetType); + log.info("Looking up ", DebugLogger.quote(functionName), " type=", targetType); } return MH.findStatic(LOOKUP, codeClass, functionName, targetType); } @@ -817,6 +842,26 @@ return true; } + /** + * Restores the {@link #getFunctionFlags()} flags to a function node. During on-demand compilation, we might need + * to restore flags to a function node that was otherwise not subjected to a full compile pipeline (e.g. its parse + * was skipped, or it's a nested function of a deserialized function. + * @param lc current lexical context + * @param fn the function node to restore flags onto + * @return the transformed function node + */ + public FunctionNode restoreFlags(final LexicalContext lc, final FunctionNode fn) { + assert fn.getId() == functionNodeId; + FunctionNode newFn = fn.setFlags(lc, functionFlags); + // This compensates for missing markEval() in case the function contains an inner function + // that contains eval(), that now we didn't discover since we skipped the inner function. + if (newFn.hasNestedEval()) { + assert newFn.hasScopeBlock(); + newFn = newFn.setBody(lc, newFn.getBody().setNeedsScope(null)); + } + return newFn; + } + private void readObject(final java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); createLogger();
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptObject.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptObject.java Thu Oct 23 13:45:22 2014 -0700 @@ -692,8 +692,7 @@ assert isValidArrayIndex(index) : "invalid array index"; final long longIndex = ArrayIndex.toLongIndex(index); doesNotHaveEnsureDelete(longIndex, getArray().length(), false); - setArray(getArray().ensure(longIndex)); - setArray(getArray().set(index, value, false)); + setArray(getArray().ensure(longIndex).set(index,value, false)); } private void checkIntegerKey(final String key) { @@ -1462,9 +1461,8 @@ //invalidate any fast array setters final ArrayData array = getArray(); - if (array != null) { - array.invalidateSetters(); - } + assert array != null; + setArray(ArrayData.preventExtension(array)); return this; } @@ -2645,20 +2643,22 @@ * @param newLength new length to set */ public final void setLength(final long newLength) { - final long arrayLength = getArray().length(); - if (newLength == arrayLength) { - return; - } - - if (newLength > arrayLength) { - setArray(getArray().ensure(newLength - 1)); - if (getArray().canDelete(arrayLength, newLength - 1, false)) { - setArray(getArray().delete(arrayLength, newLength - 1)); - } - return; - } - - if (newLength < arrayLength) { + ArrayData data = getArray(); + final long arrayLength = data.length(); + if (newLength == arrayLength) { + return; + } + + if (newLength > arrayLength) { + data = data.ensure(newLength - 1); + if (data.canDelete(arrayLength, newLength - 1, false)) { + data = data.delete(arrayLength, newLength - 1); + } + setArray(data); + return; + } + + if (newLength < arrayLength) { long actualLength = newLength; // Check for numeric keys in property map and delete them or adjust length, depending on whether @@ -2680,8 +2680,8 @@ } } - setArray(getArray().shrink(actualLength)); - getArray().setLength(actualLength); + setArray(data.shrink(actualLength)); + data.setLength(actualLength); } } @@ -3194,8 +3194,9 @@ final int index = getArrayIndex(primitiveKey); if (isValidArrayIndex(index)) { - if (getArray().has(index)) { - setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); + final ArrayData data = getArray(); + if (data.has(index)) { + setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); } else { doesNotHave(index, value, callSiteFlags); } @@ -3213,8 +3214,9 @@ final int index = getArrayIndex(primitiveKey); if (isValidArrayIndex(index)) { - if (getArray().has(index)) { - setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); + final ArrayData data = getArray(); + if (data.has(index)) { + setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); } else { doesNotHave(index, value, callSiteFlags); } @@ -3232,8 +3234,9 @@ final int index = getArrayIndex(primitiveKey); if (isValidArrayIndex(index)) { - if (getArray().has(index)) { - setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); + final ArrayData data = getArray(); + if (data.has(index)) { + setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); } else { doesNotHave(index, value, callSiteFlags); } @@ -3251,8 +3254,9 @@ final int index = getArrayIndex(primitiveKey); if (isValidArrayIndex(index)) { - if (getArray().has(index)) { - setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); + final ArrayData data = getArray(); + if (data.has(index)) { + setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); } else { doesNotHave(index, value, callSiteFlags); } @@ -3269,8 +3273,9 @@ final int index = getArrayIndex(key); if (isValidArrayIndex(index)) { - if (getArray().has(index)) { - setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); + final ArrayData data = getArray(); + if (data.has(index)) { + setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); } else { doesNotHave(index, value, callSiteFlags); } @@ -3287,8 +3292,9 @@ final int index = getArrayIndex(key); if (isValidArrayIndex(index)) { - if (getArray().has(index)) { - setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); + final ArrayData data = getArray(); + if (data.has(index)) { + setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); } else { doesNotHave(index, value, callSiteFlags); } @@ -3305,8 +3311,9 @@ final int index = getArrayIndex(key); if (isValidArrayIndex(index)) { - if (getArray().has(index)) { - setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); + final ArrayData data = getArray(); + if (data.has(index)) { + setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); } else { doesNotHave(index, value, callSiteFlags); } @@ -3323,8 +3330,9 @@ final int index = getArrayIndex(key); if (isValidArrayIndex(index)) { - if (getArray().has(index)) { - setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); + final ArrayData data = getArray(); + if (data.has(index)) { + setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); } else { doesNotHave(index, value, callSiteFlags); } @@ -3341,8 +3349,9 @@ final int index = getArrayIndex(key); if (isValidArrayIndex(index)) { - if (getArray().has(index)) { - setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); + final ArrayData data = getArray(); + if (data.has(index)) { + setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); } else { doesNotHave(index, value, callSiteFlags); } @@ -3359,8 +3368,9 @@ final int index = getArrayIndex(key); if (isValidArrayIndex(index)) { - if (getArray().has(index)) { - setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); + final ArrayData data = getArray(); + if (data.has(index)) { + setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); } else { doesNotHave(index, value, callSiteFlags); } @@ -3377,8 +3387,9 @@ final int index = getArrayIndex(key); if (isValidArrayIndex(index)) { - if (getArray().has(index)) { - setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); + final ArrayData data = getArray(); + if (data.has(index)) { + setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); } else { doesNotHave(index, value, callSiteFlags); } @@ -3395,8 +3406,9 @@ final int index = getArrayIndex(key); if (isValidArrayIndex(index)) { - if (getArray().has(index)) { - setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); + final ArrayData data = getArray(); + if (data.has(index)) { + setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); } else { doesNotHave(index, value, callSiteFlags); } @@ -3413,7 +3425,8 @@ final int index = getArrayIndex(key); if (isValidArrayIndex(index)) { if (getArray().has(index)) { - setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); + final ArrayData data = getArray(); + setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); } else { doesNotHave(index, value, callSiteFlags); } @@ -3429,8 +3442,9 @@ final int index = getArrayIndex(key); if (isValidArrayIndex(index)) { - if (getArray().has(index)) { - setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); + final ArrayData data = getArray(); + if (data.has(index)) { + setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); } else { doesNotHave(index, value, callSiteFlags); } @@ -3447,8 +3461,9 @@ final int index = getArrayIndex(key); if (isValidArrayIndex(index)) { - if (getArray().has(index)) { - setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); + final ArrayData data = getArray(); + if (data.has(index)) { + setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); } else { doesNotHave(index, value, callSiteFlags); } @@ -3465,8 +3480,9 @@ final int index = getArrayIndex(key); if (isValidArrayIndex(index)) { - if (getArray().has(index)) { - setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); + final ArrayData data = getArray(); + if (data.has(index)) { + setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags))); } else { doesNotHave(index, value, callSiteFlags); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/AnyElements.java Thu Oct 23 13:45:22 2014 -0700 @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.nashorn.internal.runtime.arrays; + +/** + * Marker interface for any ContinuousArray with any elements + * Used for type checks that throw ClassCastExceptions and force relinks + * for fast NativeArray specializations of builtin methods + */ +public interface AnyElements { + /** + * Return a numeric weight of the element type - wider is higher + * @return element type weight + */ + public int getElementWeight(); +} \ No newline at end of file
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/ArrayData.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/ArrayData.java Thu Oct 23 13:45:22 2014 -0700 @@ -28,6 +28,7 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; +import java.lang.reflect.Array; import java.nio.ByteBuffer; import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.linker.GuardedInvocation; @@ -37,6 +38,7 @@ import jdk.nashorn.internal.objects.Global; import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.PropertyDescriptor; +import jdk.nashorn.internal.runtime.ScriptRuntime; import jdk.nashorn.internal.runtime.UnwarrantedOptimismException; /** @@ -49,10 +51,180 @@ /** Mask for getting a chunk */ protected static final int CHUNK_MASK = CHUNK_SIZE - 1; + /** Untouched data - still link callsites as IntArrayData, but expands to + * a proper ArrayData when we try to write to it */ + public static final ArrayData EMPTY_ARRAY = new UntouchedArrayData(); + /** * Immutable empty array to get ScriptObjects started. + * Use the same array and convert it to mutable as soon as it is modified */ - public static final ArrayData EMPTY_ARRAY = new NoTypeArrayData(); + private static class UntouchedArrayData extends ContinuousArrayData { + private UntouchedArrayData() { + this(0); + } + + private UntouchedArrayData(final int length) { + super(length); + } + + private ArrayData toRealArrayData() { + return toRealArrayData(0); + } + + private ArrayData toRealArrayData(final int index) { + final IntArrayData newData = new IntArrayData(index + 1); + if (index == 0) { + return newData; + } + return new DeletedRangeArrayFilter(newData, 0, index); + } + + @Override + public ContinuousArrayData copy() { + return new UntouchedArrayData((int)length); + } + + @Override + public Object asArrayOfType(final Class<?> componentType) { + return Array.newInstance(componentType, 0); + } + + @Override + public Object[] asObjectArray() { + return ScriptRuntime.EMPTY_ARRAY; + } + + @Override + public ArrayData ensure(final long safeIndex) { + if (safeIndex > 0L) { + return toRealArrayData((int)safeIndex).ensure(safeIndex); + } + return this; + } + + @Override + public ArrayData convert(final Class<?> type) { + return toRealArrayData(0).convert(type); + } + + @Override + public void shiftLeft(final int by) { + //nop, always empty or we wouldn't be of this class + } + + @Override + public ArrayData shiftRight(final int by) { + return this; //always empty or we wouldn't be of this class + } + + @Override + public ArrayData shrink(final long newLength) { + return this; + } + + @Override + public ArrayData set(final int index, final Object value, final boolean strict) { + return toRealArrayData(index).set(index, value, strict); + } + + @Override + public ArrayData set(final int index, final int value, final boolean strict) { + return toRealArrayData(index).set(index, value, strict); + } + + @Override + public ArrayData set(final int index, final long value, final boolean strict) { + return toRealArrayData(index).set(index, value, strict); + } + + @Override + public ArrayData set(final int index, final double value, final boolean strict) { + return toRealArrayData(index).set(index, value, strict); + } + + @Override + public int getInt(final int index) { + throw new ArrayIndexOutOfBoundsException(index); //empty + } + + @Override + public long getLong(final int index) { + throw new ArrayIndexOutOfBoundsException(index); //empty + } + + @Override + public double getDouble(final int index) { + throw new ArrayIndexOutOfBoundsException(index); //empty + } + + @Override + public Object getObject(final int index) { + throw new ArrayIndexOutOfBoundsException(index); //empty + } + + @Override + public boolean has(final int index) { + return false; //empty + } + + @Override + public ArrayData delete(final int index) { + return new DeletedRangeArrayFilter(this, index, index); + } + + @Override + public ArrayData delete(final long fromIndex, final long toIndex) { + return new DeletedRangeArrayFilter(this, fromIndex, toIndex); + } + + @Override + public Object pop() { + return ScriptRuntime.UNDEFINED; + } + + @Override + public ArrayData push(final boolean strict, final Object item) { + return toRealArrayData().push(strict, item); + } + + @Override + public ArrayData slice(final long from, final long to) { + return this; //empty + } + + @Override + public ContinuousArrayData fastConcat(final ContinuousArrayData otherData) { + return otherData.copy(); + } + + //no need to override fastPopInt, as the default behavior is to throw classcast exception so we + //can relink and return an undefined, this is the IntArrayData default behavior + @Override + public String toString() { + return getClass().getSimpleName(); + } + + @Override + public MethodHandle getElementGetter(final Class<?> returnType, final int programPoint) { + return null; + } + + @Override + public MethodHandle getElementSetter(final Class<?> elementType) { + return null; + } + + @Override + public Class<?> getElementType() { + return int.class; + } + + @Override + public Class<?> getBoxedElementType() { + return Integer.class; + } + }; /** * Length of the array data. Not necessarily length of the wrapped array. @@ -77,7 +249,7 @@ * Factory method for unspecified array - start as int * @return ArrayData */ - public static ArrayData initialArray() { + public final static ArrayData initialArray() { return new IntArrayData(); } @@ -92,24 +264,13 @@ throw new UnwarrantedOptimismException(data.getObject(index), programPoint); } - private static int alignUp(final int size) { - return size + CHUNK_SIZE - 1 & ~(CHUNK_SIZE - 1); - } - /** - * Generic invalidation hook for script object to have call sites to this array indexing - * relinked, e.g. when a native array is marked as non extensible + * Align an array size up to the nearest array chunk size + * @param size size required + * @return size given, always >= size */ - public void invalidateGetters() { - //subclass responsibility - } - - /** - * Generic invalidation hook for script object to have call sites to this array indexing - * relinked, e.g. when a native array is marked as non extensible - */ - public void invalidateSetters() { - //subclass responsibility + protected final static int alignUp(final int size) { + return size + CHUNK_SIZE - 1 & ~(CHUNK_SIZE - 1); } /** @@ -118,7 +279,7 @@ * @param length the initial length * @return ArrayData */ - public static ArrayData allocate(final int length) { + public static final ArrayData allocate(final int length) { if (length == 0) { return new IntArrayData(); } else if (length >= SparseArrayData.MAX_DENSE_LENGTH) { @@ -134,7 +295,7 @@ * @param array the array * @return ArrayData wrapping this array */ - public static ArrayData allocate(final Object array) { + public static final ArrayData allocate(final Object array) { final Class<?> clazz = array.getClass(); if (clazz == int[].class) { @@ -154,7 +315,7 @@ * @param array the array to use for initial elements * @return the ArrayData */ - public static ArrayData allocate(final int[] array) { + public static final ArrayData allocate(final int[] array) { return new IntArrayData(array, array.length); } @@ -164,7 +325,7 @@ * @param array the array to use for initial elements * @return the ArrayData */ - public static ArrayData allocate(final long[] array) { + public static final ArrayData allocate(final long[] array) { return new LongArrayData(array, array.length); } @@ -174,7 +335,7 @@ * @param array the array to use for initial elements * @return the ArrayData */ - public static ArrayData allocate(final double[] array) { + public static final ArrayData allocate(final double[] array) { return new NumberArrayData(array, array.length); } @@ -184,7 +345,7 @@ * @param array the array to use for initial elements * @return the ArrayData */ - public static ArrayData allocate(final Object[] array) { + public static final ArrayData allocate(final Object[] array) { return new ObjectArrayData(array, array.length); } @@ -194,7 +355,7 @@ * @param buf the nio ByteBuffer to wrap * @return the ArrayData */ - public static ArrayData allocate(final ByteBuffer buf) { + public static final ArrayData allocate(final ByteBuffer buf) { return new ByteBufferArrayData(buf); } @@ -204,7 +365,7 @@ * @param underlying the underlying ArrayData to wrap in the freeze filter * @return the frozen ArrayData */ - public static ArrayData freeze(final ArrayData underlying) { + public static final ArrayData freeze(final ArrayData underlying) { return new FrozenArrayFilter(underlying); } @@ -214,11 +375,21 @@ * @param underlying the underlying ArrayData to wrap in the seal filter * @return the sealed ArrayData */ - public static ArrayData seal(final ArrayData underlying) { + public static final ArrayData seal(final ArrayData underlying) { return new SealedArrayFilter(underlying); } /** + * Prevent this array from being extended + * + * @param underlying the underlying ArrayData to wrap in the non extensible filter + * @return new array data, filtered + */ + public static final ArrayData preventExtension(final ArrayData underlying) { + return new NonExtensibleArrayFilter(underlying); + } + + /** * Return the length of the array data. This may differ from the actual * length of the array this wraps as length may be set or gotten as any * other JavaScript Property @@ -728,5 +899,4 @@ public GuardedInvocation findFastSetIndexMethod(final Class<? extends ArrayData> clazz, final CallSiteDescriptor desc, final LinkRequest request) { // array, index, value return null; } - }
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/ContinuousArrayData.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/ContinuousArrayData.java Thu Oct 23 13:45:22 2014 -0700 @@ -1,5 +1,4 @@ /* - * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +29,6 @@ import static jdk.nashorn.internal.runtime.JSType.getAccessorTypeIndex; import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT; import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid; - import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; @@ -50,9 +48,6 @@ */ @Logger(name="arrays") public abstract class ContinuousArrayData extends ArrayData { - - private SwitchPoint sp; - /** * Constructor * @param length length (elementLength) @@ -61,18 +56,6 @@ super(length); } - private SwitchPoint ensureSwitchPointExists() { - if (sp == null){ - sp = new SwitchPoint(); - } - return sp; - } - - @Override - public void invalidateSetters() { - SwitchPoint.invalidateAll(new SwitchPoint[] { ensureSwitchPointExists() }); - } - /** * Check if we can put one more element at the end of this continous * array without reallocating, or if we are overwriting an already @@ -86,6 +69,14 @@ } /** + * Check if an arraydata is empty + * @return true if empty + */ + public boolean isEmpty() { + return length == 0L; + } + + /** * Return element getter for a certain type at a certain program point * @param returnType return type * @param programPoint program point @@ -109,13 +100,16 @@ * @param index index to check - currently only int indexes * @return index */ - protected int throwHas(final int index) { + protected final int throwHas(final int index) { if (!has(index)) { throw new ClassCastException(); } return index; } + @Override + public abstract ContinuousArrayData copy(); + /** * Returns the type used to store an element in this array * @return element type @@ -128,6 +122,25 @@ } /** + * Returns the boxed type of the type used to store an element in this array + * @return element type + */ + public abstract Class<?> getBoxedElementType(); + + /** + * Get the widest element type of two arrays. This can be done faster in subclasses, but + * this works for all ContinuousArrayDatas and for where more optimal checks haven't been + * implemented. + * + * @param otherData another ContinuousArrayData + * @return the widest boxed element type + */ + public ContinuousArrayData widest(final ContinuousArrayData otherData) { + final Class<?> elementType = getElementType(); + return Type.widest(elementType, otherData.getElementType()) == elementType ? this : otherData; + } + + /** * Look up a continuous array element getter * @param get getter, sometimes combined with a has check that throws CCE on failure for relink * @param returnType return type @@ -256,12 +269,7 @@ final Object[] args = request.getArguments(); final int index = (int)args[args.length - 2]; - //sp may be invalidated by e.g. preventExtensions before the first setter is linked - //then it is already created. otherwise, create it here to guard against future - //invalidations - ensureSwitchPointExists(); - - if (!sp.hasBeenInvalidated() && hasRoomFor(index)) { + if (hasRoomFor(index)) { MethodHandle setElement = getElementSetter(elementType); //Z(continuousarraydata, int, int), return true if successful if (setElement != null) { //else we are dealing with a wider type than supported by this callsite @@ -269,7 +277,7 @@ getArray = MH.asType(getArray, getArray.type().changeReturnType(getClass())); setElement = MH.filterArguments(setElement, 0, getArray); final MethodHandle guard = MH.insertArguments(FAST_ACCESS_GUARD, 0, clazz); - return new GuardedInvocation(setElement, guard, sp, ClassCastException.class); //CCE if not a scriptObject anymore + return new GuardedInvocation(setElement, guard, (SwitchPoint)null, ClassCastException.class); //CCE if not a scriptObject anymore } } } @@ -344,4 +352,13 @@ public Object fastPopObject() { throw new ClassCastException(String.valueOf(getClass())); //type is wrong, relink } + + /** + * Specialization - fast concat implementation + * @param otherData data to concat + * @return new arraydata + */ + public ContinuousArrayData fastConcat(final ContinuousArrayData otherData) { + throw new ClassCastException(String.valueOf(getClass()) + " != " + String.valueOf(otherData.getClass())); + } }
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/IntArrayData.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/IntArrayData.java Thu Oct 23 13:45:22 2014 -0700 @@ -26,7 +26,6 @@ package jdk.nashorn.internal.runtime.arrays; import static jdk.nashorn.internal.codegen.CompilerConstants.specialCall; - import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.util.Arrays; @@ -57,17 +56,32 @@ * @param array an int array * @param length a length, not necessarily array.length */ - IntArrayData(final int array[], final int length) { + IntArrayData(final int[] array, final int length) { super(length); - assert array.length >= length; + assert array == null || array.length >= length; this.array = array; } @Override - public Class<?> getElementType() { + public final Class<?> getElementType() { return int.class; } + @Override + public final Class<?> getBoxedElementType() { + return Integer.class; + } + + @Override + public final int getElementWeight() { + return 1; + } + + @Override + public final ContinuousArrayData widest(final ContinuousArrayData otherData) { + return otherData; + } + private static final MethodHandle HAS_GET_ELEM = specialCall(MethodHandles.lookup(), IntArrayData.class, "getElem", int.class, int.class).methodHandle(); private static final MethodHandle SET_ELEM = specialCall(MethodHandles.lookup(), IntArrayData.class, "setElem", void.class, int.class, int.class).methodHandle(); @@ -104,7 +118,7 @@ } @Override - public ArrayData copy() { + public IntArrayData copy() { return new IntArrayData(array.clone(), (int)length); } @@ -165,8 +179,7 @@ public ArrayData convert(final Class<?> type) { if (type == Integer.class) { return this; - } - if (type == Long.class) { + } else if (type == Long.class) { return convertToLong(); } else if (type == Double.class) { return convertToDouble(); @@ -209,8 +222,7 @@ @Override public ArrayData shrink(final long newLength) { - Arrays.fill(array, (int) newLength, array.length, 0); - + Arrays.fill(array, (int)newLength, array.length, 0); return this; } @@ -322,10 +334,7 @@ @Override public ArrayData slice(final long from, final long to) { - final long start = from < 0 ? from + length : from; - final long newLength = to - start; - - return new IntArrayData(Arrays.copyOfRange(array, (int)from, (int)to), (int)newLength); + return new IntArrayData(Arrays.copyOfRange(array, (int)from, (int)to), (int)(to - (from < 0 ? from + length : from))); } @Override @@ -347,7 +356,13 @@ throw new UnsupportedOperationException(); } final ArrayData returnValue = removed == 0 ? - EMPTY_ARRAY : new IntArrayData(Arrays.copyOfRange(array, start, start + removed), removed); + EMPTY_ARRAY : + new IntArrayData( + Arrays.copyOfRange( + array, + start, + start + removed), + removed); if (newLength != oldLength) { final int[] newArray; @@ -403,4 +418,26 @@ public Object fastPopObject() { return fastPopInt(); } + + @Override + public ContinuousArrayData fastConcat(final ContinuousArrayData otherData) { + final int otherLength = (int)otherData.length; + final int thisLength = (int)length; + assert otherLength > 0 && thisLength > 0; + + final int[] otherArray = ((IntArrayData)otherData).array; + final int newLength = otherLength + thisLength; + final int[] newArray = new int[ArrayData.alignUp(newLength)]; + + System.arraycopy(array, 0, newArray, 0, thisLength); + System.arraycopy(otherArray, 0, newArray, thisLength, otherLength); + + return new IntArrayData(newArray, newLength); + } + + @Override + public String toString() { + assert length <= array.length : length + " > " + array.length; + return getClass().getSimpleName() + ':' + Arrays.toString(Arrays.copyOf(array, (int)length)); + } }
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/LongArrayData.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/LongArrayData.java Thu Oct 23 13:45:22 2014 -0700 @@ -52,16 +52,31 @@ LongArrayData(final long array[], final int length) { super(length); assert array.length >= length; - this.array = array; + this.array = array; } @Override - public Class<?> getElementType() { + public final Class<?> getElementType() { return long.class; } @Override - public ArrayData copy() { + public final Class<?> getBoxedElementType() { + return Long.class; + } + + @Override + public final ContinuousArrayData widest(final ContinuousArrayData otherData) { + return otherData instanceof IntElements ? this : otherData; + } + + @Override + public final int getElementWeight() { + return 2; + } + + @Override + public LongArrayData copy() { return new LongArrayData(array.clone(), (int)length); } @@ -101,7 +116,7 @@ } @Override - public ArrayData convert(final Class<?> type) { + public ContinuousArrayData convert(final Class<?> type) { if (type == Integer.class || type == Long.class) { return this; } @@ -145,8 +160,7 @@ @Override public ArrayData shrink(final long newLength) { - Arrays.fill(array, (int) newLength, array.length, 0); - + Arrays.fill(array, (int)newLength, array.length, 0L); return this; } @@ -359,4 +373,37 @@ public Object fastPopObject() { return fastPopLong(); } + + @Override + public ContinuousArrayData fastConcat(final ContinuousArrayData otherData) { + final int otherLength = (int)otherData.length; + final int thisLength = (int)length; + assert otherLength > 0 && thisLength > 0; + + final long[] otherArray = ((LongArrayData)otherData).array; + final int newLength = otherLength + thisLength; + final long[] newArray = new long[ArrayData.alignUp(newLength)]; + + System.arraycopy(array, 0, newArray, 0, thisLength); + System.arraycopy(otherArray, 0, newArray, thisLength, otherLength); + + return new LongArrayData(newArray, newLength); + } + + @Override + public String toString() { + assert length <= array.length : length + " > " + array.length; + + final StringBuilder sb = new StringBuilder(getClass().getSimpleName()). + append(": ["); + for (int i = 0; i < length; i++) { + sb.append(array[i]).append('L'); //make sure L suffix is on elements, to discriminate this from IntArrayData.toString() + if (i + 1 < length) { + sb.append(", "); + } + } + sb.append(']'); + + return sb.toString(); + } }
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/NoTypeArrayData.java Thu Oct 23 11:19:29 2014 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,186 +0,0 @@ -/* - * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.nashorn.internal.runtime.arrays; - -import java.lang.reflect.Array; -import jdk.nashorn.internal.runtime.ScriptRuntime; - -/** - * Place holding array data for non-array objects. Activates a true array when - * accessed. Should only exist as a singleton defined in ArrayData. - */ -final class NoTypeArrayData extends ArrayData { - NoTypeArrayData() { - super(0); - } - - NoTypeArrayData(final long length) { - super(length); - } - - @Override - public Object[] asObjectArray() { - return ScriptRuntime.EMPTY_ARRAY; - } - - @Override - public ArrayData copy() { - return new NoTypeArrayData(); - } - - @Override - public Object asArrayOfType(final Class<?> componentType) { - return Array.newInstance(componentType, 0); - } - - @Override - public ArrayData convert(final Class<?> type) { - final long len = length; - final ArrayData arrayData; - if (type == Long.class) { - arrayData = new LongArrayData(new long[ArrayData.nextSize((int)len)], (int)len); - } else if (type == Double.class) { - arrayData = new NumberArrayData(new double[ArrayData.nextSize((int)len)], (int)len); - } else if (type == Integer.class) { - arrayData = new IntArrayData(new int[ArrayData.nextSize((int)len)], (int)len); - } else { - assert !type.isPrimitive(); - arrayData = new ObjectArrayData(new Object[ArrayData.nextSize((int)len)], (int)len); - } - return length == 0 ? arrayData : new DeletedRangeArrayFilter(arrayData, 0, len - 1); - } - - @Override - public void shiftLeft(final int by) { - //empty - } - - @Override - public ArrayData shiftRight(final int by) { - return this; - } - - @Override - public ArrayData ensure(final long safeIndex) { - if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH) { - return new SparseArrayData(this, safeIndex + 1); - } - - // Don't trample the shared EMPTY_ARRAY. - if (length == 0) { - return new NoTypeArrayData(Math.max(safeIndex + 1, length)); - } - - setLength(Math.max(safeIndex + 1, length)); - return this; - } - - @Override - public ArrayData shrink(final long newLength) { - return this; - } - - @Override - public ArrayData set(final int index, final Object value, final boolean strict) { - ArrayData newData; - - if (value instanceof Double) { - newData = convert(Double.class); - } else if (value instanceof Long) { - newData = convert(Long.class); - } else if (value instanceof Integer) { - newData = convert(Integer.class); - } else { - assert !(value instanceof Number); - newData = convert(value == null ? Object.class : value.getClass()); - } - - return newData.set(index, value, strict); - } - - @Override - public ArrayData set(final int index, final int value, final boolean strict) { - final ArrayData newData = convert(Integer.class); - return newData.set(index, value, strict); - } - - @Override - public ArrayData set(final int index, final long value, final boolean strict) { - final ArrayData newData = convert(Long.class); - return newData.set(index, value, strict); - } - - @Override - public ArrayData set(final int index, final double value, final boolean strict) { - final ArrayData newData = convert(Double.class); - return newData.set(index, value, strict); - } - - @Override - public int getInt(final int index) { - throw new ArrayIndexOutOfBoundsException(index); - } - - @Override - public long getLong(final int index) { - throw new ArrayIndexOutOfBoundsException(index); - } - - @Override - public double getDouble(final int index) { - throw new ArrayIndexOutOfBoundsException(index); - } - - @Override - public Object getObject(final int index) { - throw new ArrayIndexOutOfBoundsException(index); - } - - @Override - public boolean has(final int index) { - return false; - } - - @Override - public ArrayData delete(final int index) { - return new DeletedRangeArrayFilter(this, index, index); - } - - @Override - public ArrayData delete(final long fromIndex, final long toIndex) { - return new DeletedRangeArrayFilter(this, fromIndex, toIndex); - } - - @Override - public Object pop() { - return ScriptRuntime.UNDEFINED; - } - - @Override - public ArrayData slice(final long from, final long to) { - return this; - } -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/NonExtensibleArrayFilter.java Thu Oct 23 13:45:22 2014 -0700 @@ -0,0 +1,68 @@ +package jdk.nashorn.internal.runtime.arrays; + +import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; +import jdk.nashorn.internal.objects.Global; +import jdk.nashorn.internal.runtime.ScriptRuntime; + +/** + * Filter class that wrap arrays that have been tagged non extensible + */ +public class NonExtensibleArrayFilter extends ArrayFilter { + + /** + * Constructor + * @param underlying array + */ + public NonExtensibleArrayFilter(final ArrayData underlying) { + super(underlying); + } + + @Override + public ArrayData copy() { + return new NonExtensibleArrayFilter(underlying.copy()); + } + + @Override + public ArrayData slice(final long from, final long to) { + return new NonExtensibleArrayFilter(underlying.slice(from, to)); + } + + private ArrayData extensionCheck(final boolean strict, final int index) { + if (!strict) { + return this; + } + throw typeError(Global.instance(), "object.non.extensible", String.valueOf(index), ScriptRuntime.safeToString(this)); + } + + @Override + public ArrayData set(final int index, final Object value, final boolean strict) { + if (has(index)) { + return underlying.set(index, value, strict); + } + return extensionCheck(strict, index); + } + + @Override + public ArrayData set(final int index, final int value, final boolean strict) { + if (has(index)) { + return underlying.set(index, value, strict); + } + return extensionCheck(strict, index); + } + + @Override + public ArrayData set(final int index, final long value, final boolean strict) { + if (has(index)) { + return underlying.set(index, value, strict); + } + return extensionCheck(strict, index); + } + + @Override + public ArrayData set(final int index, final double value, final boolean strict) { + if (has(index)) { + return underlying.set(index, value, strict); + } + return extensionCheck(strict, index); + } +}
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/NumberArrayData.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/NumberArrayData.java Thu Oct 23 13:45:22 2014 -0700 @@ -48,19 +48,34 @@ * @param array an int array * @param length a length, not necessarily array.length */ - NumberArrayData(final double array[], final int length) { + NumberArrayData(final double[] array, final int length) { super(length); assert array.length >= length; - this.array = array; + this.array = array; } @Override - public Class<?> getElementType() { + public final Class<?> getElementType() { return double.class; } @Override - public ArrayData copy() { + public final Class<?> getBoxedElementType() { + return Double.class; + } + + @Override + public final int getElementWeight() { + return 3; + } + + @Override + public final ContinuousArrayData widest(final ContinuousArrayData otherData) { + return otherData instanceof IntOrLongElements ? this : otherData; + } + + @Override + public NumberArrayData copy() { return new NumberArrayData(array.clone(), (int)length); } @@ -88,7 +103,7 @@ } @Override - public ArrayData convert(final Class<?> type) { + public ContinuousArrayData convert(final Class<?> type) { if (type != Double.class && type != Integer.class && type != Long.class) { final int len = (int)length; return new ObjectArrayData(toObjectArray(false), len); @@ -129,7 +144,7 @@ @Override public ArrayData shrink(final long newLength) { - Arrays.fill(array, (int) newLength, array.length, 0.0); + Arrays.fill(array, (int)newLength, array.length, 0.0); return this; } @@ -334,4 +349,26 @@ public Object fastPopObject() { return fastPopDouble(); } + + @Override + public ContinuousArrayData fastConcat(final ContinuousArrayData otherData) { + final int otherLength = (int)otherData.length; + final int thisLength = (int)length; + assert otherLength > 0 && thisLength > 0; + + final double[] otherArray = ((NumberArrayData)otherData).array; + final int newLength = otherLength + thisLength; + final double[] newArray = new double[ArrayData.alignUp(newLength)]; + + System.arraycopy(array, 0, newArray, 0, thisLength); + System.arraycopy(otherArray, 0, newArray, thisLength, otherLength); + + return new NumberArrayData(newArray, newLength); + } + + @Override + public String toString() { + assert length <= array.length : length + " > " + array.length; + return getClass().getSimpleName() + ':' + Arrays.toString(Arrays.copyOf(array, (int)length)); + } }
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/NumericElements.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/NumericElements.java Thu Oct 23 13:45:22 2014 -0700 @@ -30,6 +30,6 @@ * Used for type checks that throw ClassCastExceptions and force relinks * for fast NativeArray specializations of builtin methods */ -public interface NumericElements { +public interface NumericElements extends AnyElements { //empty }
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/ObjectArrayData.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/ObjectArrayData.java Thu Oct 23 13:45:22 2014 -0700 @@ -37,7 +37,7 @@ * Implementation of {@link ArrayData} as soon as an Object has been * written to the array */ -final class ObjectArrayData extends ContinuousArrayData { +final class ObjectArrayData extends ContinuousArrayData implements AnyElements { /** * The wrapped array @@ -49,19 +49,34 @@ * @param array an int array * @param length a length, not necessarily array.length */ - ObjectArrayData(final Object array[], final int length) { + ObjectArrayData(final Object[] array, final int length) { super(length); assert array.length >= length; this.array = array; } @Override - public Class<?> getElementType() { + public final Class<?> getElementType() { return Object.class; } @Override - public ArrayData copy() { + public final Class<?> getBoxedElementType() { + return getElementType(); + } + + @Override + public final int getElementWeight() { + return 4; + } + + @Override + public final ContinuousArrayData widest(final ContinuousArrayData otherData) { + return otherData instanceof NumericElements ? this : otherData; + } + + @Override + public ObjectArrayData copy() { return new ObjectArrayData(array.clone(), (int)length); } @@ -79,7 +94,7 @@ } @Override - public ArrayData convert(final Class<?> type) { + public ObjectArrayData convert(final Class<?> type) { return this; } @@ -325,4 +340,26 @@ return returnValue; } + + @Override + public ContinuousArrayData fastConcat(final ContinuousArrayData otherData) { + final int otherLength = (int)otherData.length; + final int thisLength = (int)length; + assert otherLength > 0 && thisLength > 0; + + final Object[] otherArray = ((ObjectArrayData)otherData).array; + final int newLength = otherLength + thisLength; + final Object[] newArray = new Object[ArrayData.alignUp(newLength)]; + + System.arraycopy(array, 0, newArray, 0, thisLength); + System.arraycopy(otherArray, 0, newArray, thisLength, otherLength); + + return new ObjectArrayData(newArray, newLength); + } + + @Override + public String toString() { + assert length <= array.length : length + " > " + array.length; + return getClass().getSimpleName() + ':' + Arrays.toString(Arrays.copyOf(array, (int)length)); + } }
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/TypedArrayData.java Thu Oct 23 11:19:29 2014 -0700 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/TypedArrayData.java Thu Oct 23 13:45:22 2014 -0700 @@ -88,7 +88,7 @@ } @Override - public ArrayData copy() { + public TypedArrayData<T> copy() { throw new UnsupportedOperationException(); } @@ -133,7 +133,7 @@ } @Override - public ArrayData convert(final Class<?> type) { + public TypedArrayData<T> convert(final Class<?> type) { throw new UnsupportedOperationException(); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8058610.js Thu Oct 23 13:45:22 2014 -0700 @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8058610: must not let long operations overflow + * + * @test + * @run + */ + +function mul(x) { + return x.foo * x.bar; +} +print("=== mul ===") +print(mul({foo: 2147483647, bar: 2147483647})); // 2^31 +print(mul({foo: 17179869184, bar: 2147483647})); // 2^34 + +function self_mul(x) { + return x.foo *= x.bar; +} +print("=== self_mul ===") +print(self_mul({foo: 2147483647, bar: 2147483647})); // 2^31 +print(self_mul({foo: 17179869184, bar: 2147483647})); // 2^34 + +// We'll need to use this function to obtain long values larger in +// magnitude than those precisely representable in a double (2^53), +// as Nashorn's parser will reify such literals as a double. For +// overflow on add and sub we need (2^63)-1. +var parseLong = Java.type("java.lang.Long").parseLong; + +function sub(x) { + return x.foo - x.bar; +} +print("=== sub ===") +print(sub({foo: 2147483647, bar: -2147483647})); // 2^31 +print(sub({foo: parseLong("9223372036854775807"), bar: parseLong("-9223372036854775807")})); // 2^63-1 + +function self_sub(x) { + return x.foo -= x.bar; +} +print("=== self_sub ===") +print(self_sub({foo: 2147483647, bar: -2147483647})); // 2^31 +print(self_sub({foo: parseLong("9223372036854775807"), bar: parseLong("-9223372036854775807")})); // 2^63-1 + +function add(x) { + return x.foo + x.bar; +} +print("=== add ===") +print(add({foo: 2147483647, bar: 2147483647})); // 2^31 +print(add({foo: parseLong("9223372036854775807"), bar: parseLong("9223372036854775807")})); // 2^63-1 + +function self_add(x) { + return x.foo += x.bar; +} +print("=== self_add ===") +print(self_add({foo: 2147483647, bar: 2147483647})); // 2^31 +print(self_add({foo: parseLong("9223372036854775807"), bar: parseLong("9223372036854775807")})); // 2^63-1
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8058610.js.EXPECTED Thu Oct 23 13:45:22 2014 -0700 @@ -0,0 +1,18 @@ +=== mul === +4611686014132420600 +36893488130239234000 +=== self_mul === +4611686014132420600 +36893488130239234000 +=== sub === +4294967294 +18446744073709552000 +=== self_sub === +4294967294 +18446744073709552000 +=== add === +4294967294 +18446744073709552000 +=== self_add === +4294967294 +18446744073709552000
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8061391.js Thu Oct 23 13:45:22 2014 -0700 @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8061391 - Checks that the optimistic builtin for concat is semantically + * correct. + * + * @test + * @run + */ + +var maxJavaInt = 0x7fffffff; + +var ia = [1, 2, 3, 4]; +var la = [maxJavaInt + 1000, maxJavaInt + 2000, maxJavaInt + 3000, maxJavaInt + 4000]; +var da = [1.1, 2.2, 3.3, 4.4]; +var oa = ["one", "two", "three", "four"]; + +var aa = [ia, la, da, oa]; + +function concats() { + print("shared callsite"); + + print(ia); + print(la); + print(da); + print(oa); + print(aa); + + for (var i = 0; i < aa.length; i++) { + print(aa[i].concat(aa[i][0])); + for (var j = 0; j < aa.length ; j++) { + print(aa[i].concat(aa[j])); + } + } +} + +function concats_inline() { + print("separate callsites"); + + print(ia); + print(la); + print(da); + print(oa); + print(aa); + + print(aa[0].concat(aa[0])); + print(aa[0].concat(aa[1])); + print(aa[0].concat(aa[2])); + print(aa[0].concat(aa[3])); + print(aa[0].concat(aa[0][0])); + + print(aa[1].concat(aa[0])); + print(aa[1].concat(aa[1])); + print(aa[1].concat(aa[2])); + print(aa[1].concat(aa[3])); + print(aa[1].concat(aa[1][0])); + + print(aa[2].concat(aa[0])); + print(aa[2].concat(aa[1])); + print(aa[2].concat(aa[2])); + print(aa[2].concat(aa[3])); + print(aa[2].concat(aa[2][0])); + + print(aa[3].concat(aa[0])); + print(aa[3].concat(aa[1])); + print(aa[3].concat(aa[2])); + print(aa[3].concat(aa[3])); + print(aa[3].concat(aa[3][0])); +} + +concats(); +concats_inline(); + +print(); +var oldia = ia.slice(0); //clone ia +print("oldia = " + oldia); +ia[10] = "sparse"; +print("oldia = " + oldia); + +print(); +print("Redoing with sparse arrays"); + +concats(); +concats_inline(); + +ia = oldia; +print("Restored ia = " + ia); + +function concat_expand() { + print("concat type expansion"); + print(ia.concat(la)); + print(ia.concat(da)); + print(ia.concat(oa)); + print(la.concat(ia)); + print(la.concat(da)); + print(la.concat(oa)); + print(da.concat(ia)); + print(da.concat(la)); + print(da.concat(oa)); +} + +print(); +concat_expand(); + +print(); + +function concat_varargs() { + print("concat varargs"); + print(ia.concat(la)); //fast + print(ia.concat(la, da, oa)); //slow + var slow = ia.concat(1, maxJavaInt * 2, 4711.17, function() { print("hello, world") }); //slow + print(slow); + return slow; +} + +var slow = concat_varargs(); + +print(); +print("sanity checks"); +slow.map( + function(elem) { + if (elem instanceof Function) { + elem(); + } else { + print((typeof elem) + " = " + elem); + } + }); + +print(ia.concat({key: "value"})); +print(ia.concat({key: "value"}, {key2: "value2"}));
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8061391.js.EXPECTED Thu Oct 23 13:45:22 2014 -0700 @@ -0,0 +1,138 @@ +shared callsite +1,2,3,4 +2147484647,2147485647,2147486647,2147487647 +1.1,2.2,3.3,4.4 +one,two,three,four +1,2,3,4,2147484647,2147485647,2147486647,2147487647,1.1,2.2,3.3,4.4,one,two,three,four +1,2,3,4,1 +1,2,3,4,1,2,3,4 +1,2,3,4,2147484647,2147485647,2147486647,2147487647 +1,2,3,4,1.1,2.2,3.3,4.4 +1,2,3,4,one,two,three,four +2147484647,2147485647,2147486647,2147487647,2147484647 +2147484647,2147485647,2147486647,2147487647,1,2,3,4 +2147484647,2147485647,2147486647,2147487647,2147484647,2147485647,2147486647,2147487647 +2147484647,2147485647,2147486647,2147487647,1.1,2.2,3.3,4.4 +2147484647,2147485647,2147486647,2147487647,one,two,three,four +1.1,2.2,3.3,4.4,1.1 +1.1,2.2,3.3,4.4,1,2,3,4 +1.1,2.2,3.3,4.4,2147484647,2147485647,2147486647,2147487647 +1.1,2.2,3.3,4.4,1.1,2.2,3.3,4.4 +1.1,2.2,3.3,4.4,one,two,three,four +one,two,three,four,one +one,two,three,four,1,2,3,4 +one,two,three,four,2147484647,2147485647,2147486647,2147487647 +one,two,three,four,1.1,2.2,3.3,4.4 +one,two,three,four,one,two,three,four +separate callsites +1,2,3,4 +2147484647,2147485647,2147486647,2147487647 +1.1,2.2,3.3,4.4 +one,two,three,four +1,2,3,4,2147484647,2147485647,2147486647,2147487647,1.1,2.2,3.3,4.4,one,two,three,four +1,2,3,4,1,2,3,4 +1,2,3,4,2147484647,2147485647,2147486647,2147487647 +1,2,3,4,1.1,2.2,3.3,4.4 +1,2,3,4,one,two,three,four +1,2,3,4,1 +2147484647,2147485647,2147486647,2147487647,1,2,3,4 +2147484647,2147485647,2147486647,2147487647,2147484647,2147485647,2147486647,2147487647 +2147484647,2147485647,2147486647,2147487647,1.1,2.2,3.3,4.4 +2147484647,2147485647,2147486647,2147487647,one,two,three,four +2147484647,2147485647,2147486647,2147487647,2147484647 +1.1,2.2,3.3,4.4,1,2,3,4 +1.1,2.2,3.3,4.4,2147484647,2147485647,2147486647,2147487647 +1.1,2.2,3.3,4.4,1.1,2.2,3.3,4.4 +1.1,2.2,3.3,4.4,one,two,three,four +1.1,2.2,3.3,4.4,1.1 +one,two,three,four,1,2,3,4 +one,two,three,four,2147484647,2147485647,2147486647,2147487647 +one,two,three,four,1.1,2.2,3.3,4.4 +one,two,three,four,one,two,three,four +one,two,three,four,one + +oldia = 1,2,3,4 +oldia = 1,2,3,4 + +Redoing with sparse arrays +shared callsite +1,2,3,4,,,,,,,sparse +2147484647,2147485647,2147486647,2147487647 +1.1,2.2,3.3,4.4 +one,two,three,four +1,2,3,4,,,,,,,sparse,2147484647,2147485647,2147486647,2147487647,1.1,2.2,3.3,4.4,one,two,three,four +1,2,3,4,,,,,,,sparse,1 +1,2,3,4,,,,,,,sparse,1,2,3,4,,,,,,,sparse +1,2,3,4,,,,,,,sparse,2147484647,2147485647,2147486647,2147487647 +1,2,3,4,,,,,,,sparse,1.1,2.2,3.3,4.4 +1,2,3,4,,,,,,,sparse,one,two,three,four +2147484647,2147485647,2147486647,2147487647,2147484647 +2147484647,2147485647,2147486647,2147487647,1,2,3,4,,,,,,,sparse +2147484647,2147485647,2147486647,2147487647,2147484647,2147485647,2147486647,2147487647 +2147484647,2147485647,2147486647,2147487647,1.1,2.2,3.3,4.4 +2147484647,2147485647,2147486647,2147487647,one,two,three,four +1.1,2.2,3.3,4.4,1.1 +1.1,2.2,3.3,4.4,1,2,3,4,,,,,,,sparse +1.1,2.2,3.3,4.4,2147484647,2147485647,2147486647,2147487647 +1.1,2.2,3.3,4.4,1.1,2.2,3.3,4.4 +1.1,2.2,3.3,4.4,one,two,three,four +one,two,three,four,one +one,two,three,four,1,2,3,4,,,,,,,sparse +one,two,three,four,2147484647,2147485647,2147486647,2147487647 +one,two,three,four,1.1,2.2,3.3,4.4 +one,two,three,four,one,two,three,four +separate callsites +1,2,3,4,,,,,,,sparse +2147484647,2147485647,2147486647,2147487647 +1.1,2.2,3.3,4.4 +one,two,three,four +1,2,3,4,,,,,,,sparse,2147484647,2147485647,2147486647,2147487647,1.1,2.2,3.3,4.4,one,two,three,four +1,2,3,4,,,,,,,sparse,1,2,3,4,,,,,,,sparse +1,2,3,4,,,,,,,sparse,2147484647,2147485647,2147486647,2147487647 +1,2,3,4,,,,,,,sparse,1.1,2.2,3.3,4.4 +1,2,3,4,,,,,,,sparse,one,two,three,four +1,2,3,4,,,,,,,sparse,1 +2147484647,2147485647,2147486647,2147487647,1,2,3,4,,,,,,,sparse +2147484647,2147485647,2147486647,2147487647,2147484647,2147485647,2147486647,2147487647 +2147484647,2147485647,2147486647,2147487647,1.1,2.2,3.3,4.4 +2147484647,2147485647,2147486647,2147487647,one,two,three,four +2147484647,2147485647,2147486647,2147487647,2147484647 +1.1,2.2,3.3,4.4,1,2,3,4,,,,,,,sparse +1.1,2.2,3.3,4.4,2147484647,2147485647,2147486647,2147487647 +1.1,2.2,3.3,4.4,1.1,2.2,3.3,4.4 +1.1,2.2,3.3,4.4,one,two,three,four +1.1,2.2,3.3,4.4,1.1 +one,two,three,four,1,2,3,4,,,,,,,sparse +one,two,three,four,2147484647,2147485647,2147486647,2147487647 +one,two,three,four,1.1,2.2,3.3,4.4 +one,two,three,four,one,two,three,four +one,two,three,four,one +Restored ia = 1,2,3,4 + +concat type expansion +1,2,3,4,2147484647,2147485647,2147486647,2147487647 +1,2,3,4,1.1,2.2,3.3,4.4 +1,2,3,4,one,two,three,four +2147484647,2147485647,2147486647,2147487647,1,2,3,4 +2147484647,2147485647,2147486647,2147487647,1.1,2.2,3.3,4.4 +2147484647,2147485647,2147486647,2147487647,one,two,three,four +1.1,2.2,3.3,4.4,1,2,3,4 +1.1,2.2,3.3,4.4,2147484647,2147485647,2147486647,2147487647 +1.1,2.2,3.3,4.4,one,two,three,four + +concat varargs +1,2,3,4,2147484647,2147485647,2147486647,2147487647 +1,2,3,4,2147484647,2147485647,2147486647,2147487647,1.1,2.2,3.3,4.4,one,two,three,four +1,2,3,4,1,4294967294,4711.17,function() { print("hello, world") } + +sanity checks +number = 1 +number = 2 +number = 3 +number = 4 +number = 1 +number = 4294967294 +number = 4711.17 +hello, world +1,2,3,4,[object Object] +1,2,3,4,[object Object],[object Object]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8061391_2.js Thu Oct 23 13:45:22 2014 -0700 @@ -0,0 +1,52 @@ +/* + * 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. + */ + +/** + * Array extension check + * + * @test + * @run + */ + +"use strict"; +var a = [1,2,3]; +Object.preventExtensions(a); +try { + a[4] = 4; + print(a); +} catch (e) { + if (!(e instanceof TypeError)) { + print("TypeError expected but got e"); + } +} + +if (a[0] != 1) { + throw "element 0 is wrong"; +} +if (a[1] != 2) { + throw "element 1 is wrong"; +} +if (a[2] != 3) { + throw "element 2 is wrong"; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8061391_3.js Thu Oct 23 13:45:22 2014 -0700 @@ -0,0 +1,44 @@ +/* + * 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. + */ + +/** + * Array extension check + * + * @test + * @run + */ + +var a = [1,2,3]; +Object.preventExtensions(a); +a[4] = 4; +print(a); +if (a[0] != 1) { + throw "element 0 is wrong"; +} +if (a[1] != 2) { + throw "element 1 is wrong"; +} +if (a[2] != 3) { + throw "element 2 is wrong"; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8061391_3.js.EXPECTED Thu Oct 23 13:45:22 2014 -0700 @@ -0,0 +1,1 @@ +1,2,3
--- a/test/src/jdk/nashorn/internal/test/framework/ParallelTestRunner.java Thu Oct 23 11:19:29 2014 -0700 +++ b/test/src/jdk/nashorn/internal/test/framework/ParallelTestRunner.java Thu Oct 23 13:45:22 2014 -0700 @@ -78,7 +78,8 @@ // ParallelTestRunner-specific private static final String TEST_JS_THREADS = "test.js.threads"; private static final String TEST_JS_REPORT_FILE = "test.js.report.file"; - private static final int THREADS = Integer.getInteger(TEST_JS_THREADS, Runtime.getRuntime().availableProcessors()); + // test262 does a lot of eval's and the JVM hates multithreaded class definition, so lower thread count is usually faster. + private static final int THREADS = Integer.getInteger(TEST_JS_THREADS, Runtime.getRuntime().availableProcessors() > 4 ? 4 : 2); private final List<ScriptRunnable> tests = new ArrayList<>(); private final Set<String> orphans = new TreeSet<>();