mirror of
https://github.com/openjdk/jdk.git
synced 2026-02-10 10:28:37 +00:00
Merge
This commit is contained in:
commit
4b4e46f1fb
@ -69,7 +69,6 @@ ENABLE_ASSERTIONS_FLAGS="-ea -esa"
|
||||
|
||||
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
|
||||
|
||||
@ -25,6 +25,14 @@ Example:
|
||||
> 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 @@ be split into several classes in order not to run out of bytecode space.
|
||||
The default value is 0x8000 (32768).
|
||||
|
||||
|
||||
SYSTEM PROPERTY: -Dnashorn.compiler.intarithmetic
|
||||
SYSTEM PROPERTY: -Dnashorn.serialize.compression=<x>
|
||||
|
||||
(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:
|
||||
|
||||
// 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.
|
||||
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.
|
||||
|
||||
|
||||
SYSTEM PROPERTY: -Dnashorn.fields.dual
|
||||
|
||||
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 @@ for invoking a particular script function.
|
||||
"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 @@ Finally we count callsite misses on a per callsite bases, which occur
|
||||
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.
|
||||
|
||||
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.
|
||||
"diffPropertyMaps" Returns a diagnostic string representing the difference
|
||||
of two property maps.
|
||||
|
||||
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.
|
||||
"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.
|
||||
|
||||
"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.
|
||||
|
||||
"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.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.
|
||||
|
||||
|
||||
SYSTEM PROPERTY: -Dnashorn.scriptfunction.specialization.disable
|
||||
SYSTEM PROPERTY: -Dnashorn.spill.threshold=<x>
|
||||
|
||||
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.
|
||||
|
||||
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 @@ system property can be used to constrain the percentage of misses that
|
||||
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 @@ JDK's java.util.regex package. Set this property to "joni" to install
|
||||
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 @@ times on the same command line, with the same effect.
|
||||
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 @@ settings that all the tiers of codegen (e.g. Lower and CodeGenerator)
|
||||
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 @@ and inlining finally blocks.
|
||||
Lower is also responsible for determining control flow information
|
||||
like end points.
|
||||
|
||||
* symbols
|
||||
|
||||
* attr
|
||||
The symbols logger tracks the assignment os symbols to identifiers.
|
||||
|
||||
The lowering annotates a FunctionNode with symbols for each identifier
|
||||
and transforms high level constructs into lower level ones, that the
|
||||
CodeGenerator consumes.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
* 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.
|
||||
* scopedepths
|
||||
|
||||
This logs the calculation of scope depths for non-local symbols.
|
||||
|
||||
* fields
|
||||
|
||||
@ -896,6 +454,21 @@ Here is an example:
|
||||
[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
|
||||
=======================
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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}"
|
||||
|
||||
@ -27,6 +27,7 @@ package jdk.nashorn.internal.codegen;
|
||||
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS_VAR;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.EXPLODED_ARGUMENT_PREFIX;
|
||||
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
@ -38,6 +39,7 @@ import jdk.nashorn.internal.ir.AccessNode;
|
||||
import jdk.nashorn.internal.ir.CallNode;
|
||||
import jdk.nashorn.internal.ir.Expression;
|
||||
import jdk.nashorn.internal.ir.FunctionNode;
|
||||
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
|
||||
import jdk.nashorn.internal.ir.IdentNode;
|
||||
import jdk.nashorn.internal.ir.LexicalContext;
|
||||
import jdk.nashorn.internal.ir.Node;
|
||||
@ -321,7 +323,7 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
|
||||
|
||||
explodedArguments.pop();
|
||||
|
||||
return newFunctionNode;
|
||||
return newFunctionNode.setState(lc, CompilationState.BUILTINS_TRANSFORMED);
|
||||
}
|
||||
|
||||
private static boolean isApply(final CallNode callNode) {
|
||||
|
||||
@ -76,7 +76,6 @@ import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit;
|
||||
import jdk.nashorn.internal.ir.Node;
|
||||
import jdk.nashorn.internal.ir.RuntimeNode;
|
||||
import jdk.nashorn.internal.ir.RuntimeNode.Request;
|
||||
import jdk.nashorn.internal.ir.SplitNode;
|
||||
import jdk.nashorn.internal.ir.Statement;
|
||||
import jdk.nashorn.internal.ir.SwitchNode;
|
||||
import jdk.nashorn.internal.ir.Symbol;
|
||||
@ -135,9 +134,6 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
|
||||
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 @@ final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggabl
|
||||
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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -51,6 +51,7 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.className;
|
||||
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.MethodVisitor;
|
||||
import jdk.internal.org.objectweb.asm.util.TraceClassVisitor;
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.FunctionNode;
|
||||
import jdk.nashorn.internal.ir.SplitNode;
|
||||
import jdk.nashorn.internal.ir.debug.NashornClassReader;
|
||||
import jdk.nashorn.internal.ir.debug.NashornTextifier;
|
||||
import jdk.nashorn.internal.runtime.Context;
|
||||
@ -476,12 +476,6 @@ public class ClassEmitter implements Emitter {
|
||||
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
|
||||
*
|
||||
|
||||
@ -34,9 +34,7 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.GET_MAP;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.GET_STRING;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.QUICK_PREFIX;
|
||||
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.ExpressionStatement;
|
||||
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.PropertyNode;
|
||||
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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
//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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
}
|
||||
|
||||
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 class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
}
|
||||
|
||||
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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
@ -1047,6 +1064,13 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
return false;
|
||||
}
|
||||
|
||||
@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.
|
||||
@ -1219,7 +1243,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
popScopesUntil(target);
|
||||
final Label targetLabel = jump.getTargetLabel(target);
|
||||
targetLabel.markAsBreakTarget();
|
||||
method.splitAwareGoto(lc, targetLabel, target);
|
||||
method._goto(targetLabel);
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -2029,10 +2053,10 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
}
|
||||
|
||||
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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
method.begin();
|
||||
|
||||
defineCommonSplitMethodParameters();
|
||||
defineSplitMethodParameter(3, arrayType);
|
||||
defineSplitMethodParameter(CompilerConstants.SPLIT_ARRAY_ARG.slot(), arrayType);
|
||||
|
||||
fixScopeSlot(currentFunction);
|
||||
// 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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
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;
|
||||
public boolean enterSplitReturn(final SplitReturn splitReturn) {
|
||||
if (method.isReachable()) {
|
||||
method.loadUndefined(lc.getCurrentFunction().getReturnType())._return();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//no external jump targets or return in switch node
|
||||
if (!hasControlFlow) {
|
||||
return splitNode;
|
||||
@Override
|
||||
public boolean enterSetSplitState(final SetSplitState setSplitState) {
|
||||
if (method.isReachable()) {
|
||||
method.setSplitState(setSplitState.getState());
|
||||
}
|
||||
|
||||
// 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;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -3678,13 +3562,15 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
@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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
|
||||
@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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
@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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
// 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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
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();
|
||||
|
||||
@ -38,12 +38,11 @@ import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SCOPE_DEPTHS
|
||||
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;
|
||||
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 @@ enum CompilationPhase {
|
||||
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 @@ enum CompilationPhase {
|
||||
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 @@ enum CompilationPhase {
|
||||
* 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 @@ enum CompilationPhase {
|
||||
//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 @@ enum CompilationPhase {
|
||||
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 @@ enum CompilationPhase {
|
||||
});
|
||||
|
||||
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 @@ enum CompilationPhase {
|
||||
}
|
||||
},
|
||||
|
||||
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 @@ enum CompilationPhase {
|
||||
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 @@ enum CompilationPhase {
|
||||
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 @@ enum CompilationPhase {
|
||||
@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 @@ enum CompilationPhase {
|
||||
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 @@ enum CompilationPhase {
|
||||
|
||||
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 @@ enum CompilationPhase {
|
||||
//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()) {
|
||||
final FunctionNode newFunctionNode = transformFunction(fn, new ReplaceCompileUnits() {
|
||||
@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);
|
||||
}
|
||||
|
||||
@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 @@ enum CompilationPhase {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
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 @@ enum CompilationPhase {
|
||||
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 @@ enum CompilationPhase {
|
||||
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 @@ enum CompilationPhase {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -121,14 +128,6 @@ public final class CompileUnit implements Comparable<CompileUnit> {
|
||||
this.weight += w;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -155,7 +154,7 @@ public final class CompileUnit implements Comparable<CompileUnit> {
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@ -32,15 +32,16 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
|
||||
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 @@ public final class Compiler implements Loggable {
|
||||
*/
|
||||
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));
|
||||
}
|
||||
|
||||
final List<CompilationPhase> newPhases = new LinkedList<>();
|
||||
newPhases.addAll(Arrays.asList(phases));
|
||||
this.phases = Collections.unmodifiableList(newPhases);
|
||||
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));
|
||||
}
|
||||
|
||||
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 @@ public final class Compiler implements Loggable {
|
||||
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 @@ public final class Compiler implements Loggable {
|
||||
}
|
||||
|
||||
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 @@ public final class Compiler implements Loggable {
|
||||
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 @@ public final class Compiler implements Loggable {
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
@ -187,7 +187,6 @@ final class FindScopeDepths extends NodeVisitor<LexicalContext> implements Logga
|
||||
|
||||
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 @@ final class FindScopeDepths extends NodeVisitor<LexicalContext> implements Logga
|
||||
|
||||
//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 @@ final class FindScopeDepths extends NodeVisitor<LexicalContext> implements Logga
|
||||
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);
|
||||
|
||||
@ -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 @@ import jdk.nashorn.internal.codegen.types.Type;
|
||||
*
|
||||
* 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 @@ public final class Label {
|
||||
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 @@ public final class Label {
|
||||
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
|
||||
|
||||
@ -72,7 +72,7 @@ import jdk.nashorn.internal.ir.PropertyNode;
|
||||
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 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
|
||||
// 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 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
|
||||
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 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
|
||||
}
|
||||
|
||||
@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 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
|
||||
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) {
|
||||
|
||||
@ -352,8 +352,6 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
|
||||
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 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
|
||||
//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;
|
||||
}
|
||||
|
||||
@ -71,6 +71,7 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup
|
||||
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.ArrayType;
|
||||
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;
|
||||
@ -1662,19 +1661,6 @@ public class MethodEmitter implements Emitter {
|
||||
doesNotContinueSequentially();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
|
||||
@ -30,7 +30,6 @@ import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
|
||||
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.LoopNode;
|
||||
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 @@ final class OptimisticTypesCalculator extends NodeVisitor<LexicalContext> {
|
||||
|
||||
// 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 @@ final class OptimisticTypesCalculator extends NodeVisitor<LexicalContext> {
|
||||
return false;
|
||||
}
|
||||
neverOptimistic.push(new BitSet());
|
||||
splitDepth.push(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -189,19 +184,6 @@ final class OptimisticTypesCalculator extends NodeVisitor<LexicalContext> {
|
||||
return true;
|
||||
}
|
||||
|
||||
@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());
|
||||
@ -226,16 +208,11 @@ final class OptimisticTypesCalculator extends NodeVisitor<LexicalContext> {
|
||||
@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 @@ final class OptimisticTypesCalculator extends NodeVisitor<LexicalContext> {
|
||||
|
||||
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 @@ final class OptimisticTypesCalculator extends NodeVisitor<LexicalContext> {
|
||||
tagNeverOptimistic(test.getExpression());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean inSplitNode() {
|
||||
return splitDepth.peek() > 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,7 +85,7 @@ class ProgramPoints extends NodeVisitor<LexicalContext> {
|
||||
|
||||
@Override
|
||||
public boolean enterVarNode(final VarNode varNode) {
|
||||
noProgramPoint.add(varNode.getAssignmentDest());
|
||||
noProgramPoint.add(varNode.getName());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -37,6 +37,7 @@ import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
* 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
|
||||
|
||||
@ -29,6 +29,7 @@ package jdk.nashorn.internal.codegen.types;
|
||||
* 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
|
||||
|
||||
@ -69,6 +69,7 @@ import jdk.nashorn.internal.codegen.CompilerConstants;
|
||||
* 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);
|
||||
|
||||
@ -60,6 +60,7 @@ import jdk.nashorn.internal.codegen.CompilerConstants;
|
||||
* 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);
|
||||
|
||||
@ -54,6 +54,7 @@ import jdk.nashorn.internal.runtime.JSType;
|
||||
* 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);
|
||||
|
||||
|
||||
@ -46,6 +46,7 @@ import jdk.nashorn.internal.codegen.CompilerConstants;
|
||||
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);
|
||||
|
||||
|
||||
@ -29,6 +29,8 @@ package jdk.nashorn.internal.codegen.types;
|
||||
* 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
|
||||
*
|
||||
|
||||
@ -47,6 +47,7 @@ import jdk.nashorn.internal.runtime.Undefined;
|
||||
* contain a class that is a more specialized object
|
||||
*/
|
||||
class ObjectType extends Type {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
protected ObjectType() {
|
||||
this(Object.class);
|
||||
|
||||
@ -47,9 +47,11 @@ import static jdk.internal.org.objectweb.asm.Opcodes.T_DOUBLE;
|
||||
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 @@ import jdk.nashorn.internal.runtime.linker.Bootstrap;
|
||||
* 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 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
|
||||
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 abstract class Type implements Comparable<Type>, BytecodeOps {
|
||||
public int getSlots() {
|
||||
return slots;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the widest or most common of two types
|
||||
*
|
||||
@ -605,6 +609,18 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
|
||||
return type0.weight() > type1.weight() ? type0 : type1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -934,6 +950,8 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
|
||||
* 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 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
|
||||
* 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 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
|
||||
* 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 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
|
||||
|
||||
/** 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 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
|
||||
|
||||
/** 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 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
|
||||
}
|
||||
|
||||
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 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
|
||||
* 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 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
|
||||
* 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 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
|
||||
cache.put(type.getTypeClass(), type);
|
||||
return type;
|
||||
}
|
||||
|
||||
protected final Object readResolve() {
|
||||
return Type.typeFor(clazz);
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,6 +34,8 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
*/
|
||||
@Immutable
|
||||
public final class AccessNode extends BaseNode {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Property name. */
|
||||
private final String property;
|
||||
|
||||
|
||||
@ -39,6 +39,7 @@ import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
*/
|
||||
@Immutable
|
||||
public abstract class BaseNode extends Expression implements FunctionCall, Optimistic {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Base Node. */
|
||||
protected final Expression base;
|
||||
|
||||
@ -43,6 +43,8 @@ import jdk.nashorn.internal.parser.TokenType;
|
||||
*/
|
||||
@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 @@ public final class BinaryNode extends Expression implements Assignment<Expressio
|
||||
|
||||
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 =
|
||||
|
||||
@ -42,6 +42,8 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
*/
|
||||
@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;
|
||||
|
||||
|
||||
@ -108,6 +108,16 @@ public class BlockLexicalContext extends LexicalContext {
|
||||
return statement;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
@ -32,6 +32,8 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
* 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;
|
||||
|
||||
|
||||
@ -34,6 +34,7 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
*/
|
||||
@Immutable
|
||||
public final class BreakNode extends JumpStatement {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
||||
@ -32,6 +32,7 @@ import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
|
||||
@Immutable
|
||||
abstract class BreakableStatement extends LexicalContextStatement implements BreakableNode {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** break label. */
|
||||
protected final Label breakLabel;
|
||||
|
||||
@ -27,6 +27,7 @@ package jdk.nashorn.internal.ir;
|
||||
|
||||
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 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
*/
|
||||
@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 @@ public final class CallNode extends LexicalContextExpression implements Optimist
|
||||
/**
|
||||
* 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 */
|
||||
|
||||
@ -37,6 +37,8 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
*/
|
||||
@Immutable
|
||||
public final class CaseNode extends Node implements JoinPredecessor, Labels, Terminal {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Test expression. */
|
||||
private final Expression test;
|
||||
|
||||
|
||||
@ -33,6 +33,8 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
*/
|
||||
@Immutable
|
||||
public final class CatchNode extends Statement {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Exception identifier. */
|
||||
private final IdentNode exception;
|
||||
|
||||
|
||||
@ -34,6 +34,8 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
*/
|
||||
@Immutable
|
||||
public class ContinueNode extends JumpStatement {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
@ -80,4 +82,3 @@ public class ContinueNode extends JumpStatement {
|
||||
return ((LoopNode)target).getContinueLabel();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -33,6 +33,7 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
*/
|
||||
@Immutable
|
||||
public final class EmptyNode extends Statement {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
||||
@ -35,6 +35,8 @@ import jdk.nashorn.internal.runtime.UnwarrantedOptimismException;
|
||||
*
|
||||
*/
|
||||
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>() {
|
||||
|
||||
@ -35,6 +35,8 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
*/
|
||||
@Immutable
|
||||
public final class ExpressionStatement extends Statement {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Expression to execute. */
|
||||
private final Expression expression;
|
||||
|
||||
|
||||
@ -33,6 +33,8 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
*/
|
||||
@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;
|
||||
|
||||
@ -31,6 +31,7 @@ import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALL
|
||||
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 @@ import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
|
||||
*/
|
||||
@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 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
}
|
||||
|
||||
/** 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 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
private final long lastToken;
|
||||
|
||||
/** Method's namespace. */
|
||||
private final Namespace namespace;
|
||||
private transient final Namespace namespace;
|
||||
|
||||
/** Current compilation state */
|
||||
@Ignore
|
||||
@ -207,31 +210,23 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
/** 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 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
*/
|
||||
|
||||
/** 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 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
* 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 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
/** 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 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
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 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
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;
|
||||
}
|
||||
@ -442,6 +438,39 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
return source;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
}
|
||||
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 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
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 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
body,
|
||||
parameters,
|
||||
thisProperties,
|
||||
rootClass));
|
||||
rootClass, source, namespace));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -705,17 +757,10 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
* @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.
|
||||
*
|
||||
@ -778,7 +823,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
body,
|
||||
parameters,
|
||||
thisProperties,
|
||||
rootClass));
|
||||
rootClass, source, namespace));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -846,7 +891,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
* @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 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
body,
|
||||
parameters,
|
||||
thisProperties,
|
||||
rootClass));
|
||||
rootClass, source, namespace));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -951,7 +996,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
body,
|
||||
parameters,
|
||||
thisProperties,
|
||||
rootClass));
|
||||
rootClass, source, namespace));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -965,9 +1010,9 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
body,
|
||||
parameters,
|
||||
thisProperties,
|
||||
rootClass));
|
||||
rootClass, source, namespace));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1096,7 +1141,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
body,
|
||||
parameters,
|
||||
thisProperties,
|
||||
rootClass
|
||||
rootClass, source, namespace
|
||||
));
|
||||
}
|
||||
|
||||
@ -1144,7 +1189,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
body,
|
||||
parameters,
|
||||
thisProperties,
|
||||
rootClass));
|
||||
rootClass, source, namespace));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1200,6 +1245,6 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
body,
|
||||
parameters,
|
||||
thisProperties,
|
||||
rootClass));
|
||||
rootClass, source, namespace));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -42,6 +42,8 @@ import jdk.nashorn.internal.parser.TokenType;
|
||||
*/
|
||||
@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;
|
||||
|
||||
@ -33,6 +33,8 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
*/
|
||||
@Immutable
|
||||
public final class IfNode extends Statement implements JoinPredecessor {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Test expression. */
|
||||
private final Expression test;
|
||||
|
||||
|
||||
@ -33,6 +33,8 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
*/
|
||||
@Immutable
|
||||
public final class IndexNode extends BaseNode {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Property index. */
|
||||
private final Expression index;
|
||||
|
||||
|
||||
@ -33,6 +33,7 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
* 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;
|
||||
|
||||
@ -31,6 +31,7 @@ import jdk.nashorn.internal.codegen.Label;
|
||||
* 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;
|
||||
|
||||
@ -35,6 +35,8 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
*/
|
||||
@Immutable
|
||||
public final class LabelNode extends LexicalContextStatement implements JoinPredecessor {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Label ident. */
|
||||
private final String labelName;
|
||||
|
||||
|
||||
@ -439,6 +439,14 @@ public class LexicalContext {
|
||||
return getParentBlock() == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -472,9 +480,6 @@ public class LexicalContext {
|
||||
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()) {
|
||||
|
||||
@ -28,6 +28,7 @@ package jdk.nashorn.internal.ir;
|
||||
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);
|
||||
|
||||
@ -28,6 +28,8 @@ package jdk.nashorn.internal.ir;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
|
||||
abstract class LexicalContextStatement extends Statement implements LexicalContextNode {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
|
||||
@ -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 @@ import jdk.nashorn.internal.runtime.Undefined;
|
||||
*/
|
||||
@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 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
||||
* @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 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
||||
|
||||
@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 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
||||
|
||||
@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 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
||||
}
|
||||
|
||||
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 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
||||
|
||||
@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 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
||||
|
||||
@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 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
||||
}
|
||||
|
||||
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 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
||||
*/
|
||||
@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 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
||||
* 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 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
||||
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) {
|
||||
|
||||
@ -34,6 +34,8 @@ import jdk.nashorn.internal.codegen.Label;
|
||||
* 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;
|
||||
|
||||
|
||||
@ -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 @@ import jdk.nashorn.internal.parser.TokenType;
|
||||
/**
|
||||
* 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;
|
||||
|
||||
|
||||
@ -37,6 +37,7 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
*/
|
||||
@Immutable
|
||||
public final class ObjectNode extends Expression {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Literal elements. */
|
||||
private final List<PropertyNode> elements;
|
||||
|
||||
@ -33,6 +33,7 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
*/
|
||||
@Immutable
|
||||
public final class PropertyNode extends Node {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Property key. */
|
||||
private final PropertyKey key;
|
||||
|
||||
@ -36,6 +36,8 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
*/
|
||||
@Immutable
|
||||
public class ReturnNode extends Statement {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Optional expression. */
|
||||
private final Expression expression;
|
||||
|
||||
|
||||
@ -42,6 +42,7 @@ import jdk.nashorn.internal.parser.TokenType;
|
||||
*/
|
||||
@Immutable
|
||||
public class RuntimeNode extends Expression implements Optimistic {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Request enum used for meta-information about the runtime request
|
||||
|
||||
@ -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(");");
|
||||
}
|
||||
}
|
||||
@ -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 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
* 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 @@ public class SplitNode extends LexicalContextStatement implements Labels, Compil
|
||||
/** Body of split code. */
|
||||
private final Block body;
|
||||
|
||||
private Map<Label, JoinPredecessor> jumps;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
@ -65,19 +62,18 @@ public class SplitNode extends LexicalContextStatement implements Labels, Compil
|
||||
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 @@ public class SplitNode extends LexicalContextStatement implements Labels, Compil
|
||||
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 @@ public class SplitNode extends LexicalContextStatement implements Labels, Compil
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -31,6 +31,7 @@ package jdk.nashorn.internal.ir;
|
||||
* location information is the Statement
|
||||
*/
|
||||
public abstract class Statement extends Node implements Terminal {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final int lineNumber;
|
||||
|
||||
|
||||
@ -37,6 +37,8 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
*/
|
||||
@Immutable
|
||||
public final class SwitchNode extends BreakableStatement {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Switch expression. */
|
||||
private final Expression expression;
|
||||
|
||||
|
||||
@ -97,7 +97,7 @@ public final class Symbol implements Comparable<Symbol> {
|
||||
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 @@ public final class Symbol implements Comparable<Symbol> {
|
||||
*
|
||||
* @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)));
|
||||
|
||||
@ -37,6 +37,8 @@ import jdk.nashorn.internal.parser.TokenType;
|
||||
*/
|
||||
@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;
|
||||
|
||||
@ -33,6 +33,8 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
*/
|
||||
@Immutable
|
||||
public final class ThrowNode extends Statement implements JoinPredecessor {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Exception expression. */
|
||||
private final Expression expression;
|
||||
|
||||
|
||||
@ -36,6 +36,8 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
*/
|
||||
@Immutable
|
||||
public final class TryNode extends Statement implements JoinPredecessor {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Try statements. */
|
||||
private final Block body;
|
||||
|
||||
|
||||
@ -46,6 +46,8 @@ import jdk.nashorn.internal.parser.TokenType;
|
||||
*/
|
||||
@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;
|
||||
|
||||
|
||||
@ -34,6 +34,8 @@ import jdk.nashorn.internal.parser.Token;
|
||||
*/
|
||||
@Immutable
|
||||
public final class VarNode extends Statement implements Assignment<IdentNode> {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Var name. */
|
||||
private final IdentNode name;
|
||||
|
||||
|
||||
@ -34,6 +34,8 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
*/
|
||||
@Immutable
|
||||
public final class WhileNode extends LoopNode {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
|
||||
/** is this a do while node ? */
|
||||
private final boolean isDoWhile;
|
||||
|
||||
@ -33,6 +33,8 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
*/
|
||||
@Immutable
|
||||
public final class WithNode extends LexicalContextStatement {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** This expression. */
|
||||
private final Expression expression;
|
||||
|
||||
|
||||
@ -38,6 +38,7 @@ import jdk.nashorn.internal.ir.EmptyNode;
|
||||
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.ObjectNode;
|
||||
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;
|
||||
@ -389,6 +392,26 @@ public abstract class NodeVisitor<T extends LexicalContext> {
|
||||
return leaveDefault(functionNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
@ -569,6 +592,26 @@ public abstract class NodeVisitor<T extends LexicalContext> {
|
||||
return leaveDefault(runtimeNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
@ -589,6 +632,26 @@ public abstract class NodeVisitor<T extends LexicalContext> {
|
||||
return leaveDefault(splitNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
|
||||
@ -48,7 +48,7 @@ import jdk.nashorn.internal.runtime.options.Options;
|
||||
/**
|
||||
* 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
|
||||
|
||||
@ -92,9 +92,10 @@ public final class NativeArray extends ScriptObject implements OptimisticBuiltin
|
||||
private static final Object CALL_CMP = new Object();
|
||||
private static final Object TO_LOCALE_STRING = new Object();
|
||||
|
||||
private SwitchPoint lengthMadeNotWritableSwitchPoint;
|
||||
private PushLinkLogic pushLinkLogic;
|
||||
private PopLinkLogic popLinkLogic;
|
||||
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 @@ public final class NativeArray extends ScriptObject implements OptimisticBuiltin
|
||||
this(ArrayData.allocate(array.length));
|
||||
|
||||
ArrayData arrayData = this.getArray();
|
||||
arrayData.ensure(array.length - 1);
|
||||
if (array.length > 0) {
|
||||
arrayData.ensure(array.length - 1);
|
||||
}
|
||||
|
||||
for (int index = 0; index < array.length; index++) {
|
||||
final Object value = array[index];
|
||||
@ -753,6 +756,79 @@ public final class NativeArray extends ScriptObject implements OptimisticBuiltin
|
||||
return construct(newObj, self, new Object[]{length});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 [ , ... ] ] ] )
|
||||
*
|
||||
@ -763,6 +839,7 @@ public final class NativeArray extends ScriptObject implements OptimisticBuiltin
|
||||
@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 @@ public final class NativeArray extends ScriptObject implements OptimisticBuiltin
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1797,6 +1876,40 @@ public final class NativeArray extends ScriptObject implements OptimisticBuiltin
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@ -1864,6 +1977,14 @@ public final class NativeArray extends ScriptObject implements OptimisticBuiltin
|
||||
throw new ClassCastException();
|
||||
}
|
||||
|
||||
private static final ContinuousArrayData getContinuousArrayDataCCE(final Object self) {
|
||||
try {
|
||||
return (ContinuousArrayData)((NativeArray)self).getArray();
|
||||
} catch (final NullPointerException e) {
|
||||
throw new ClassCastException();
|
||||
}
|
||||
}
|
||||
|
||||
private static final ContinuousArrayData getContinuousArrayDataCCE(final Object self, final Class<?> elementType) {
|
||||
try {
|
||||
return (ContinuousArrayData)((NativeArray)self).getArray(elementType); //ensure element type can fit "elementType"
|
||||
|
||||
@ -89,6 +89,11 @@ public final class NativeFloat32Array extends ArrayBufferView {
|
||||
return double.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getBoxedElementType() {
|
||||
return Double.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MethodHandle getGetElem() {
|
||||
return GET_ELEM;
|
||||
|
||||
@ -99,6 +99,11 @@ public final class NativeFloat64Array extends ArrayBufferView {
|
||||
return double.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getBoxedElementType() {
|
||||
return Double.class;
|
||||
}
|
||||
|
||||
private double getElem(final int index) {
|
||||
try {
|
||||
return nb.get(index);
|
||||
|
||||
@ -100,6 +100,11 @@ public final class NativeInt16Array extends ArrayBufferView {
|
||||
return int.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getBoxedElementType() {
|
||||
return Integer.class;
|
||||
}
|
||||
|
||||
private int getElem(final int index) {
|
||||
try {
|
||||
return nb.get(index);
|
||||
|
||||
@ -117,6 +117,11 @@ public final class NativeInt32Array extends ArrayBufferView {
|
||||
return int.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getBoxedElementType() {
|
||||
return Integer.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(final int index) {
|
||||
return getElem(index);
|
||||
|
||||
@ -98,6 +98,11 @@ public final class NativeInt8Array extends ArrayBufferView {
|
||||
return int.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getBoxedElementType() {
|
||||
return Integer.class;
|
||||
}
|
||||
|
||||
private int getElem(final int index) {
|
||||
try {
|
||||
return nb.get(index);
|
||||
|
||||
@ -28,7 +28,6 @@ package jdk.nashorn.internal.objects;
|
||||
import static jdk.nashorn.internal.lookup.Lookup.MH;
|
||||
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
|
||||
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
|
||||
@ -123,6 +123,11 @@ public final class NativeUint16Array extends ArrayBufferView {
|
||||
return int.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getBoxedElementType() {
|
||||
return Integer.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(final int index) {
|
||||
return getElem(index);
|
||||
|
||||
@ -132,6 +132,11 @@ public final class NativeUint32Array extends ArrayBufferView {
|
||||
return long.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getBoxedElementType() {
|
||||
return Integer.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(final int index) {
|
||||
return (int)getLong(index);
|
||||
|
||||
@ -123,6 +123,11 @@ public final class NativeUint8Array extends ArrayBufferView {
|
||||
return int.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getBoxedElementType() {
|
||||
return Integer.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(final int index) {
|
||||
return getElem(index);
|
||||
|
||||
@ -103,6 +103,11 @@ public final class NativeUint8ClampedArray extends ArrayBufferView {
|
||||
return int.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getBoxedElementType() {
|
||||
return int.class;
|
||||
}
|
||||
|
||||
private int getElem(final int index) {
|
||||
try {
|
||||
return nb.get(index) & 0xff;
|
||||
|
||||
@ -32,7 +32,6 @@ import static jdk.nashorn.internal.parser.TokenType.ESCSTRING;
|
||||
import static jdk.nashorn.internal.parser.TokenType.RBRACE;
|
||||
import static jdk.nashorn.internal.parser.TokenType.RBRACKET;
|
||||
import static jdk.nashorn.internal.parser.TokenType.STRING;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import jdk.nashorn.internal.ir.Expression;
|
||||
|
||||
@ -46,6 +46,7 @@ import static jdk.nashorn.internal.parser.TokenType.RPAREN;
|
||||
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 @@ public class Lexer extends Scanner {
|
||||
* 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 @@ public class Lexer extends Scanner {
|
||||
* 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 @@ public class Lexer extends Scanner {
|
||||
* Temporary container for XML expression.
|
||||
*/
|
||||
public static class XMLToken extends LexerToken {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -27,6 +27,7 @@ package jdk.nashorn.internal.runtime;
|
||||
import static jdk.nashorn.internal.lookup.Lookup.MH;
|
||||
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
|
||||
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
|
||||
|
||||
import java.lang.invoke.CallSite;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
@ -786,6 +787,7 @@ final class CompiledFunction {
|
||||
// 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 @@ final class CompiledFunction {
|
||||
// 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 @@ final class CompiledFunction {
|
||||
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 @@ final class CompiledFunction {
|
||||
FunctionNode reparse() {
|
||||
return data.reparse();
|
||||
}
|
||||
|
||||
boolean isSerialized() {
|
||||
return data.isSerialized();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
|
||||
@ -150,6 +150,13 @@ public final class Context {
|
||||
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 @@ public final class Context {
|
||||
|
||||
@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 @@ public final class Context {
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
|
||||
@ -29,7 +29,6 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
|
||||
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY;
|
||||
import static jdk.nashorn.internal.lookup.Lookup.MH;
|
||||
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.Array;
|
||||
@ -1775,6 +1774,23 @@ public enum JSType {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the boxed version of a primitive class
|
||||
* @param clazz the class
|
||||
* @return the boxed type of clazz, or unchanged if not primitive
|
||||
*/
|
||||
public static Class<?> getBoxedClass(final Class<?> clazz) {
|
||||
if (clazz == int.class) {
|
||||
return Integer.class;
|
||||
} else if (clazz == long.class) {
|
||||
return Long.class;
|
||||
} else if (clazz == double.class) {
|
||||
return Double.class;
|
||||
}
|
||||
assert !clazz.isPrimitive();
|
||||
return clazz;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a method handle constant of the correct primitive type
|
||||
* for a constant object
|
||||
|
||||
@ -43,6 +43,7 @@ import jdk.nashorn.internal.codegen.Compiler;
|
||||
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 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
/** 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 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
* @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 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
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 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
nfn.setParent(this);
|
||||
}
|
||||
|
||||
this.serializedAst = serializedAst;
|
||||
createLogger();
|
||||
}
|
||||
|
||||
@ -212,10 +219,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
*/
|
||||
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 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
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 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
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 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
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 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
|
||||
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 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
|
||||
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 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
|
||||
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();
|
||||
|
||||
@ -692,8 +692,7 @@ public abstract class ScriptObject implements PropertyAccess {
|
||||
assert isValidArrayIndex(index) : "invalid array index";
|
||||
final long longIndex = ArrayIndex.toLongIndex(index);
|
||||
doesNotHaveEnsureDelete(longIndex, getArray().length(), false);
|
||||
setArray(getArray().ensure(longIndex));
|
||||
setArray(getArray().set(index, value, false));
|
||||
setArray(getArray().ensure(longIndex).set(index,value, false));
|
||||
}
|
||||
|
||||
private void checkIntegerKey(final String key) {
|
||||
@ -1462,9 +1461,8 @@ public abstract class ScriptObject implements PropertyAccess {
|
||||
|
||||
//invalidate any fast array setters
|
||||
final ArrayData array = getArray();
|
||||
if (array != null) {
|
||||
array.invalidateSetters();
|
||||
}
|
||||
assert array != null;
|
||||
setArray(ArrayData.preventExtension(array));
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -2645,20 +2643,22 @@ public abstract class ScriptObject implements PropertyAccess {
|
||||
* @param newLength new length to set
|
||||
*/
|
||||
public final void setLength(final long newLength) {
|
||||
final long arrayLength = getArray().length();
|
||||
if (newLength == arrayLength) {
|
||||
return;
|
||||
}
|
||||
ArrayData data = getArray();
|
||||
final long arrayLength = data.length();
|
||||
if (newLength == arrayLength) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (newLength > arrayLength) {
|
||||
setArray(getArray().ensure(newLength - 1));
|
||||
if (getArray().canDelete(arrayLength, newLength - 1, false)) {
|
||||
setArray(getArray().delete(arrayLength, newLength - 1));
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (newLength > arrayLength) {
|
||||
data = data.ensure(newLength - 1);
|
||||
if (data.canDelete(arrayLength, newLength - 1, false)) {
|
||||
data = data.delete(arrayLength, newLength - 1);
|
||||
}
|
||||
setArray(data);
|
||||
return;
|
||||
}
|
||||
|
||||
if (newLength < arrayLength) {
|
||||
if (newLength < arrayLength) {
|
||||
long actualLength = newLength;
|
||||
|
||||
// Check for numeric keys in property map and delete them or adjust length, depending on whether
|
||||
@ -2680,8 +2680,8 @@ public abstract class ScriptObject implements PropertyAccess {
|
||||
}
|
||||
}
|
||||
|
||||
setArray(getArray().shrink(actualLength));
|
||||
getArray().setLength(actualLength);
|
||||
setArray(data.shrink(actualLength));
|
||||
data.setLength(actualLength);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3194,8 +3194,9 @@ public abstract class ScriptObject implements PropertyAccess {
|
||||
final int index = getArrayIndex(primitiveKey);
|
||||
|
||||
if (isValidArrayIndex(index)) {
|
||||
if (getArray().has(index)) {
|
||||
setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
|
||||
final ArrayData data = getArray();
|
||||
if (data.has(index)) {
|
||||
setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
|
||||
} else {
|
||||
doesNotHave(index, value, callSiteFlags);
|
||||
}
|
||||
@ -3213,8 +3214,9 @@ public abstract class ScriptObject implements PropertyAccess {
|
||||
final int index = getArrayIndex(primitiveKey);
|
||||
|
||||
if (isValidArrayIndex(index)) {
|
||||
if (getArray().has(index)) {
|
||||
setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
|
||||
final ArrayData data = getArray();
|
||||
if (data.has(index)) {
|
||||
setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
|
||||
} else {
|
||||
doesNotHave(index, value, callSiteFlags);
|
||||
}
|
||||
@ -3232,8 +3234,9 @@ public abstract class ScriptObject implements PropertyAccess {
|
||||
final int index = getArrayIndex(primitiveKey);
|
||||
|
||||
if (isValidArrayIndex(index)) {
|
||||
if (getArray().has(index)) {
|
||||
setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
|
||||
final ArrayData data = getArray();
|
||||
if (data.has(index)) {
|
||||
setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
|
||||
} else {
|
||||
doesNotHave(index, value, callSiteFlags);
|
||||
}
|
||||
@ -3251,8 +3254,9 @@ public abstract class ScriptObject implements PropertyAccess {
|
||||
final int index = getArrayIndex(primitiveKey);
|
||||
|
||||
if (isValidArrayIndex(index)) {
|
||||
if (getArray().has(index)) {
|
||||
setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
|
||||
final ArrayData data = getArray();
|
||||
if (data.has(index)) {
|
||||
setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
|
||||
} else {
|
||||
doesNotHave(index, value, callSiteFlags);
|
||||
}
|
||||
@ -3269,8 +3273,9 @@ public abstract class ScriptObject implements PropertyAccess {
|
||||
final int index = getArrayIndex(key);
|
||||
|
||||
if (isValidArrayIndex(index)) {
|
||||
if (getArray().has(index)) {
|
||||
setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
|
||||
final ArrayData data = getArray();
|
||||
if (data.has(index)) {
|
||||
setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
|
||||
} else {
|
||||
doesNotHave(index, value, callSiteFlags);
|
||||
}
|
||||
@ -3287,8 +3292,9 @@ public abstract class ScriptObject implements PropertyAccess {
|
||||
final int index = getArrayIndex(key);
|
||||
|
||||
if (isValidArrayIndex(index)) {
|
||||
if (getArray().has(index)) {
|
||||
setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
|
||||
final ArrayData data = getArray();
|
||||
if (data.has(index)) {
|
||||
setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
|
||||
} else {
|
||||
doesNotHave(index, value, callSiteFlags);
|
||||
}
|
||||
@ -3305,8 +3311,9 @@ public abstract class ScriptObject implements PropertyAccess {
|
||||
final int index = getArrayIndex(key);
|
||||
|
||||
if (isValidArrayIndex(index)) {
|
||||
if (getArray().has(index)) {
|
||||
setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
|
||||
final ArrayData data = getArray();
|
||||
if (data.has(index)) {
|
||||
setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
|
||||
} else {
|
||||
doesNotHave(index, value, callSiteFlags);
|
||||
}
|
||||
@ -3323,8 +3330,9 @@ public abstract class ScriptObject implements PropertyAccess {
|
||||
final int index = getArrayIndex(key);
|
||||
|
||||
if (isValidArrayIndex(index)) {
|
||||
if (getArray().has(index)) {
|
||||
setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
|
||||
final ArrayData data = getArray();
|
||||
if (data.has(index)) {
|
||||
setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
|
||||
} else {
|
||||
doesNotHave(index, value, callSiteFlags);
|
||||
}
|
||||
@ -3341,8 +3349,9 @@ public abstract class ScriptObject implements PropertyAccess {
|
||||
final int index = getArrayIndex(key);
|
||||
|
||||
if (isValidArrayIndex(index)) {
|
||||
if (getArray().has(index)) {
|
||||
setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
|
||||
final ArrayData data = getArray();
|
||||
if (data.has(index)) {
|
||||
setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
|
||||
} else {
|
||||
doesNotHave(index, value, callSiteFlags);
|
||||
}
|
||||
@ -3359,8 +3368,9 @@ public abstract class ScriptObject implements PropertyAccess {
|
||||
final int index = getArrayIndex(key);
|
||||
|
||||
if (isValidArrayIndex(index)) {
|
||||
if (getArray().has(index)) {
|
||||
setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
|
||||
final ArrayData data = getArray();
|
||||
if (data.has(index)) {
|
||||
setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
|
||||
} else {
|
||||
doesNotHave(index, value, callSiteFlags);
|
||||
}
|
||||
@ -3377,8 +3387,9 @@ public abstract class ScriptObject implements PropertyAccess {
|
||||
final int index = getArrayIndex(key);
|
||||
|
||||
if (isValidArrayIndex(index)) {
|
||||
if (getArray().has(index)) {
|
||||
setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
|
||||
final ArrayData data = getArray();
|
||||
if (data.has(index)) {
|
||||
setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
|
||||
} else {
|
||||
doesNotHave(index, value, callSiteFlags);
|
||||
}
|
||||
@ -3395,8 +3406,9 @@ public abstract class ScriptObject implements PropertyAccess {
|
||||
final int index = getArrayIndex(key);
|
||||
|
||||
if (isValidArrayIndex(index)) {
|
||||
if (getArray().has(index)) {
|
||||
setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
|
||||
final ArrayData data = getArray();
|
||||
if (data.has(index)) {
|
||||
setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
|
||||
} else {
|
||||
doesNotHave(index, value, callSiteFlags);
|
||||
}
|
||||
@ -3413,7 +3425,8 @@ public abstract class ScriptObject implements PropertyAccess {
|
||||
final int index = getArrayIndex(key);
|
||||
if (isValidArrayIndex(index)) {
|
||||
if (getArray().has(index)) {
|
||||
setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
|
||||
final ArrayData data = getArray();
|
||||
setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
|
||||
} else {
|
||||
doesNotHave(index, value, callSiteFlags);
|
||||
}
|
||||
@ -3429,8 +3442,9 @@ public abstract class ScriptObject implements PropertyAccess {
|
||||
final int index = getArrayIndex(key);
|
||||
|
||||
if (isValidArrayIndex(index)) {
|
||||
if (getArray().has(index)) {
|
||||
setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
|
||||
final ArrayData data = getArray();
|
||||
if (data.has(index)) {
|
||||
setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
|
||||
} else {
|
||||
doesNotHave(index, value, callSiteFlags);
|
||||
}
|
||||
@ -3447,8 +3461,9 @@ public abstract class ScriptObject implements PropertyAccess {
|
||||
final int index = getArrayIndex(key);
|
||||
|
||||
if (isValidArrayIndex(index)) {
|
||||
if (getArray().has(index)) {
|
||||
setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
|
||||
final ArrayData data = getArray();
|
||||
if (data.has(index)) {
|
||||
setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
|
||||
} else {
|
||||
doesNotHave(index, value, callSiteFlags);
|
||||
}
|
||||
@ -3465,8 +3480,9 @@ public abstract class ScriptObject implements PropertyAccess {
|
||||
final int index = getArrayIndex(key);
|
||||
|
||||
if (isValidArrayIndex(index)) {
|
||||
if (getArray().has(index)) {
|
||||
setArray(getArray().set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
|
||||
final ArrayData data = getArray();
|
||||
if (data.has(index)) {
|
||||
setArray(data.set(index, value, NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)));
|
||||
} else {
|
||||
doesNotHave(index, value, callSiteFlags);
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user