Merge
This commit is contained in:
commit
9bd2647140
@ -24,3 +24,4 @@ jcov2/*
|
||||
.idea/*
|
||||
test/lib/testng.jar
|
||||
test/script/external/*
|
||||
.project
|
||||
|
@ -71,9 +71,20 @@ Classes</a></span></li>
|
||||
Arrays</a></span></li>
|
||||
<li><span><a href="#jsimplement">Implementing Java
|
||||
Interfaces</a></span></li>
|
||||
<li><span><a href="#jsextend">Extending Java classes
|
||||
<li><span><a href="#jsextendabstract">Extending Abstract Java Classes
|
||||
</a></span></li>
|
||||
<li><span><a href="#jsextendconcrete">Extending Concrete Java Classes
|
||||
</a></span></li>
|
||||
<li><span><a href="#jsimplementmultiple">Implementing Multiple Java Interfaces
|
||||
</a></span></li>
|
||||
<li><span><a href="#classBoundImplementations">Class-Bound Implementations
|
||||
</a></span></li>
|
||||
<li><span><a href="#jsoverload">Overload Resolution</a></span></li>
|
||||
<li><span><a href="#dataTypeMapping">Mapping of Data Types Between Java
|
||||
and JavaScript</a></span></li>
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
<li><span><a href="#engineimpl">Implementing Your Own Script
|
||||
@ -466,10 +477,10 @@ language rather than JavaScript.</p>
|
||||
</code>
|
||||
</pre>
|
||||
|
||||
Note that the name of the type is always a string for a fully qualified name. You can use any of these types to create new instances, e.g.:
|
||||
Note that the name of the type is always a string for a fully qualified name. You can use any of these expressions to create new instances, e.g.:
|
||||
|
||||
<pre><code>
|
||||
var anArrayList = new Java.type("java.util.ArrayList")
|
||||
var anArrayList = new (Java.type("java.util.ArrayList"))
|
||||
</code></pre>
|
||||
|
||||
or
|
||||
@ -496,6 +507,37 @@ However, once you retrieved the outer class, you can access the inner class as a
|
||||
<p>
|
||||
You can access both static and non-static inner classes. If you want to create an instance of a non-static inner class, remember to pass an instance of its outer class as the first argument to the constructor.
|
||||
</p>
|
||||
<p>
|
||||
In addition to creating new instances, the type objects returned from <code>Java.type</code> calls can also be used to access the
|
||||
static fields and methods of the classes:
|
||||
<pre><code>
|
||||
var File = Java.type("java.io.File")
|
||||
File.createTempFile("nashorn", ".tmp")
|
||||
</code></pre>
|
||||
<p>
|
||||
Methods with names of the form <code>isXxx()</code>, <code>getXxx()</code>, and <code>setXxx()</code> can also be used as properties, for both instances and statics.
|
||||
</p>
|
||||
<p>
|
||||
A type object returned from <code>Java.type</code> is distinct from a <code>java.lang.Class</code> object. You can obtain one from the other using properties <code>class</code> and <code>static</code> on them.
|
||||
<pre><code>
|
||||
var ArrayList = Java.type("java.util.ArrayList")
|
||||
var a = new ArrayList
|
||||
|
||||
// All of the following print true:
|
||||
print("Type acts as target of instanceof: " + (a instanceof ArrayList))
|
||||
print("Class doesn't act as target of instanceof: " + !(a instanceof a.getClass()))
|
||||
print("Type is not same as instance's getClass(): " + (a.getClass() !== ArrayList))
|
||||
print("Type's `class` property is same as instance getClass(): " + (a.getClass() === ArrayList.class))
|
||||
print("Type is same as instance getClass()'s `static` property: " + (a.getClass().static === ArrayList))
|
||||
</code></pre>
|
||||
<p>
|
||||
You can think of the type object as similar to the class names as used in Java source code: you use them as the
|
||||
arguments to the <code>new</code> and <code>instanceof</code> operators and as the namespace for the static fields
|
||||
and methods, but they are different than the runtime <code>Class</code> objects returned by <code>getClass()</code> calls.
|
||||
Syntactically and semantically, this separation produces code that is most similar to Java code, where a distinction
|
||||
between compile-time class expressions and runtime class objects also exists. (Also, Java can't have the equivalent of <code>static</code>
|
||||
property on a <code>Class</code> object since compile-time class expressions are never reified as objects).
|
||||
</p>
|
||||
<hr>
|
||||
<a name="jsimport" id="jsimport"></a>
|
||||
<h3>Importing Java Packages, Classes</h3>
|
||||
@ -558,10 +600,7 @@ with (SwingGui) {
|
||||
<a name="jsarrays" id="jsarrays"></a>
|
||||
<h3>Creating, Converting and Using Java Arrays</h3>
|
||||
<p>
|
||||
Array element access or length access is
|
||||
the same as in Java. Also, a script array can be used when a Java
|
||||
method expects a Java array (auto conversion). So in most cases we
|
||||
don't have to create Java arrays explicitly.</p>
|
||||
Array element access or length access is the same as in Java.</p>
|
||||
<pre><code>
|
||||
// <a href="source/javaarray.js">javaarray.js</a>
|
||||
|
||||
@ -577,27 +616,31 @@ print(a[0]);
|
||||
</pre>
|
||||
<p>
|
||||
It is also possible to convert between JavaScript and Java arrays.
|
||||
Given a JavaScript array and a Java type, <code>Java.toJavaArray</code> returns a Java array with the same initial contents, and with the specified component type.
|
||||
Given a JavaScript array and a Java type, <code>Java.to</code> returns a Java array with the same initial contents, and with the specified array type.
|
||||
</p>
|
||||
<pre><code>
|
||||
var anArray = [1, "13", false]
|
||||
var javaIntArray = Java.toJavaArray(anArray, "int")
|
||||
var javaIntArray = Java.to(anArray, "int[]")
|
||||
print(javaIntArray[0]) // prints 1
|
||||
print(javaIntArray[1]) // prints 13, as string "13" was converted to number 13 as per ECMAScript ToNumber conversion
|
||||
print(javaIntArray[2]) // prints 0, as boolean false was converted to number 0 as per ECMAScript ToNumber conversion
|
||||
</code></pre>
|
||||
<p>
|
||||
Given a Java array or Collection, <code>Java.toJavaScriptArray</code> returns a JavaScript array with a shallow copy of its contents. Note that in most cases, you can use Java arrays and lists natively in Nashorn; in cases where for some reason you need to have an actual JavaScript native array (e.g. to work with the array comprehensions functions), you will want to use this method.i
|
||||
You can use either a string or a type object returned from <code>Java.type()</code> to specify the type of the array.
|
||||
You can also omit the array type, in which case a <code>Object[]</code> will be created.
|
||||
</p>
|
||||
<p>
|
||||
Given a Java array or Collection, <code>Java.from</code> returns a JavaScript array with a shallow copy of its contents. Note that in most cases, you can use Java arrays and lists natively in Nashorn; in cases where for some reason you need to have an actual JavaScript native array (e.g. to work with the array comprehensions functions), you will want to use this method.
|
||||
</p>
|
||||
<pre><code>
|
||||
var File = Java.type("java.io.File");
|
||||
var listCurDir = new File(".").listFiles();
|
||||
var jsList = Java.toJavaScriptArray(listCurDir);
|
||||
var jsList = Java.from(listCurDir);
|
||||
print(jsList);
|
||||
</code></pre>
|
||||
<hr>
|
||||
<a name="jsimplement" id="jsimplement"></a>
|
||||
<h3>Implementing Java Interfaces</h3>
|
||||
<h3>Implementing Java interfaces</h3>
|
||||
<p>A Java interface can be implemented in JavaScript by using a
|
||||
Java anonymous class-like syntax:</p>
|
||||
<pre><code>
|
||||
@ -631,8 +674,8 @@ th.join();
|
||||
</code>
|
||||
</pre>
|
||||
<hr>
|
||||
<a name="jsextend" id="jsextend"></a>
|
||||
<h3>Extending Java classes</h3>
|
||||
<a name="jsextendabstract" id="jsextendabstract"></a>
|
||||
<h3>Extending Abstract Java Classes</h3>
|
||||
<p>
|
||||
If a Java class is abstract, you can instantiate an anonymous subclass of it using an argument list that is applicable to any of its public or protected constructors, but inserting a JavaScript object with functions properties that provide JavaScript implementations of the abstract methods. If method names are overloaded, the JavaScript function will provide implementation for all overloads. E.g.:
|
||||
</p>
|
||||
@ -671,6 +714,9 @@ The use of functions can be taken even further; if you are invoking a Java metho
|
||||
|
||||
Here, <code>Timer.schedule()</code> expects a <code>TimerTask</code> as its argument, so Nashorn creates an instance of a TimerTask subclass and uses the passed function to implement its only abstract method, run(). In this usage though, you can't use non-default constructors; the type must be either an interface, or must have a protected or public no-arg constructor.
|
||||
|
||||
<hr>
|
||||
<a name="jsextendconcrete" id="jsextendconcrete"></a>
|
||||
<h3>Extending Concrete Java Classes</h3>
|
||||
<p>
|
||||
To extend a concrete Java class, you have to use <code>Java.extend</code> function.
|
||||
<code>Java.extend</code> returns a type object for a subclass of the specified Java class (or implementation of the specified interface) that acts as a script-to-Java adapter for it.
|
||||
@ -695,26 +741,178 @@ var printAddInvokedArrayList = new ArrayListExtender() {
|
||||
printSizeInvokedArrayList.size();
|
||||
printAddInvokedArrayList.add(33, 33);
|
||||
</code></pre>
|
||||
<p>
|
||||
The reason you must use <code>Java.extend()</code> with concrete classes is that with concrete classes, there can be a
|
||||
syntactic ambiguity if you just invoke their constructor. Consider this example:
|
||||
</p>
|
||||
<pre><code>
|
||||
var t = new java.lang.Thread({ run: function() { print("Hello!") } })
|
||||
</code></pre>
|
||||
<p>
|
||||
If we allowed subclassing of concrete classes with constructor syntax, Nashorn couldn't tell if you're creating a new
|
||||
<code>Thread</code> and passing it a <code>Runnable</code> at this point, or you are subclassing <code>Thread</code> and
|
||||
passing it a new implementation for its own <code>run()</code> method.
|
||||
</p>
|
||||
<hr>
|
||||
<a name="jsimplementmultiple" id="jsimplementmultiple"></a>
|
||||
<h3>Implementing Multiple Interfaces</h3>
|
||||
<p>
|
||||
<code>Java.extend</code> can in fact take a list of multiple types. At most one of the types can be a class, and the rest must
|
||||
be interfaces (the class doesn't have to be the first in the list). You will get back an object that extends the class and
|
||||
implements all the interfaces. (Obviously, if you only specify interfaces and no class, the object will extend <code>java.lang.Object</code>).
|
||||
<hr>
|
||||
<a name="classBoundImplementations" id="classBoundImplementations"></a>
|
||||
<h3>Class-Bound Implementations</h3>
|
||||
<p>
|
||||
The methods shown so far for extending Java classes and implementing interfaces – passing an implementation JavaScript object
|
||||
or function to a constructor, or using <code>Java.extend</code> with <code>new</code> – all produce classes that take an
|
||||
extra JavaScript object parameter in their constructors that specifies the implementation. The implementation is therefore always bound
|
||||
to the actual instance being created with <code>new</code>, and not to the whole class. This has some advantages, for example in the
|
||||
memory footprint of the runtime, as Nashorn can just create a single "universal adapter" for every combination of types being implemented.
|
||||
In reality, the below code shows that different instantiations of, say, <code>Runnable</code> have the same class regardless of them having
|
||||
different JavaScript implementation objects:
|
||||
</p>
|
||||
<pre><code>
|
||||
var Runnable = java.lang.Runnable;
|
||||
var r1 = new Runnable(function() { print("I'm runnable 1!") })
|
||||
var r2 = new Runnable(function() { print("I'm runnable 2!") })
|
||||
r1.run()
|
||||
r2.run()
|
||||
print("We share the same class: " + (r1.class === r2.class))
|
||||
</code></pre>
|
||||
<p>
|
||||
prints:
|
||||
</p>
|
||||
<pre><code>
|
||||
I'm runnable 1!
|
||||
I'm runnable 2!
|
||||
We share the same class: true
|
||||
</code></pre>
|
||||
<p>
|
||||
Sometimes, however, you'll want to extend a Java class or implement an interface with implementation bound to the class, not to
|
||||
its instances. Such a need arises, for example, when you need to pass the class for instantiation to an external API; prime example
|
||||
of this is the JavaFX framework where you need to pass an Application class to the FX API and let it instantiate it.
|
||||
</p>
|
||||
<p>
|
||||
Fortunately, there's a solution for that: <code>Java.extend()</code> – aside from being able to take any number of type parameters
|
||||
denoting a class to extend and interfaces to implement – can also take one last argument that has to be a JavaScript object
|
||||
that serves as the implementation for the methods. In this case, <code>Java.extend()</code> will create a class that has the same
|
||||
constructors as the original class had, as they don't need to take an an extra implementation object parameter. The example below
|
||||
shows how you can create class-bound implementations, and shows that in this case, the implementation classes for different invocations
|
||||
are indeed different:
|
||||
</p>
|
||||
<pre><code>
|
||||
var RunnableImpl1 = Java.extend(java.lang.Runnable, function() { print("I'm runnable 1!") })
|
||||
var RunnableImpl2 = Java.extend(java.lang.Runnable, function() { print("I'm runnable 2!") })
|
||||
var r1 = new RunnableImpl1()
|
||||
var r2 = new RunnableImpl2()
|
||||
r1.run()
|
||||
r2.run()
|
||||
print("We share the same class: " + (r1.class === r2.class))
|
||||
</code></pre>
|
||||
<p>
|
||||
prints:
|
||||
</p>
|
||||
<pre><code>
|
||||
I'm runnable 1!
|
||||
I'm runnable 2!
|
||||
We share the same class: false
|
||||
</code></pre>
|
||||
<p>
|
||||
As you can see, the major difference here is that we moved the implementation object into the invocation of <code>Java.extend</code>
|
||||
from the constructor invocations – indeed the constructor invocations now don't even need to take an extra parameter! Since
|
||||
the implementations are bound to a class, the two classes obviously can't be the same, and we indeed see that the two runnables no
|
||||
longer share the same class – every invocation of <code>Java.extend()</code> with a class-specific implementation object triggers
|
||||
the creation of a new Java adapter class.
|
||||
</p>
|
||||
<p>
|
||||
Finally, the adapter classes with class-bound implementations can <i>still</i> take an additional constructor parameter to further
|
||||
override the behavior on a per-instance basis. Thus, you can even combine the two approaches: you can provide part of the implementation
|
||||
in a class-based JavaScript implementation object passed to <code>Java.extend</code>, and part in another object passed to the constructor.
|
||||
Whatever functions are provided by the constructor-passed object will override the functions in the class-bound object.
|
||||
</p>
|
||||
<pre><code>
|
||||
var RunnableImpl = Java.extend(java.lang.Runnable, function() { print("I'm runnable 1!") })
|
||||
var r1 = new RunnableImpl()
|
||||
var r2 = new RunnableImpl(function() { print("I'm runnable 2!") })
|
||||
r1.run()
|
||||
r2.run()
|
||||
print("We share the same class: " + (r1.class === r2.class))
|
||||
</code></pre>
|
||||
<p>
|
||||
prints:
|
||||
</p>
|
||||
<pre><code>
|
||||
I'm runnable 1!
|
||||
I'm runnable 2!
|
||||
We share the same class: true
|
||||
</code></pre>
|
||||
<hr>
|
||||
<a name="jsoverload" id="jsoverload"></a>
|
||||
<h3>Overload Resolution</h3>
|
||||
<p>Java methods can be overloaded by argument types. In Java,
|
||||
overload resolution occurs at compile time (performed by javac).
|
||||
When calling Java methods from a script, the script
|
||||
interpreter/compiler needs to select the appropriate method. With
|
||||
the JavaScript engine, you do not need to do anything special - the
|
||||
correct Java method overload variant is selected based on the
|
||||
argument types. But, sometimes you may want (or have) to explicitly
|
||||
select a particular overload variant.</p>
|
||||
When calling Java methods from Nashorn, the appropriate method will be
|
||||
selected based on the argument types at invocation time. You do not need
|
||||
to do anything special – the correct Java method overload variant
|
||||
is selected based automatically. You still have the option of explicitly
|
||||
specifying a particular overload variant. Reasons for this include
|
||||
either running into a genuine ambiguity with actual argument types, or
|
||||
rarely reasons of performance – if you specify the actual overload
|
||||
then the engine doesn't have to perform resolution during invocation.
|
||||
Individual overloads of a Java methods are exposed as special properties
|
||||
with the name of the method followed with its signature in parentheses.
|
||||
You can invoke them like this:</p>
|
||||
<pre><code>
|
||||
// <a href="source/overload.js">overload.js</a>
|
||||
|
||||
var out = java.lang.System.out;
|
||||
|
||||
// select a particular print function
|
||||
out["println(java.lang.Object)"]("hello");
|
||||
out["println(Object)"]("hello");
|
||||
</code>
|
||||
</pre>
|
||||
<p>
|
||||
Note that you normally don't even have to use qualified class names in
|
||||
the signatures as long as the unqualified name of the type is sufficient
|
||||
for uniquely identifying the signature. In practice this means that only
|
||||
in the extremely unlikely case that two overloads only differ in
|
||||
parameter types that have identical unqualified names but come from
|
||||
different packages would you need to use the fully qualified name of the
|
||||
class.
|
||||
</p>
|
||||
<hr>
|
||||
<a name="dataTypeMapping" id="dataTypeMapping"></a>
|
||||
<h3>Mapping of Data Types Between Java and JavaScript</h3>
|
||||
<p>
|
||||
We have previously shown some of the data type mappings between Java and JavaScript.
|
||||
We saw that arrays need to be explicitly converted. We have also shown that JavaScript functions
|
||||
are automatically converted to SAM types when passed as parameters to Java methods. Most other
|
||||
conversions work as you would expect.
|
||||
</p>
|
||||
<p>
|
||||
Every JavaScript object is also a <code>java.util.Map</code> so APIs receiving maps will receive them directly.
|
||||
</p>
|
||||
<p>
|
||||
When numbers are passed to a Java API, they will be converted to the expected target numeric type, either boxed or
|
||||
primitive, but if the target type is less specific, say <code>Number</code> or <code>Object</code>, you can only
|
||||
count on them being a <code>Number</code>, and have to test specifically for whether it's a boxed <code>Double</code>,
|
||||
<code>Integer</code>, <code>Long</code>, etc. – it can be any of these due to internal optimizations. Also, you
|
||||
can pass any JavaScript value to a Java API expecting either a boxed or primitive number; the JavaScript specification's
|
||||
<code>ToNumber</code> conversion algorithm will be applied to the value.
|
||||
</p>
|
||||
<p>
|
||||
In a similar vein, if a Java method expects a <code>String</code> or a <code>Boolean</code>, the values will be
|
||||
converted using all conversions allowed by the JavaScript specification's <code>ToString</code> and <code>ToBoolean</code>
|
||||
conversions.
|
||||
</p>
|
||||
<p>
|
||||
Finally, a word of caution about strings. Due to internal performance optimizations of string operations, JavaScript strings are
|
||||
not always necessarily of type <code>java.lang.String</code>, but they will always be of type <code>java.lang.CharSequence</code>.
|
||||
If you pass them to a Java method that expects a <code>java.lang.String</code> parameter, then you will naturally receive a Java
|
||||
String, but if the signature of your method is more generic, i.e. it receives a <code>java.lang.Object</code> parameter, you can
|
||||
end up with an object of private engine implementation class that implements <code>CharSequence</code> but is not a Java String.
|
||||
</p>
|
||||
<hr>
|
||||
<a name="engineimpl" id="engineimpl"></a>
|
||||
<h2>Implementing Your Own Script Engine</h2>
|
||||
|
@ -40,7 +40,7 @@ print(a[0]);
|
||||
|
||||
// convert a script array to Java array
|
||||
var anArray = [1, "13", false];
|
||||
var javaIntArray = Java.toJavaArray(anArray, "int");
|
||||
var javaIntArray = Java.to(anArray, "int[]");
|
||||
print(javaIntArray[0]);// prints 1
|
||||
print(javaIntArray[1]); // prints 13, as string "13" was converted to number 13 as per ECMAScript ToNumber conversion
|
||||
print(javaIntArray[2]);// prints 0, as boolean false was converted to number 0 as per ECMAScript ToNumber conversion
|
||||
@ -48,5 +48,5 @@ print(javaIntArray[2]);// prints 0, as boolean false was converted to number 0 a
|
||||
// convert a Java array to a JavaScript array
|
||||
var File = Java.type("java.io.File");
|
||||
var listCurDir = new File(".").listFiles();
|
||||
var jsList = Java.toJavaScriptArray(listCurDir);
|
||||
var jsList = Java.from(listCurDir);
|
||||
print(jsList);
|
||||
|
@ -24,258 +24,270 @@
|
||||
<project name="nashorn-benchmarks" default="all" basedir="..">
|
||||
|
||||
<target name="octane-init" depends="jar">
|
||||
<fileset id="octane-set"
|
||||
dir="${octane-test-sys-prop.test.js.roots}"
|
||||
excludes="${octane-test-sys-prop.test.js.exclude.list}">
|
||||
<include name="*.js"/>
|
||||
</fileset>
|
||||
<pathconvert pathsep=" " property="octane-tests" refid="octane-set"/>
|
||||
<property name="octane-tests" value="box2d code-load crypto deltablue earley-boyer gbemu navier-stokes pdfjs raytrace regexp richards splay"/>
|
||||
</target>
|
||||
|
||||
<!-- ignore benchmarks where rhino crashes -->
|
||||
<target name="octane-init-rhino" depends="jar">
|
||||
<property name="octane-tests" value="box2d code-load crypto deltablue earley-boyer gbemu navier-stokes raytrace regexp richards splay"/>
|
||||
</target>
|
||||
|
||||
<!-- box2d -->
|
||||
<target name="octane-box2d" depends="jar">
|
||||
<antcall target="run-octane">
|
||||
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/box2d.js"/>
|
||||
<param name="octane-tests" value="box2d"/>
|
||||
</antcall>
|
||||
</target>
|
||||
|
||||
<target name="octane-box2d-v8" depends="jar">
|
||||
<antcall target="run-octane-v8">
|
||||
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/box2d.js"/>
|
||||
<param name="octane-tests" value="box2d"/>
|
||||
</antcall>
|
||||
</target>
|
||||
|
||||
<target name="octane-box2d-rhino" depends="jar">
|
||||
<antcall target="run-octane-rhino">
|
||||
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/box2d.js"/>
|
||||
<param name="octane-tests" value="box2d"/>
|
||||
</antcall>
|
||||
</target>
|
||||
|
||||
|
||||
<!-- code-load -->
|
||||
<target name="octane-code-load" depends="jar">
|
||||
<antcall target="run-octane">
|
||||
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/code-load.js"/>
|
||||
<param name="octane-tests" value="code-load"/>
|
||||
</antcall>
|
||||
</target>
|
||||
|
||||
<target name="octane-code-load-v8" depends="jar">
|
||||
<antcall target="run-octane-v8">
|
||||
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/code-load.js"/>
|
||||
<param name="octane-tests" value="code-load"/>
|
||||
</antcall>
|
||||
</target>
|
||||
|
||||
<target name="octane-code-load-rhino" depends="jar">
|
||||
<antcall target="run-octane-rhino">
|
||||
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/code-load.js"/>
|
||||
<param name="octane-tests" value="code-load"/>
|
||||
</antcall>
|
||||
</target>
|
||||
|
||||
|
||||
<!-- crypto -->
|
||||
<target name="octane-crypto" depends="jar">
|
||||
<antcall target="run-octane">
|
||||
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/crypto.js"/>
|
||||
<param name="octane-tests" value="crypto"/>
|
||||
</antcall>
|
||||
</target>
|
||||
|
||||
<target name="octane-crypto-v8" depends="jar">
|
||||
<antcall target="run-octane-v8">
|
||||
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/crypto.js"/>
|
||||
<param name="octane-tests" value="crypto"/>
|
||||
</antcall>
|
||||
</target>
|
||||
|
||||
<target name="octane-crypto-rhino" depends="jar">
|
||||
<antcall target="run-octane-rhino">
|
||||
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/crypto.js"/>
|
||||
<param name="octane-tests" value="crypto"/>
|
||||
</antcall>
|
||||
</target>
|
||||
|
||||
|
||||
<!-- deltablue -->
|
||||
<target name="octane-deltablue" depends="jar">
|
||||
<antcall target="run-octane">
|
||||
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/deltablue.js"/>
|
||||
<param name="octane-tests" value="deltablue"/>
|
||||
</antcall>
|
||||
</target>
|
||||
|
||||
<target name="octane-deltablue-v8" depends="jar">
|
||||
<antcall target="run-octane-v8">
|
||||
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/deltablue.js"/>
|
||||
<param name="octane-tests" value="deltablue"/>
|
||||
</antcall>
|
||||
</target>
|
||||
|
||||
<target name="octane-deltablue-rhino" depends="jar">
|
||||
<antcall target="run-octane-rhino">
|
||||
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/deltablue.js"/>
|
||||
<param name="octane-tests" value="deltablue"/>
|
||||
</antcall>
|
||||
</target>
|
||||
|
||||
|
||||
<!-- earley-boyer -->
|
||||
<target name="octane-earley-boyer" depends="jar">
|
||||
<antcall target="run-octane">
|
||||
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/earley-boyer.js"/>
|
||||
<param name="octane-tests" value="earley-boyer"/>
|
||||
</antcall>
|
||||
</target>
|
||||
|
||||
<target name="octane-earley-boyer-v8" depends="jar">
|
||||
<antcall target="run-octane-v8">
|
||||
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/earley-boyer.js"/>
|
||||
<param name="octane-tests" value="earley-boyer"/>
|
||||
</antcall>
|
||||
</target>
|
||||
|
||||
<target name="octane-earley-boyer-rhino" depends="jar">
|
||||
<antcall target="run-octane-rhino">
|
||||
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/earley-boyer.js"/>
|
||||
<param name="octane-tests" value="earley-boyer"/>
|
||||
</antcall>
|
||||
</target>
|
||||
|
||||
|
||||
<!-- gbemu -->
|
||||
<target name="octane-gbemu" depends="jar">
|
||||
<antcall target="run-octane">
|
||||
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/gbemu.js"/>
|
||||
<param name="octane-tests" value="gbemu"/>
|
||||
</antcall>
|
||||
</target>
|
||||
|
||||
<target name="octane-gbemu-v8" depends="jar">
|
||||
<antcall target="run-octane-v8">
|
||||
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/gbemu.js"/>
|
||||
<param name="octane-tests" value="gbemu"/>
|
||||
</antcall>
|
||||
</target>
|
||||
|
||||
<target name="octane-gbemu-rhino" depends="jar">
|
||||
<antcall target="run-octane-rhino">
|
||||
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/gbemu.js"/>
|
||||
<param name="octane-tests" value="gbemu"/>
|
||||
</antcall>
|
||||
</target>
|
||||
|
||||
|
||||
<!-- mandreel -->
|
||||
<target name="octane-mandreel" depends="jar">
|
||||
<antcall target="run-octane">
|
||||
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/mandreel.js"/>
|
||||
<param name="octane-tests" value="mandreel"/>
|
||||
</antcall>
|
||||
</target>
|
||||
|
||||
<target name="octane-mandreel-v8" depends="jar">
|
||||
<antcall target="run-octane-v8">
|
||||
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/mandreel.js"/>
|
||||
<param name="octane-tests" value="mandreel"/>
|
||||
</antcall>
|
||||
</target>
|
||||
|
||||
<target name="octane-mandreel-rhino" depends="jar">
|
||||
<antcall target="run-octane-rhino">
|
||||
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/mandreel.js"/>
|
||||
<param name="octane-tests" value="mandreel"/>
|
||||
</antcall>
|
||||
</target>
|
||||
|
||||
|
||||
<!-- navier-stokes -->
|
||||
<target name="octane-navier-stokes" depends="jar">
|
||||
<antcall target="run-octane">
|
||||
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/navier-stokes.js"/>
|
||||
<param name="octane-tests" value="navier-stokes"/>
|
||||
</antcall>
|
||||
</target>
|
||||
|
||||
<target name="octane-navier-stokes-v8" depends="jar">
|
||||
<antcall target="run-octane-v8">
|
||||
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/navier-stokes.js"/>
|
||||
<param name="octane-tests" value="navier-stokes"/>
|
||||
</antcall>
|
||||
</target>
|
||||
|
||||
<target name="octane-navier-stokes-rhino" depends="jar">
|
||||
<antcall target="run-octane-rhino">
|
||||
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/navier-stokes.js"/>
|
||||
<param name="octane-tests" value="navier-stokes"/>
|
||||
</antcall>
|
||||
</target>
|
||||
|
||||
|
||||
<!-- pdfjs -->
|
||||
<target name="octane-pdfjs" depends="jar">
|
||||
<antcall target="run-octane">
|
||||
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/pdfjs.js"/>
|
||||
<param name="octane-tests" value="pdfjs"/>
|
||||
</antcall>
|
||||
</target>
|
||||
|
||||
<target name="octane-pdfjs-v8" depends="jar">
|
||||
<antcall target="run-octane-v8">
|
||||
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/pdfjs.js"/>
|
||||
<param name="octane-tests" value="pdfjs"/>
|
||||
</antcall>
|
||||
</target>
|
||||
|
||||
<target name="octane-pdfjs-rhino" depends="jar">
|
||||
<antcall target="run-octane-rhino">
|
||||
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/pdfjs.js"/>
|
||||
<param name="octane-tests" value="pdfjs"/>
|
||||
</antcall>
|
||||
</target>
|
||||
|
||||
|
||||
<!-- raytrace -->
|
||||
<target name="octane-raytrace" depends="jar">
|
||||
<antcall target="run-octane">
|
||||
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/raytrace.js"/>
|
||||
<param name="octane-tests" value="raytrace"/>
|
||||
</antcall>
|
||||
</target>
|
||||
|
||||
<target name="octane-raytrace-v8" depends="jar">
|
||||
<antcall target="run-octane-v8">
|
||||
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/raytrace.js"/>
|
||||
<param name="octane-tests" value="raytrace"/>
|
||||
</antcall>
|
||||
</target>
|
||||
|
||||
<target name="octane-raytrace-rhino" depends="jar">
|
||||
<antcall target="run-octane-rhino">
|
||||
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/raytrace.js"/>
|
||||
<param name="octane-tests" value="raytrace"/>
|
||||
</antcall>
|
||||
</target>
|
||||
|
||||
|
||||
<!-- regexp -->
|
||||
<target name="octane-regexp" depends="jar">
|
||||
<antcall target="run-octane">
|
||||
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/regexp.js"/>
|
||||
<param name="octane-tests" value="regexp"/>
|
||||
</antcall>
|
||||
</target>
|
||||
|
||||
<target name="octane-regexp-octane-v8" depends="jar">
|
||||
<antcall target="run-octane-v8">
|
||||
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/regexp.js"/>
|
||||
<param name="octane-tests" value="regexp"/>
|
||||
</antcall>
|
||||
</target>
|
||||
|
||||
<target name="octane-regexp-rhino" depends="jar">
|
||||
<antcall target="run-octane-rhino">
|
||||
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/regexp.js"/>
|
||||
<param name="octane-tests" value="regexp"/>
|
||||
</antcall>
|
||||
</target>
|
||||
|
||||
|
||||
<!-- richards -->
|
||||
<target name="octane-richards" depends="jar">
|
||||
<antcall target="run-octane">
|
||||
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/richards.js"/>
|
||||
<param name="octane-tests" value="richards"/>
|
||||
</antcall>
|
||||
</target>
|
||||
|
||||
<target name="octane-richards-v8" depends="jar">
|
||||
<antcall target="run-octane-v8">
|
||||
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/richards.js"/>
|
||||
<param name="octane-tests" value="richards"/>
|
||||
</antcall>
|
||||
</target>
|
||||
|
||||
<target name="octane-richards-rhino" depends="jar">
|
||||
<antcall target="run-octane-rhino">
|
||||
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/richards.js"/>
|
||||
<param name="octane-tests" value="richards"/>
|
||||
</antcall>
|
||||
</target>
|
||||
|
||||
|
||||
<!-- splay -->
|
||||
<target name="octane-splay" depends="jar">
|
||||
<antcall target="run-octane">
|
||||
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/splay.js"/>
|
||||
<param name="octane-tests" value="splay"/>
|
||||
</antcall>
|
||||
</target>
|
||||
|
||||
<target name="octane-splay-v8" depends="jar">
|
||||
<antcall target="run-octane-v8">
|
||||
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/splay.js"/>
|
||||
<param name="octane-tests" value="splay"/>
|
||||
</antcall>
|
||||
</target>
|
||||
|
||||
<target name="octane-splay-rhino" depends="jar">
|
||||
<antcall target="run-octane-rhino">
|
||||
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/splay.js"/>
|
||||
<param name="octane-tests" value="splay"/>
|
||||
</antcall>
|
||||
</target>
|
||||
|
||||
@ -307,7 +319,7 @@
|
||||
</target>
|
||||
|
||||
<!-- run octane benchmarks using Rhino as runtime -->
|
||||
<target name="octane-rhino" depends="octane-init">
|
||||
<target name="octane-rhino" depends="octane-init-rhino">
|
||||
<antcall target="run-octane-rhino"/>
|
||||
</target>
|
||||
|
||||
|
@ -212,7 +212,9 @@
|
||||
target="${javac.target}"
|
||||
debug="${javac.debug}"
|
||||
encoding="${javac.encoding}"
|
||||
includeantruntime="false"/>
|
||||
includeantruntime="false">
|
||||
<compilerarg line="-extdirs """/>
|
||||
</javac>
|
||||
|
||||
<!-- tests that check nashorn internals and internal API -->
|
||||
<jar jarfile="${nashorn.internal.tests.jar}">
|
||||
@ -305,6 +307,8 @@
|
||||
<include name="**/codegen/*Test.class"/>
|
||||
<include name="**/parser/*Test.class"/>
|
||||
<include name="**/runtime/*Test.class"/>
|
||||
<include name="**/runtime/regexp/*Test.class"/>
|
||||
<include name="**/runtime/regexp/joni/*Test.class"/>
|
||||
<include name="**/framework/*Test.class"/>
|
||||
</fileset>
|
||||
|
||||
|
@ -139,6 +139,32 @@
|
||||
<arg value="${cc.merged.xml}"/>
|
||||
<arg value="-exclude"/>
|
||||
<arg value="com\.oracle\.nashorn\.runtime\.ScriptRuntime*"/>
|
||||
<arg value="-exclude"/>
|
||||
<arg value="jdk\.nashorn\.internal\.javaadapters*"/>
|
||||
<arg value="-exclude"/>
|
||||
<arg value="jdk\.nashorn\.internal\.objects\.annotations*"/>
|
||||
<arg value="-exclude"/>
|
||||
<arg value="jdk\.nashorn\.internal\.scripts*"/>
|
||||
<arg value="-exclude"/>
|
||||
<arg value="jdk\.nashorn\.internal\.lookup\.MethodHandleFactory*"/>
|
||||
<arg value="-exclude"/>
|
||||
<arg value="jdk\.nashorn\.internal\.test\.framework*"/>
|
||||
<arg value="-exclude"/>
|
||||
<arg value="jdk\.nashorn\.test\.models*"/>
|
||||
<arg value="-exclude"/>
|
||||
<arg value="jdk\.nashorn\.internal\.ir\.debug*"/>
|
||||
<arg value="-exclude"/>
|
||||
<arg value="jdk\.nashorn\.internal\.runtime\.regexp\.joni\.bench*"/>
|
||||
<arg value="-exclude"/>
|
||||
<arg value="jdk\.nashorn\.internal\.runtime\.DebugLogger*"/>
|
||||
<arg value="-exclude"/>
|
||||
<arg value="jdk\.nashorn\.internal\.runtime\.Timing*"/>
|
||||
<arg value="-exclude"/>
|
||||
<arg value="jdk\.nashorn\.internal\.runtime\.Logging*"/>
|
||||
<arg value="-exclude"/>
|
||||
<arg value="jdk\.nashorn\.internal\.runtime\.Debug*"/>
|
||||
<arg value="-exclude"/>
|
||||
<arg value="jdk\.nashorn\.internal\.objects\.NativeDebug*"/>
|
||||
<arg line="${cc.all.xmls}"/>
|
||||
<classpath>
|
||||
<pathelement location="${jcov.jar}"/>
|
||||
|
@ -87,6 +87,7 @@ testng.verbose=2
|
||||
|
||||
testng.listeners=\
|
||||
org.testng.reporters.SuiteHTMLReporter, \
|
||||
org.testng.reporters.TestHTMLReporter, \
|
||||
org.testng.reporters.jq.Main, \
|
||||
org.testng.reporters.FailedReporter, \
|
||||
org.testng.reporters.XMLReporter \
|
||||
@ -214,9 +215,13 @@ test.src.dir=test/src
|
||||
run.test.xmx=3G
|
||||
run.test.xms=2G
|
||||
|
||||
run.test.user.language=tr
|
||||
run.test.user.country=TR
|
||||
|
||||
# -XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintNMethods
|
||||
# add '-Dtest.js.outofprocess' to run each test in a new sub-process
|
||||
run.test.jvmargs.main=-server -Xmx${run.test.xmx} -XX:+TieredCompilation -ea -Dnashorn.debug=true -Dfile.encoding=UTF-8
|
||||
run.test.jvmargs.main=-server -Xmx${run.test.xmx} -XX:+TieredCompilation -ea -Dfile.encoding=UTF-8 -Duser.language=${run.test.user.language} -Duser.country=${run.test.user.country}
|
||||
|
||||
#-XX:+HeapDumpOnOutOfMemoryError -XX:-UseCompressedKlassPointers -XX:+PrintHeapAtGC -XX:ClassMetaspaceSize=300M
|
||||
run.test.jvmargs.octane.main=-Xms${run.test.xms} ${run.test.jvmargs.main}
|
||||
|
||||
|
@ -23,26 +23,12 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package netscape.javascript;
|
||||
|
||||
import java.applet.Applet;
|
||||
package jdk.nashorn.api.scripting;
|
||||
|
||||
/**
|
||||
* Stub for JSObject to get compilation going.
|
||||
* netscape.javascript.JSObject-like interface for nashorn script objects.
|
||||
*/
|
||||
public abstract class JSObject {
|
||||
|
||||
/**
|
||||
* Get the window for an {@link Applet}. Not supported
|
||||
* by Nashorn
|
||||
*
|
||||
* @param a applet
|
||||
* @return the window instance
|
||||
*/
|
||||
public static JSObject getWindow(final Applet a) {
|
||||
throw new UnsupportedOperationException("getWindow");
|
||||
}
|
||||
|
||||
/**
|
||||
* Call a JavaScript method
|
||||
*
|
@ -42,7 +42,6 @@ import jdk.nashorn.internal.runtime.Context;
|
||||
import jdk.nashorn.internal.runtime.ScriptFunction;
|
||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
import jdk.nashorn.internal.runtime.ScriptRuntime;
|
||||
import netscape.javascript.JSObject;
|
||||
|
||||
/**
|
||||
* Mirror object that wraps a given ScriptObject instance. User can
|
||||
|
@ -88,7 +88,7 @@ Object.defineProperty(this, "sprintf", {
|
||||
}
|
||||
}
|
||||
|
||||
array = Java.toJavaArray(array);
|
||||
array = Java.to(array);
|
||||
return Packages.jdk.nashorn.api.scripting.ScriptUtils.format(format, array);
|
||||
}
|
||||
});
|
||||
|
@ -84,8 +84,8 @@ import jdk.nashorn.internal.ir.TryNode;
|
||||
import jdk.nashorn.internal.ir.UnaryNode;
|
||||
import jdk.nashorn.internal.ir.VarNode;
|
||||
import jdk.nashorn.internal.ir.WithNode;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
|
||||
import jdk.nashorn.internal.parser.TokenType;
|
||||
import jdk.nashorn.internal.runtime.Context;
|
||||
import jdk.nashorn.internal.runtime.Debug;
|
||||
@ -111,7 +111,7 @@ import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
* computed.
|
||||
*/
|
||||
|
||||
final class Attr extends NodeOperatorVisitor {
|
||||
final class Attr extends NodeOperatorVisitor<LexicalContext> {
|
||||
|
||||
/**
|
||||
* Local definitions in current block (to discriminate from function
|
||||
@ -138,6 +138,7 @@ final class Attr extends NodeOperatorVisitor {
|
||||
* Constructor.
|
||||
*/
|
||||
Attr(final TemporarySymbols temporarySymbols) {
|
||||
super(new LexicalContext());
|
||||
this.temporarySymbols = temporarySymbols;
|
||||
this.localDefs = new ArrayDeque<>();
|
||||
this.localUses = new ArrayDeque<>();
|
||||
@ -202,7 +203,7 @@ final class Attr extends NodeOperatorVisitor {
|
||||
private void acceptDeclarations(final FunctionNode functionNode, final Block body) {
|
||||
// This visitor will assign symbol to all declared variables, except function declarations (which are taken care
|
||||
// in a separate step above) and "var" declarations in for loop initializers.
|
||||
body.accept(new NodeOperatorVisitor() {
|
||||
body.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
|
||||
@Override
|
||||
public boolean enterFunctionNode(final FunctionNode nestedFn) {
|
||||
return false;
|
||||
@ -218,7 +219,7 @@ final class Attr extends NodeOperatorVisitor {
|
||||
if (varNode.isFunctionDeclaration()) {
|
||||
newType(symbol, FunctionNode.FUNCTION_TYPE);
|
||||
}
|
||||
return varNode.setName((IdentNode)ident.setSymbol(getLexicalContext(), symbol));
|
||||
return varNode.setName((IdentNode)ident.setSymbol(lc, symbol));
|
||||
}
|
||||
return varNode;
|
||||
}
|
||||
@ -227,8 +228,8 @@ final class Attr extends NodeOperatorVisitor {
|
||||
|
||||
private void enterFunctionBody() {
|
||||
|
||||
final FunctionNode functionNode = getLexicalContext().getCurrentFunction();
|
||||
final Block body = getLexicalContext().getCurrentBlock();
|
||||
final FunctionNode functionNode = lc.getCurrentFunction();
|
||||
final Block body = lc.getCurrentBlock();
|
||||
|
||||
initFunctionWideVariables(functionNode, body);
|
||||
|
||||
@ -256,7 +257,7 @@ final class Attr extends NodeOperatorVisitor {
|
||||
//the symbols in the block should really be stateless
|
||||
block.clearSymbols();
|
||||
|
||||
if (getLexicalContext().isFunctionBody()) {
|
||||
if (lc.isFunctionBody()) {
|
||||
enterFunctionBody();
|
||||
}
|
||||
pushLocalsBlock();
|
||||
@ -283,7 +284,7 @@ final class Attr extends NodeOperatorVisitor {
|
||||
@Override
|
||||
public boolean enterCatchNode(final CatchNode catchNode) {
|
||||
final IdentNode exception = catchNode.getException();
|
||||
final Block block = getLexicalContext().getCurrentBlock();
|
||||
final Block block = lc.getCurrentBlock();
|
||||
|
||||
start(catchNode);
|
||||
|
||||
@ -298,10 +299,10 @@ final class Attr extends NodeOperatorVisitor {
|
||||
@Override
|
||||
public Node leaveCatchNode(final CatchNode catchNode) {
|
||||
final IdentNode exception = catchNode.getException();
|
||||
final Block block = getLexicalContext().getCurrentBlock();
|
||||
final Block block = lc.getCurrentBlock();
|
||||
final Symbol symbol = findSymbol(block, exception.getName());
|
||||
assert symbol != null;
|
||||
return end(catchNode.setException((IdentNode)exception.setSymbol(getLexicalContext(), symbol)));
|
||||
return end(catchNode.setException((IdentNode)exception.setSymbol(lc, symbol)));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -320,7 +321,7 @@ final class Attr extends NodeOperatorVisitor {
|
||||
flags |= IS_SCOPE;
|
||||
}
|
||||
|
||||
final FunctionNode function = getLexicalContext().getFunction(block);
|
||||
final FunctionNode function = lc.getFunction(block);
|
||||
if (symbol != null) {
|
||||
// Symbol was already defined. Check if it needs to be redefined.
|
||||
if ((flags & KINDMASK) == IS_PARAM) {
|
||||
@ -353,12 +354,12 @@ final class Attr extends NodeOperatorVisitor {
|
||||
if ((flags & Symbol.KINDMASK) == IS_VAR && ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET)) {
|
||||
symbolBlock = block; //internal vars are always defined in the block closest to them
|
||||
} else {
|
||||
symbolBlock = getLexicalContext().getFunctionBody(function);
|
||||
symbolBlock = lc.getFunctionBody(function);
|
||||
}
|
||||
|
||||
// Create and add to appropriate block.
|
||||
symbol = new Symbol(name, flags);
|
||||
symbolBlock.putSymbol(getLexicalContext(), symbol);
|
||||
symbolBlock.putSymbol(lc, symbol);
|
||||
|
||||
if ((flags & Symbol.KINDMASK) != IS_GLOBAL) {
|
||||
symbol.setNeedsSlot(true);
|
||||
@ -381,7 +382,7 @@ final class Attr extends NodeOperatorVisitor {
|
||||
//an outermost function in our lexical context that is not a program (runScript)
|
||||
//is possible - it is a function being compiled lazily
|
||||
if (functionNode.isDeclared()) {
|
||||
final Iterator<Block> blocks = getLexicalContext().getBlocks();
|
||||
final Iterator<Block> blocks = lc.getBlocks();
|
||||
if (blocks.hasNext()) {
|
||||
defineSymbol(blocks.next(), functionNode.getIdent().getName(), IS_VAR);
|
||||
}
|
||||
@ -397,13 +398,11 @@ final class Attr extends NodeOperatorVisitor {
|
||||
public Node leaveFunctionNode(final FunctionNode functionNode) {
|
||||
FunctionNode newFunctionNode = functionNode;
|
||||
|
||||
final LexicalContext lc = getLexicalContext();
|
||||
|
||||
final Block body = newFunctionNode.getBody();
|
||||
|
||||
//look for this function in the parent block
|
||||
if (functionNode.isDeclared()) {
|
||||
final Iterator<Block> blocks = getLexicalContext().getBlocks();
|
||||
final Iterator<Block> blocks = lc.getBlocks();
|
||||
if (blocks.hasNext()) {
|
||||
newFunctionNode = (FunctionNode)newFunctionNode.setSymbol(lc, findSymbol(blocks.next(), functionNode.getIdent().getName()));
|
||||
}
|
||||
@ -411,7 +410,7 @@ final class Attr extends NodeOperatorVisitor {
|
||||
final boolean anonymous = functionNode.isAnonymous();
|
||||
final String name = anonymous ? null : functionNode.getIdent().getName();
|
||||
if (anonymous || body.getExistingSymbol(name) != null) {
|
||||
newFunctionNode = (FunctionNode)ensureSymbol(lc, FunctionNode.FUNCTION_TYPE, newFunctionNode);
|
||||
newFunctionNode = (FunctionNode)ensureSymbol(FunctionNode.FUNCTION_TYPE, newFunctionNode);
|
||||
} else {
|
||||
assert name != null;
|
||||
final Symbol self = body.getExistingSymbol(name);
|
||||
@ -490,8 +489,6 @@ final class Attr extends NodeOperatorVisitor {
|
||||
|
||||
start(identNode);
|
||||
|
||||
final LexicalContext lc = getLexicalContext();
|
||||
|
||||
if (identNode.isPropertyName()) {
|
||||
// assign a pseudo symbol to property name
|
||||
final Symbol pseudoSymbol = pseudoSymbol(name);
|
||||
@ -549,7 +546,7 @@ final class Attr extends NodeOperatorVisitor {
|
||||
*/
|
||||
private void maybeForceScope(final Symbol symbol) {
|
||||
if (!symbol.isScope() && symbolNeedsToBeScope(symbol)) {
|
||||
Symbol.setSymbolIsScope(getLexicalContext(), symbol);
|
||||
Symbol.setSymbolIsScope(lc, symbol);
|
||||
}
|
||||
}
|
||||
|
||||
@ -558,7 +555,7 @@ final class Attr extends NodeOperatorVisitor {
|
||||
return false;
|
||||
}
|
||||
boolean previousWasBlock = false;
|
||||
for(final Iterator<LexicalContextNode> it = getLexicalContext().getAllNodes(); it.hasNext();) {
|
||||
for(final Iterator<LexicalContextNode> it = lc.getAllNodes(); it.hasNext();) {
|
||||
final LexicalContextNode node = it.next();
|
||||
if(node instanceof FunctionNode) {
|
||||
// We reached the function boundary without seeing a definition for the symbol - it needs to be in
|
||||
@ -594,10 +591,8 @@ final class Attr extends NodeOperatorVisitor {
|
||||
}
|
||||
|
||||
if (symbol.isScope()) {
|
||||
final LexicalContext lc = getLexicalContext();
|
||||
|
||||
Block scopeBlock = null;
|
||||
for (final Iterator<LexicalContextNode> contextNodeIter = getLexicalContext().getAllNodes(); contextNodeIter.hasNext(); ) {
|
||||
for (final Iterator<LexicalContextNode> contextNodeIter = lc.getAllNodes(); contextNodeIter.hasNext(); ) {
|
||||
final LexicalContextNode node = contextNodeIter.next();
|
||||
if (node instanceof Block) {
|
||||
if (((Block)node).getExistingSymbol(name) != null) {
|
||||
@ -610,7 +605,7 @@ final class Attr extends NodeOperatorVisitor {
|
||||
}
|
||||
|
||||
if (scopeBlock != null) {
|
||||
assert getLexicalContext().contains(scopeBlock);
|
||||
assert lc.contains(scopeBlock);
|
||||
lc.setFlag(scopeBlock, Block.NEEDS_SCOPE);
|
||||
}
|
||||
}
|
||||
@ -622,8 +617,8 @@ final class Attr extends NodeOperatorVisitor {
|
||||
* @see #needsParentScope()
|
||||
*/
|
||||
private void setUsesGlobalSymbol() {
|
||||
for (final Iterator<FunctionNode> fns = getLexicalContext().getFunctions(); fns.hasNext();) {
|
||||
getLexicalContext().setFlag(fns.next(), FunctionNode.USES_ANCESTOR_SCOPE);
|
||||
for (final Iterator<FunctionNode> fns = lc.getFunctions(); fns.hasNext();) {
|
||||
lc.setFlag(fns.next(), FunctionNode.USES_ANCESTOR_SCOPE);
|
||||
}
|
||||
}
|
||||
|
||||
@ -635,7 +630,7 @@ final class Attr extends NodeOperatorVisitor {
|
||||
private Symbol findSymbol(final Block block, final String name) {
|
||||
// Search up block chain to locate symbol.
|
||||
|
||||
for (final Iterator<Block> blocks = getLexicalContext().getBlocks(block); blocks.hasNext();) {
|
||||
for (final Iterator<Block> blocks = lc.getBlocks(block); blocks.hasNext();) {
|
||||
// Find name.
|
||||
final Symbol symbol = blocks.next().getExistingSymbol(name);
|
||||
// If found then we are good.
|
||||
@ -656,11 +651,11 @@ final class Attr extends NodeOperatorVisitor {
|
||||
public Node leaveLiteralNode(final LiteralNode literalNode) {
|
||||
assert !literalNode.isTokenType(TokenType.THIS) : "tokentype for " + literalNode + " is this"; //guard against old dead code case. literal nodes should never inherit tokens
|
||||
assert literalNode instanceof ArrayLiteralNode || !(literalNode.getValue() instanceof Node) : "literals with Node values not supported";
|
||||
final Symbol symbol = new Symbol(getLexicalContext().getCurrentFunction().uniqueName(LITERAL_PREFIX.symbolName()), IS_CONSTANT, literalNode.getType());
|
||||
final Symbol symbol = new Symbol(lc.getCurrentFunction().uniqueName(LITERAL_PREFIX.symbolName()), IS_CONSTANT, literalNode.getType());
|
||||
if (literalNode instanceof ArrayLiteralNode) {
|
||||
((ArrayLiteralNode)literalNode).analyze();
|
||||
}
|
||||
return end(literalNode.setSymbol(getLexicalContext(), symbol));
|
||||
return end(literalNode.setSymbol(lc, symbol));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -676,7 +671,7 @@ final class Attr extends NodeOperatorVisitor {
|
||||
@Override
|
||||
public Node leavePropertyNode(final PropertyNode propertyNode) {
|
||||
// assign a pseudo symbol to property name, see NASHORN-710
|
||||
return propertyNode.setSymbol(getLexicalContext(), new Symbol(propertyNode.getKeyName(), 0, Type.OBJECT));
|
||||
return propertyNode.setSymbol(lc, new Symbol(propertyNode.getKeyName(), 0, Type.OBJECT));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -734,11 +729,11 @@ final class Attr extends NodeOperatorVisitor {
|
||||
type = Type.OBJECT;
|
||||
}
|
||||
|
||||
switchNode.setTag(newInternal(getLexicalContext().getCurrentFunction().uniqueName(SWITCH_TAG_PREFIX.symbolName()), type));
|
||||
switchNode.setTag(newInternal(lc.getCurrentFunction().uniqueName(SWITCH_TAG_PREFIX.symbolName()), type));
|
||||
|
||||
end(switchNode);
|
||||
|
||||
return switchNode.setCases(getLexicalContext(), newCases);
|
||||
return switchNode.setCases(lc, newCases);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -761,7 +756,7 @@ final class Attr extends NodeOperatorVisitor {
|
||||
final IdentNode ident = varNode.getName();
|
||||
final String name = ident.getName();
|
||||
|
||||
final Symbol symbol = defineSymbol(getLexicalContext().getCurrentBlock(), name, IS_VAR);
|
||||
final Symbol symbol = defineSymbol(lc.getCurrentBlock(), name, IS_VAR);
|
||||
assert symbol != null;
|
||||
|
||||
// NASHORN-467 - use before definition of vars - conservative
|
||||
@ -781,7 +776,6 @@ final class Attr extends NodeOperatorVisitor {
|
||||
final IdentNode ident = newVarNode.getName();
|
||||
final String name = ident.getName();
|
||||
|
||||
final LexicalContext lc = getLexicalContext();
|
||||
final Symbol symbol = findSymbol(lc.getCurrentBlock(), ident.getName());
|
||||
|
||||
if (init == null) {
|
||||
@ -834,7 +828,7 @@ final class Attr extends NodeOperatorVisitor {
|
||||
|
||||
@Override
|
||||
public Node leaveDELETE(final UnaryNode unaryNode) {
|
||||
final FunctionNode currentFunctionNode = getLexicalContext().getCurrentFunction();
|
||||
final FunctionNode currentFunctionNode = lc.getCurrentFunction();
|
||||
final boolean strictMode = currentFunctionNode.isStrict();
|
||||
final Node rhs = unaryNode.rhs();
|
||||
final Node strictFlagNode = LiteralNode.newInstance(unaryNode, strictMode).accept(this);
|
||||
@ -894,10 +888,10 @@ final class Attr extends NodeOperatorVisitor {
|
||||
* @return true if the symbol denoted by the specified name in the current lexical context defined in the program level.
|
||||
*/
|
||||
private boolean isProgramLevelSymbol(final String name) {
|
||||
for(final Iterator<Block> it = getLexicalContext().getBlocks(); it.hasNext();) {
|
||||
for(final Iterator<Block> it = lc.getBlocks(); it.hasNext();) {
|
||||
final Block next = it.next();
|
||||
if(next.getExistingSymbol(name) != null) {
|
||||
return next == getLexicalContext().getFunctionBody(getLexicalContext().getOutermostFunction());
|
||||
return next == lc.getFunctionBody(lc.getOutermostFunction());
|
||||
}
|
||||
}
|
||||
throw new AssertionError("Couldn't find symbol " + name + " in the context");
|
||||
@ -914,14 +908,14 @@ final class Attr extends NodeOperatorVisitor {
|
||||
}
|
||||
|
||||
private IdentNode compilerConstant(CompilerConstants cc) {
|
||||
final FunctionNode functionNode = getLexicalContext().getCurrentFunction();
|
||||
final FunctionNode functionNode = lc.getCurrentFunction();
|
||||
return (IdentNode)
|
||||
new IdentNode(
|
||||
functionNode.getToken(),
|
||||
functionNode.getFinish(),
|
||||
cc.symbolName()).
|
||||
setSymbol(
|
||||
getLexicalContext(),
|
||||
lc,
|
||||
functionNode.compilerConstant(cc));
|
||||
}
|
||||
|
||||
@ -999,7 +993,7 @@ final class Attr extends NodeOperatorVisitor {
|
||||
final Node lhs = binaryNode.lhs();
|
||||
|
||||
if (lhs instanceof IdentNode) {
|
||||
final Block block = getLexicalContext().getCurrentBlock();
|
||||
final Block block = lc.getCurrentBlock();
|
||||
final IdentNode ident = (IdentNode)lhs;
|
||||
final String name = ident.getName();
|
||||
|
||||
@ -1043,7 +1037,7 @@ final class Attr extends NodeOperatorVisitor {
|
||||
}
|
||||
|
||||
private boolean isLocal(FunctionNode function, Symbol symbol) {
|
||||
final FunctionNode definingFn = getLexicalContext().getDefiningFunction(symbol);
|
||||
final FunctionNode definingFn = lc.getDefiningFunction(symbol);
|
||||
// Temp symbols are not assigned to a block, so their defining fn is null; those can be assumed local
|
||||
return definingFn == null || definingFn == function;
|
||||
}
|
||||
@ -1329,7 +1323,7 @@ final class Attr extends NodeOperatorVisitor {
|
||||
@Override
|
||||
public Node leaveForNode(final ForNode forNode) {
|
||||
if (forNode.isForIn()) {
|
||||
forNode.setIterator(newInternal(getLexicalContext().getCurrentFunction().uniqueName(ITERATOR_PREFIX.symbolName()), Type.OBJECT)); //NASHORN-73
|
||||
forNode.setIterator(newInternal(lc.getCurrentFunction().uniqueName(ITERATOR_PREFIX.symbolName()), Type.OBJECT)); //NASHORN-73
|
||||
/*
|
||||
* Iterators return objects, so we need to widen the scope of the
|
||||
* init variable if it, for example, has been assigned double type
|
||||
@ -1407,7 +1401,7 @@ final class Attr extends NodeOperatorVisitor {
|
||||
final Symbol paramSymbol = functionNode.getBody().getExistingSymbol(param.getName());
|
||||
assert paramSymbol != null;
|
||||
assert paramSymbol.isParam();
|
||||
newParams.add((IdentNode)param.setSymbol(getLexicalContext(), paramSymbol));
|
||||
newParams.add((IdentNode)param.setSymbol(lc, paramSymbol));
|
||||
|
||||
assert paramSymbol != null;
|
||||
Type type = functionNode.getHints().getParameterType(pos);
|
||||
@ -1439,10 +1433,10 @@ final class Attr extends NodeOperatorVisitor {
|
||||
FunctionNode newFunctionNode = functionNode;
|
||||
|
||||
if (nparams == 0 || (specialize * 2) < nparams) {
|
||||
newFunctionNode = newFunctionNode.clearSnapshot(getLexicalContext());
|
||||
newFunctionNode = newFunctionNode.clearSnapshot(lc);
|
||||
}
|
||||
|
||||
return newFunctionNode.setParameters(getLexicalContext(), newParams);
|
||||
return newFunctionNode.setParameters(lc, newParams);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1506,7 +1500,7 @@ final class Attr extends NodeOperatorVisitor {
|
||||
}
|
||||
|
||||
private Symbol exceptionSymbol() {
|
||||
return newInternal(getLexicalContext().getCurrentFunction().uniqueName(EXCEPTION_PREFIX.symbolName()), Type.typeFor(ECMAException.class));
|
||||
return newInternal(lc.getCurrentFunction().uniqueName(EXCEPTION_PREFIX.symbolName()), Type.typeFor(ECMAException.class));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1520,8 +1514,8 @@ final class Attr extends NodeOperatorVisitor {
|
||||
* @param assignmentDest the destination node of the assignment, e.g. lhs for binary nodes
|
||||
*/
|
||||
private Node ensureAssignmentSlots(final Node assignmentDest) {
|
||||
final LexicalContext attrLexicalContext = getLexicalContext();
|
||||
return assignmentDest.accept(new NodeVisitor() {
|
||||
final LexicalContext attrLexicalContext = lc;
|
||||
return assignmentDest.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
|
||||
@Override
|
||||
public Node leaveIndexNode(final IndexNode indexNode) {
|
||||
assert indexNode.getSymbol().isTemp();
|
||||
@ -1565,7 +1559,7 @@ final class Attr extends NodeOperatorVisitor {
|
||||
FunctionNode currentFunctionNode = functionNode;
|
||||
do {
|
||||
changed.clear();
|
||||
final FunctionNode newFunctionNode = (FunctionNode)currentFunctionNode.accept(new NodeVisitor() {
|
||||
final FunctionNode newFunctionNode = (FunctionNode)currentFunctionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
|
||||
|
||||
private Node widen(final Node node, final Type to) {
|
||||
if (node instanceof LiteralNode) {
|
||||
@ -1579,7 +1573,7 @@ final class Attr extends NodeOperatorVisitor {
|
||||
symbol = temporarySymbols.getTypedTemporarySymbol(to);
|
||||
}
|
||||
newType(symbol, to);
|
||||
final Node newNode = node.setSymbol(getLexicalContext(), symbol);
|
||||
final Node newNode = node.setSymbol(lc, symbol);
|
||||
changed.add(newNode);
|
||||
return newNode;
|
||||
}
|
||||
@ -1622,7 +1616,7 @@ final class Attr extends NodeOperatorVisitor {
|
||||
return newBinaryNode;
|
||||
}
|
||||
});
|
||||
getLexicalContext().replace(currentFunctionNode, newFunctionNode);
|
||||
lc.replace(currentFunctionNode, newFunctionNode);
|
||||
currentFunctionNode = newFunctionNode;
|
||||
} while (!changed.isEmpty());
|
||||
return currentFunctionNode;
|
||||
@ -1643,12 +1637,12 @@ final class Attr extends NodeOperatorVisitor {
|
||||
}
|
||||
|
||||
private Node ensureSymbol(final Type type, final Node node) {
|
||||
LOG.info("New TEMPORARY added to ", getLexicalContext().getCurrentFunction().getName(), " type=", type);
|
||||
return ensureSymbol(getLexicalContext(), type, node);
|
||||
LOG.info("New TEMPORARY added to ", lc.getCurrentFunction().getName(), " type=", type);
|
||||
return temporarySymbols.ensureSymbol(lc, type, node);
|
||||
}
|
||||
|
||||
private Symbol newInternal(final String name, final Type type) {
|
||||
final Symbol iter = defineSymbol(getLexicalContext().getCurrentBlock(), name, IS_VAR | IS_INTERNAL);
|
||||
final Symbol iter = defineSymbol(lc.getCurrentBlock(), name, IS_VAR | IS_INTERNAL);
|
||||
iter.setType(type); // NASHORN-73
|
||||
return iter;
|
||||
}
|
||||
@ -1705,10 +1699,6 @@ final class Attr extends NodeOperatorVisitor {
|
||||
localUses.peek().add(name);
|
||||
}
|
||||
|
||||
private Node ensureSymbol(final LexicalContext lc, final Type type, final Node node) {
|
||||
return temporarySymbols.ensureSymbol(lc, type, node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pessimistically promote all symbols in current function node to Object types
|
||||
* This is done when the function contains unevaluated black boxes such as
|
||||
@ -1717,7 +1707,7 @@ final class Attr extends NodeOperatorVisitor {
|
||||
* @param body body for the function node we are leaving
|
||||
*/
|
||||
private static void objectifySymbols(final Block body) {
|
||||
body.accept(new NodeVisitor() {
|
||||
body.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
|
||||
private void toObject(final Block block) {
|
||||
for (final Symbol symbol : block.getSymbols()) {
|
||||
if (!symbol.isTemp()) {
|
||||
@ -1761,7 +1751,7 @@ final class Attr extends NodeOperatorVisitor {
|
||||
append("] ").
|
||||
append(printNode ? node.toString() : "").
|
||||
append(" in '").
|
||||
append(getLexicalContext().getCurrentFunction().getName()).
|
||||
append(lc.getCurrentFunction().getName()).
|
||||
append("'");
|
||||
LOG.info(sb);
|
||||
LOG.indent();
|
||||
@ -1787,7 +1777,7 @@ final class Attr extends NodeOperatorVisitor {
|
||||
append("] ").
|
||||
append(printNode ? node.toString() : "").
|
||||
append(" in '").
|
||||
append(getLexicalContext().getCurrentFunction().getName());
|
||||
append(lc.getCurrentFunction().getName());
|
||||
|
||||
if (node.getSymbol() == null) {
|
||||
sb.append(" <NO SYMBOL>");
|
||||
|
@ -52,16 +52,13 @@ import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALL
|
||||
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_STRICT;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Deque;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Locale;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
|
||||
@ -83,11 +80,11 @@ import jdk.nashorn.internal.ir.EmptyNode;
|
||||
import jdk.nashorn.internal.ir.ExecuteNode;
|
||||
import jdk.nashorn.internal.ir.ForNode;
|
||||
import jdk.nashorn.internal.ir.FunctionNode;
|
||||
import jdk.nashorn.internal.ir.LexicalContext;
|
||||
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
|
||||
import jdk.nashorn.internal.ir.IdentNode;
|
||||
import jdk.nashorn.internal.ir.IfNode;
|
||||
import jdk.nashorn.internal.ir.IndexNode;
|
||||
import jdk.nashorn.internal.ir.LexicalContext;
|
||||
import jdk.nashorn.internal.ir.LexicalContextNode;
|
||||
import jdk.nashorn.internal.ir.LiteralNode;
|
||||
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
|
||||
@ -150,7 +147,7 @@ import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
|
||||
* The CodeGenerator visits nodes only once, tags them as resolved and emits
|
||||
* bytecode for them.
|
||||
*/
|
||||
final class CodeGenerator extends NodeOperatorVisitor {
|
||||
final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContext> {
|
||||
|
||||
/** Name of the Global object, cannot be referred to as .class, @see CodeGenerator */
|
||||
private static final String GLOBAL_OBJECT = Compiler.OBJECTS_PACKAGE + '/' + "Global";
|
||||
@ -168,23 +165,12 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
/** How many regexp fields have been emitted */
|
||||
private int regexFieldCount;
|
||||
|
||||
/** Map of shared scope call sites */
|
||||
private final Map<SharedScopeCall, SharedScopeCall> scopeCalls = new HashMap<>();
|
||||
/** Line number for last statement. If we encounter a new line number, line number bytecode information
|
||||
* needs to be generated */
|
||||
private int lastLineNumber = -1;
|
||||
|
||||
/** Compile unit stack - every time we start a sub method (e.g. a split) we push one */
|
||||
private final Deque<CompileUnit> compileUnits = new ArrayDeque<>();
|
||||
|
||||
/** Method emitter stack - every time we start a sub method (e.g. a split) we push one */
|
||||
private final Deque<MethodEmitter> methodEmitters = new ArrayDeque<>();
|
||||
|
||||
/** The discard stack - whenever we enter a discard node we keep track of its return value status -
|
||||
* i.e. should we keep it or throw it away */
|
||||
private final Deque<Node> discard = new ArrayDeque<>();
|
||||
|
||||
// A stack tracking the next free local variable slot in the blocks. There's one entry for every block
|
||||
// currently on the lexical context stack.
|
||||
private int[] nextFreeSlots = new int[16];
|
||||
private int nextFreeSlotsSize = 0;
|
||||
/** When should we stop caching regexp expressions in fields to limit bytecode size? */
|
||||
private static final int MAX_REGEX_FIELDS = 2 * 1024;
|
||||
|
||||
/** Current method emitter */
|
||||
private MethodEmitter method;
|
||||
@ -192,20 +178,16 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
/** Current compile unit */
|
||||
private CompileUnit unit;
|
||||
|
||||
private int lastLineNumber = -1;
|
||||
|
||||
/** When should we stop caching regexp expressions in fields to limit bytecode size? */
|
||||
private static final int MAX_REGEX_FIELDS = 2 * 1024;
|
||||
|
||||
private static final DebugLogger LOG = new DebugLogger("codegen", "nashorn.codegen.debug");
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param compiler
|
||||
*/
|
||||
CodeGenerator(final Compiler compiler) {
|
||||
super(new DynamicScopeTrackingLexicalContext());
|
||||
super(new CodeGeneratorLexicalContext());
|
||||
this.compiler = compiler;
|
||||
this.callSiteFlags = compiler.getEnv()._callsite_flags;
|
||||
}
|
||||
@ -217,37 +199,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
* @return the correct flags for a call site in the current function
|
||||
*/
|
||||
int getCallSiteFlags() {
|
||||
return getLexicalContext().getCurrentFunction().isStrict() ? callSiteFlags | CALLSITE_STRICT : callSiteFlags;
|
||||
}
|
||||
|
||||
private void pushMethodEmitter(final MethodEmitter newMethod) {
|
||||
methodEmitters.push(newMethod);
|
||||
this.method = newMethod;
|
||||
}
|
||||
|
||||
private void popMethodEmitter(final MethodEmitter oldMethod) {
|
||||
assert methodEmitters.peek() == oldMethod;
|
||||
methodEmitters.pop();
|
||||
if (!methodEmitters.isEmpty()) {
|
||||
this.method = methodEmitters.peek();
|
||||
} else {
|
||||
this.method = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void push(final CompileUnit newUnit) {
|
||||
compileUnits.push(newUnit);
|
||||
this.unit = newUnit;
|
||||
}
|
||||
|
||||
private void pop(final CompileUnit oldUnit) {
|
||||
assert compileUnits.peek() == oldUnit;
|
||||
compileUnits.pop();
|
||||
if (!compileUnits.isEmpty()) {
|
||||
this.unit = compileUnits.peek();
|
||||
} else {
|
||||
this.unit = null;
|
||||
}
|
||||
return lc.getCurrentFunction().isStrict() ? callSiteFlags | CALLSITE_STRICT : callSiteFlags;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -265,7 +217,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
}
|
||||
|
||||
final String name = symbol.getName();
|
||||
final Source source = getLexicalContext().getCurrentFunction().getSource();
|
||||
final Source source = lc.getCurrentFunction().getSource();
|
||||
|
||||
if (CompilerConstants.__FILE__.name().equals(name)) {
|
||||
return method.load(source.getName());
|
||||
@ -290,53 +242,6 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A lexical context that also tracks if we have any dynamic scopes in the context. Such scopes can have new
|
||||
* variables introduced into them at run time - a with block or a function directly containing an eval call.
|
||||
*/
|
||||
private static class DynamicScopeTrackingLexicalContext extends LexicalContext {
|
||||
int dynamicScopeCount = 0;
|
||||
|
||||
@Override
|
||||
public <T extends LexicalContextNode> T push(T node) {
|
||||
if(isDynamicScopeBoundary(node)) {
|
||||
++dynamicScopeCount;
|
||||
}
|
||||
return super.push(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends LexicalContextNode> T pop(T node) {
|
||||
final T popped = super.pop(node);
|
||||
if(isDynamicScopeBoundary(popped)) {
|
||||
--dynamicScopeCount;
|
||||
}
|
||||
return popped;
|
||||
}
|
||||
|
||||
private boolean isDynamicScopeBoundary(LexicalContextNode node) {
|
||||
if(node instanceof Block) {
|
||||
// Block's immediate parent is a with node. Note we aren't testing for a WithNode, as that'd capture
|
||||
// processing of WithNode.expression too, but it should be unaffected.
|
||||
return !isEmpty() && peek() instanceof WithNode;
|
||||
} else if(node instanceof FunctionNode) {
|
||||
// Function has a direct eval in it (so a top-level "var ..." in the eval code can introduce a new
|
||||
// variable into the function's scope), and it isn't strict (as evals in strict functions get an
|
||||
// isolated scope).
|
||||
return isFunctionDynamicScope((FunctionNode)node);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
boolean inDynamicScope() {
|
||||
return ((DynamicScopeTrackingLexicalContext)getLexicalContext()).dynamicScopeCount > 0;
|
||||
}
|
||||
|
||||
static boolean isFunctionDynamicScope(FunctionNode fn) {
|
||||
return fn.hasEval() && !fn.isStrict();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this symbol can be accessed directly with a putfield or getfield or dynamic load
|
||||
*
|
||||
@ -344,35 +249,37 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
* @return true if fast scope
|
||||
*/
|
||||
private boolean isFastScope(final Symbol symbol) {
|
||||
if(!symbol.isScope()) {
|
||||
if (!symbol.isScope()) {
|
||||
return false;
|
||||
}
|
||||
final LexicalContext lc = getLexicalContext();
|
||||
if(!inDynamicScope()) {
|
||||
|
||||
if (!lc.inDynamicScope()) {
|
||||
// If there's no with or eval in context, and the symbol is marked as scoped, it is fast scoped. Such a
|
||||
// symbol must either be global, or its defining block must need scope.
|
||||
assert symbol.isGlobal() || lc.getDefiningBlock(symbol).needsScope() : symbol.getName();
|
||||
return true;
|
||||
}
|
||||
if(symbol.isGlobal()) {
|
||||
|
||||
if (symbol.isGlobal()) {
|
||||
// Shortcut: if there's a with or eval in context, globals can't be fast scoped
|
||||
return false;
|
||||
}
|
||||
|
||||
// Otherwise, check if there's a dynamic scope between use of the symbol and its definition
|
||||
final String name = symbol.getName();
|
||||
boolean previousWasBlock = false;
|
||||
for (final Iterator<LexicalContextNode> it = lc.getAllNodes(); it.hasNext();) {
|
||||
final LexicalContextNode node = it.next();
|
||||
if(node instanceof Block) {
|
||||
if (node instanceof Block) {
|
||||
// If this block defines the symbol, then we can fast scope the symbol.
|
||||
final Block block = (Block)node;
|
||||
if(block.getExistingSymbol(name) == symbol) {
|
||||
if (block.getExistingSymbol(name) == symbol) {
|
||||
assert block.needsScope();
|
||||
return true;
|
||||
}
|
||||
previousWasBlock = true;
|
||||
} else {
|
||||
if((node instanceof WithNode && previousWasBlock) || (node instanceof FunctionNode && isFunctionDynamicScope((FunctionNode)node))) {
|
||||
if ((node instanceof WithNode && previousWasBlock) || (node instanceof FunctionNode && CodeGeneratorLexicalContext.isFunctionDynamicScope((FunctionNode)node))) {
|
||||
// If we hit a scope that can have symbols introduced into it at run time before finding the defining
|
||||
// block, the symbol can't be fast scoped. A WithNode only counts if we've immediately seen a block
|
||||
// before - its block. Otherwise, we are currently processing the WithNode's expression, and that's
|
||||
@ -387,16 +294,14 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
}
|
||||
|
||||
private MethodEmitter loadSharedScopeVar(final Type valueType, final Symbol symbol, final int flags) {
|
||||
method.load(isFastScope(symbol) ? getScopeProtoDepth(getLexicalContext().getCurrentBlock(), symbol) : -1);
|
||||
final SharedScopeCall scopeCall = getScopeGet(valueType, symbol, flags | CALLSITE_FAST_SCOPE);
|
||||
scopeCall.generateInvoke(method);
|
||||
return method;
|
||||
method.load(isFastScope(symbol) ? getScopeProtoDepth(lc.getCurrentBlock(), symbol) : -1);
|
||||
final SharedScopeCall scopeCall = lc.getScopeGet(unit, valueType, symbol, flags | CALLSITE_FAST_SCOPE);
|
||||
return scopeCall.generateInvoke(method);
|
||||
}
|
||||
|
||||
private MethodEmitter loadFastScopeVar(final Type valueType, final Symbol symbol, final int flags, final boolean isMethod) {
|
||||
loadFastScopeProto(symbol, false);
|
||||
method.dynamicGet(valueType, symbol.getName(), flags | CALLSITE_FAST_SCOPE, isMethod);
|
||||
return method;
|
||||
return method.dynamicGet(valueType, symbol.getName(), flags | CALLSITE_FAST_SCOPE, isMethod);
|
||||
}
|
||||
|
||||
private MethodEmitter storeFastScopeVar(final Type valueType, final Symbol symbol, final int flags) {
|
||||
@ -408,7 +313,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
private int getScopeProtoDepth(final Block startingBlock, final Symbol symbol) {
|
||||
int depth = 0;
|
||||
final String name = symbol.getName();
|
||||
for(final Iterator<Block> blocks = getLexicalContext().getBlocks(startingBlock); blocks.hasNext();) {
|
||||
for(final Iterator<Block> blocks = lc.getBlocks(startingBlock); blocks.hasNext();) {
|
||||
final Block currentBlock = blocks.next();
|
||||
if (currentBlock.getExistingSymbol(name) == symbol) {
|
||||
return depth;
|
||||
@ -421,7 +326,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
}
|
||||
|
||||
private void loadFastScopeProto(final Symbol symbol, final boolean swap) {
|
||||
final int depth = getScopeProtoDepth(getLexicalContext().getCurrentBlock(), symbol);
|
||||
final int depth = getScopeProtoDepth(lc.getCurrentBlock(), symbol);
|
||||
assert depth != -1;
|
||||
if (depth > 0) {
|
||||
if (swap) {
|
||||
@ -464,7 +369,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
*/
|
||||
final CodeGenerator codegen = this;
|
||||
|
||||
node.accept(new NodeVisitor() {
|
||||
node.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
|
||||
@Override
|
||||
public boolean enterIdentNode(final IdentNode identNode) {
|
||||
loadIdent(identNode);
|
||||
@ -538,7 +443,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
final boolean isInternal = symbol.isParam() || symbol.isInternal() || symbol.isThis() || !symbol.canBeUndefined();
|
||||
|
||||
if (symbol.hasSlot() && !isInternal) {
|
||||
assert symbol.getSymbolType().isNumber() || symbol.getSymbolType().isObject() : "no potentially undefined narrower local vars than doubles are allowed: " + symbol + " in " + getLexicalContext().getCurrentFunction();
|
||||
assert symbol.getSymbolType().isNumber() || symbol.getSymbolType().isObject() : "no potentially undefined narrower local vars than doubles are allowed: " + symbol + " in " + lc.getCurrentFunction();
|
||||
if (symbol.getSymbolType().isNumber()) {
|
||||
numbers.add(symbol);
|
||||
} else if (symbol.getSymbolType().isObject()) {
|
||||
@ -595,7 +500,6 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
if (block.needsScope() && !block.isTerminal()) {
|
||||
popBlockScope(block);
|
||||
}
|
||||
--nextFreeSlotsSize;
|
||||
return block;
|
||||
}
|
||||
|
||||
@ -624,11 +528,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
public boolean enterBreakNode(final BreakNode breakNode) {
|
||||
lineNumber(breakNode);
|
||||
|
||||
final BreakableNode breakFrom = getLexicalContext().getBreakable(breakNode.getLabel());
|
||||
for (int i = 0; i < getLexicalContext().getScopeNestingLevelTo(breakFrom); i++) {
|
||||
final BreakableNode breakFrom = lc.getBreakable(breakNode.getLabel());
|
||||
for (int i = 0; i < lc.getScopeNestingLevelTo(breakFrom); i++) {
|
||||
closeWith();
|
||||
}
|
||||
method.splitAwareGoto(getLexicalContext(), breakFrom.getBreakLabel());
|
||||
method.splitAwareGoto(lc, breakFrom.getBreakLabel());
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -672,11 +576,12 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
|
||||
final List<Node> args = callNode.getArgs();
|
||||
final Node function = callNode.getFunction();
|
||||
final Block currentBlock = getLexicalContext().getCurrentBlock();
|
||||
final Block currentBlock = lc.getCurrentBlock();
|
||||
final CodeGeneratorLexicalContext codegenLexicalContext = lc;
|
||||
|
||||
function.accept(new NodeVisitor() {
|
||||
function.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
|
||||
|
||||
private void sharedScopeCall(final IdentNode identNode, final int flags) {
|
||||
private MethodEmitter sharedScopeCall(final IdentNode identNode, final int flags) {
|
||||
final Symbol symbol = identNode.getSymbol();
|
||||
int scopeCallFlags = flags;
|
||||
method.loadCompilerConstant(SCOPE);
|
||||
@ -688,8 +593,8 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
}
|
||||
loadArgs(args);
|
||||
final Type[] paramTypes = method.getTypesFromStack(args.size());
|
||||
final SharedScopeCall scopeCall = getScopeCall(symbol, identNode.getType(), callNode.getType(), paramTypes, scopeCallFlags);
|
||||
scopeCall.generateInvoke(method);
|
||||
final SharedScopeCall scopeCall = codegenLexicalContext.getScopeCall(unit, symbol, identNode.getType(), callNode.getType(), paramTypes, scopeCallFlags);
|
||||
return scopeCall.generateInvoke(method);
|
||||
}
|
||||
|
||||
private void scopeCall(final IdentNode node, final int flags) {
|
||||
@ -756,7 +661,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
evalCall(node, flags);
|
||||
} else if (useCount <= SharedScopeCall.FAST_SCOPE_CALL_THRESHOLD
|
||||
|| (!isFastScope(symbol) && useCount <= SharedScopeCall.SLOW_SCOPE_CALL_THRESHOLD)
|
||||
|| CodeGenerator.this.inDynamicScope()) {
|
||||
|| CodeGenerator.this.lc.inDynamicScope()) {
|
||||
scopeCall(node, flags);
|
||||
} else {
|
||||
sharedScopeCall(node, flags);
|
||||
@ -845,11 +750,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
public boolean enterContinueNode(final ContinueNode continueNode) {
|
||||
lineNumber(continueNode);
|
||||
|
||||
final LoopNode continueTo = getLexicalContext().getContinueTo(continueNode.getLabel());
|
||||
for (int i = 0; i < getLexicalContext().getScopeNestingLevelTo(continueTo); i++) {
|
||||
final LoopNode continueTo = lc.getContinueTo(continueNode.getLabel());
|
||||
for (int i = 0; i < lc.getScopeNestingLevelTo(continueTo); i++) {
|
||||
closeWith();
|
||||
}
|
||||
method.splitAwareGoto(getLexicalContext(), continueTo.getContinueLabel());
|
||||
method.splitAwareGoto(lc, continueTo.getContinueLabel());
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -875,90 +780,89 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
public boolean enterForNode(final ForNode forNode) {
|
||||
lineNumber(forNode);
|
||||
|
||||
final Node test = forNode.getTest();
|
||||
final Block body = forNode.getBody();
|
||||
final Node modify = forNode.getModify();
|
||||
|
||||
final Label breakLabel = forNode.getBreakLabel();
|
||||
final Label continueLabel = forNode.getContinueLabel();
|
||||
final Label loopLabel = new Label("loop");
|
||||
|
||||
Node init = forNode.getInit();
|
||||
|
||||
if (forNode.isForIn()) {
|
||||
final Symbol iter = forNode.getIterator();
|
||||
|
||||
// We have to evaluate the optional initializer expression
|
||||
// of the iterator variable of the for-in statement.
|
||||
if (init instanceof VarNode) {
|
||||
init.accept(this);
|
||||
init = ((VarNode)init).getName();
|
||||
}
|
||||
|
||||
load(modify);
|
||||
assert modify.getType().isObject();
|
||||
method.invoke(forNode.isForEach() ? ScriptRuntime.TO_VALUE_ITERATOR : ScriptRuntime.TO_PROPERTY_ITERATOR);
|
||||
method.store(iter);
|
||||
method._goto(continueLabel);
|
||||
method.label(loopLabel);
|
||||
|
||||
new Store<Node>(init) {
|
||||
@Override
|
||||
protected void storeNonDiscard() {
|
||||
return;
|
||||
}
|
||||
@Override
|
||||
protected void evaluate() {
|
||||
method.load(iter);
|
||||
method.invoke(interfaceCallNoLookup(Iterator.class, "next", Object.class));
|
||||
}
|
||||
}.store();
|
||||
|
||||
body.accept(this);
|
||||
|
||||
method.label(continueLabel);
|
||||
method.load(iter);
|
||||
method.invoke(interfaceCallNoLookup(Iterator.class, "hasNext", boolean.class));
|
||||
method.ifne(loopLabel);
|
||||
method.label(breakLabel);
|
||||
enterForIn(forNode);
|
||||
} else {
|
||||
if (init != null) {
|
||||
init.accept(this);
|
||||
}
|
||||
|
||||
final Label testLabel = new Label("test");
|
||||
|
||||
method._goto(testLabel);
|
||||
method.label(loopLabel);
|
||||
body.accept(this);
|
||||
method.label(continueLabel);
|
||||
|
||||
if (!body.isTerminal() && modify != null) {
|
||||
load(modify);
|
||||
}
|
||||
|
||||
method.label(testLabel);
|
||||
if (test != null) {
|
||||
new BranchOptimizer(this, method).execute(test, loopLabel, true);
|
||||
} else {
|
||||
method._goto(loopLabel);
|
||||
}
|
||||
|
||||
method.label(breakLabel);
|
||||
enterFor(forNode);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static int assignSlots(final Block block, final int firstSlot) {
|
||||
int nextSlot = firstSlot;
|
||||
for (final Symbol symbol : block.getSymbols()) {
|
||||
if (symbol.hasSlot()) {
|
||||
symbol.setSlot(nextSlot);
|
||||
nextSlot += symbol.slotCount();
|
||||
}
|
||||
private void enterFor(final ForNode forNode) {
|
||||
final Node init = forNode.getInit();
|
||||
final Node test = forNode.getTest();
|
||||
final Block body = forNode.getBody();
|
||||
final Node modify = forNode.getModify();
|
||||
|
||||
if (init != null) {
|
||||
init.accept(this);
|
||||
}
|
||||
return nextSlot;
|
||||
|
||||
final Label loopLabel = new Label("loop");
|
||||
final Label testLabel = new Label("test");
|
||||
|
||||
method._goto(testLabel);
|
||||
method.label(loopLabel);
|
||||
body.accept(this);
|
||||
method.label(forNode.getContinueLabel());
|
||||
|
||||
if (!body.isTerminal() && modify != null) {
|
||||
load(modify);
|
||||
}
|
||||
|
||||
method.label(testLabel);
|
||||
if (test != null) {
|
||||
new BranchOptimizer(this, method).execute(test, loopLabel, true);
|
||||
} else {
|
||||
method._goto(loopLabel);
|
||||
}
|
||||
|
||||
method.label(forNode.getBreakLabel());
|
||||
}
|
||||
|
||||
private void enterForIn(final ForNode forNode) {
|
||||
final Block body = forNode.getBody();
|
||||
final Node modify = forNode.getModify();
|
||||
|
||||
final Symbol iter = forNode.getIterator();
|
||||
final Label loopLabel = new Label("loop");
|
||||
|
||||
Node init = forNode.getInit();
|
||||
|
||||
// We have to evaluate the optional initializer expression
|
||||
// of the iterator variable of the for-in statement.
|
||||
if (init instanceof VarNode) {
|
||||
init.accept(this);
|
||||
init = ((VarNode)init).getName();
|
||||
}
|
||||
|
||||
load(modify);
|
||||
assert modify.getType().isObject();
|
||||
method.invoke(forNode.isForEach() ? ScriptRuntime.TO_VALUE_ITERATOR : ScriptRuntime.TO_PROPERTY_ITERATOR);
|
||||
method.store(iter);
|
||||
method._goto(forNode.getContinueLabel());
|
||||
method.label(loopLabel);
|
||||
|
||||
new Store<Node>(init) {
|
||||
@Override
|
||||
protected void storeNonDiscard() {
|
||||
return;
|
||||
}
|
||||
@Override
|
||||
protected void evaluate() {
|
||||
method.load(iter);
|
||||
method.invoke(interfaceCallNoLookup(Iterator.class, "next", Object.class));
|
||||
}
|
||||
}.store();
|
||||
|
||||
body.accept(this);
|
||||
|
||||
method.label(forNode.getContinueLabel());
|
||||
method.load(iter);
|
||||
method.invoke(interfaceCallNoLookup(Iterator.class, "hasNext", boolean.class));
|
||||
method.ifne(loopLabel);
|
||||
method.label(forNode.getBreakLabel());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -967,24 +871,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
* @param block block with local vars.
|
||||
*/
|
||||
private void initLocals(final Block block) {
|
||||
final boolean isFunctionBody = getLexicalContext().isFunctionBody();
|
||||
lc.nextFreeSlot(block);
|
||||
|
||||
final int nextFreeSlot;
|
||||
if (isFunctionBody) {
|
||||
// On entry to function, start with slot 0
|
||||
nextFreeSlot = 0;
|
||||
} else {
|
||||
// Otherwise, continue from previous block's first free slot
|
||||
nextFreeSlot = nextFreeSlots[nextFreeSlotsSize - 1];
|
||||
}
|
||||
if(nextFreeSlotsSize == nextFreeSlots.length) {
|
||||
final int[] newNextFreeSlots = new int[nextFreeSlotsSize * 2];
|
||||
System.arraycopy(nextFreeSlots, 0, newNextFreeSlots, 0, nextFreeSlotsSize);
|
||||
nextFreeSlots = newNextFreeSlots;
|
||||
}
|
||||
nextFreeSlots[nextFreeSlotsSize++] = assignSlots(block, nextFreeSlot);
|
||||
final boolean isFunctionBody = lc.isFunctionBody();
|
||||
|
||||
final FunctionNode function = getLexicalContext().getCurrentFunction();
|
||||
final FunctionNode function = lc.getCurrentFunction();
|
||||
if (isFunctionBody) {
|
||||
/* Fix the predefined slots so they have numbers >= 0, like varargs. */
|
||||
if (function.needsParentScope()) {
|
||||
@ -1023,7 +914,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
}
|
||||
|
||||
if (symbol.isVar()) {
|
||||
if(varsInScope || symbol.isScope()) {
|
||||
if (varsInScope || symbol.isScope()) {
|
||||
nameList.add(symbol.getName());
|
||||
newSymbols.add(symbol);
|
||||
values.add(null);
|
||||
@ -1062,7 +953,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
|
||||
@Override
|
||||
protected void loadScope(MethodEmitter m) {
|
||||
if(function.needsParentScope()) {
|
||||
if (function.needsParentScope()) {
|
||||
m.loadCompilerConstant(SCOPE);
|
||||
} else {
|
||||
m.loadNull();
|
||||
@ -1096,7 +987,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
|
||||
private void initArguments(final FunctionNode function) {
|
||||
method.loadCompilerConstant(VARARGS);
|
||||
if(function.needsCallee()) {
|
||||
if (function.needsCallee()) {
|
||||
method.loadCompilerConstant(CALLEE);
|
||||
} else {
|
||||
// If function is strict mode, "arguments.callee" is not populated, so we don't necessarily need the
|
||||
@ -1126,10 +1017,10 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
LOG.info("=== BEGIN ", functionNode.getName());
|
||||
|
||||
assert functionNode.getCompileUnit() != null : "no compile unit for " + functionNode.getName() + " " + Debug.id(functionNode);
|
||||
push(functionNode.getCompileUnit());
|
||||
assert !compileUnits.isEmpty();
|
||||
unit = lc.pushCompileUnit(functionNode.getCompileUnit());
|
||||
assert lc.hasCompileUnits();
|
||||
|
||||
pushMethodEmitter(unit.getClassEmitter().method(functionNode));
|
||||
method = lc.pushMethodEmitter(unit.getClassEmitter().method(functionNode));
|
||||
// Mark end for variable tables.
|
||||
method.begin();
|
||||
|
||||
@ -1140,11 +1031,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
public Node leaveFunctionNode(final FunctionNode functionNode) {
|
||||
try {
|
||||
method.end(); // wrap up this method
|
||||
pop(functionNode.getCompileUnit());
|
||||
popMethodEmitter(method);
|
||||
unit = lc.popCompileUnit(functionNode.getCompileUnit());
|
||||
method = lc.popMethodEmitter(method);
|
||||
LOG.info("=== END ", functionNode.getName());
|
||||
|
||||
final FunctionNode newFunctionNode = functionNode.setState(getLexicalContext(), CompilationState.EMITTED);
|
||||
final FunctionNode newFunctionNode = functionNode.setState(lc, CompilationState.EMITTED);
|
||||
|
||||
newFunctionObject(newFunctionNode, functionNode);
|
||||
return newFunctionNode;
|
||||
@ -1238,16 +1129,16 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
final MethodEmitter savedMethod = method;
|
||||
|
||||
for (final ArrayUnit arrayUnit : units) {
|
||||
push(arrayUnit.getCompileUnit());
|
||||
unit = lc.pushCompileUnit(arrayUnit.getCompileUnit());
|
||||
|
||||
final String className = unit.getUnitClassName();
|
||||
final String name = getLexicalContext().getCurrentFunction().uniqueName(SPLIT_PREFIX.symbolName());
|
||||
final String name = lc.getCurrentFunction().uniqueName(SPLIT_PREFIX.symbolName());
|
||||
final String signature = methodDescriptor(type, Object.class, ScriptFunction.class, ScriptObject.class, type);
|
||||
|
||||
final MethodEmitter me = unit.getClassEmitter().method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), name, signature);
|
||||
pushMethodEmitter(me);
|
||||
method = lc.pushMethodEmitter(me);
|
||||
|
||||
method.setFunctionNode(getLexicalContext().getCurrentFunction());
|
||||
method.setFunctionNode(lc.getCurrentFunction());
|
||||
method.begin();
|
||||
|
||||
fixScopeSlot();
|
||||
@ -1260,7 +1151,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
|
||||
method._return();
|
||||
method.end();
|
||||
popMethodEmitter(me);
|
||||
method = lc.popMethodEmitter(me);
|
||||
|
||||
assert method == savedMethod;
|
||||
method.loadCompilerConstant(THIS);
|
||||
@ -1271,7 +1162,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
method.swap();
|
||||
method.invokestatic(className, name, signature);
|
||||
|
||||
pop(unit);
|
||||
unit = lc.popCompileUnit(unit);
|
||||
}
|
||||
|
||||
return method;
|
||||
@ -1407,7 +1298,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
return loadRegexToken(regexToken);
|
||||
}
|
||||
// emit field
|
||||
final String regexName = getLexicalContext().getCurrentFunction().uniqueName(REGEX_PREFIX.symbolName());
|
||||
final String regexName = lc.getCurrentFunction().uniqueName(REGEX_PREFIX.symbolName());
|
||||
final ClassEmitter classEmitter = unit.getClassEmitter();
|
||||
|
||||
classEmitter.field(EnumSet.of(PRIVATE, STATIC), regexName, Object.class);
|
||||
@ -1545,7 +1436,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
|
||||
method.registerReturn();
|
||||
|
||||
final Type returnType = getLexicalContext().getCurrentFunction().getReturnType();
|
||||
final Type returnType = lc.getCurrentFunction().getReturnType();
|
||||
|
||||
final Node expression = returnNode.getExpression();
|
||||
if (expression != null) {
|
||||
@ -1756,7 +1647,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
|
||||
final CompileUnit splitCompileUnit = splitNode.getCompileUnit();
|
||||
|
||||
final FunctionNode fn = getLexicalContext().getCurrentFunction();
|
||||
final FunctionNode fn = lc.getCurrentFunction();
|
||||
final String className = splitCompileUnit.getUnitClassName();
|
||||
final String name = splitNode.getName();
|
||||
|
||||
@ -1767,7 +1658,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
new Class<?>[] {ScriptFunction.class, Object.class, ScriptObject.class};
|
||||
|
||||
final MethodEmitter caller = method;
|
||||
push(splitCompileUnit);
|
||||
unit = lc.pushCompileUnit(splitCompileUnit);
|
||||
|
||||
final Call splitCall = staticCallNoLookup(
|
||||
className,
|
||||
@ -1781,8 +1672,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
rtype,
|
||||
ptypes);
|
||||
|
||||
pushMethodEmitter(splitEmitter);
|
||||
|
||||
method = lc.pushMethodEmitter(splitEmitter);
|
||||
method.setFunctionNode(fn);
|
||||
|
||||
if (fn.needsCallee()) {
|
||||
@ -1809,7 +1699,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
}
|
||||
|
||||
private void fixScopeSlot() {
|
||||
if (getLexicalContext().getCurrentFunction().compilerConstant(SCOPE).getSlot() != SCOPE.slot()) {
|
||||
if (lc.getCurrentFunction().compilerConstant(SCOPE).getSlot() != SCOPE.slot()) {
|
||||
// TODO hack to move the scope to the expected slot (that's needed because split methods reuse the same slots as the root method)
|
||||
method.load(Type.typeFor(ScriptObject.class), SCOPE.slot());
|
||||
method.storeCompilerConstant(SCOPE);
|
||||
@ -1826,15 +1716,15 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
// Wrap up this method.
|
||||
|
||||
method.loadCompilerConstant(RETURN);
|
||||
method._return(getLexicalContext().getCurrentFunction().getReturnType());
|
||||
method._return(lc.getCurrentFunction().getReturnType());
|
||||
method.end();
|
||||
|
||||
pop(splitNode.getCompileUnit());
|
||||
popMethodEmitter(method);
|
||||
unit = lc.popCompileUnit(splitNode.getCompileUnit());
|
||||
method = lc.popMethodEmitter(method);
|
||||
|
||||
} catch (final Throwable t) {
|
||||
Context.printStackTrace(t);
|
||||
final VerifyError e = new VerifyError("Code generation bug in \"" + splitNode.getName() + "\": likely stack misaligned: " + t + " " + getLexicalContext().getCurrentFunction().getSource().getName());
|
||||
final VerifyError e = new VerifyError("Code generation bug in \"" + splitNode.getName() + "\": likely stack misaligned: " + t + " " + lc.getCurrentFunction().getSource().getName());
|
||||
e.initCause(t);
|
||||
throw e;
|
||||
}
|
||||
@ -1862,7 +1752,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
//has to be zero
|
||||
caller.label(new Label("split_return"));
|
||||
method.loadCompilerConstant(RETURN);
|
||||
caller._return(getLexicalContext().getCurrentFunction().getReturnType());
|
||||
caller._return(lc.getCurrentFunction().getReturnType());
|
||||
caller.label(breakLabel);
|
||||
} else {
|
||||
assert !targets.isEmpty();
|
||||
@ -1879,14 +1769,14 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
caller.label(labels[i - low]);
|
||||
if (i == 0) {
|
||||
caller.loadCompilerConstant(RETURN);
|
||||
caller._return(getLexicalContext().getCurrentFunction().getReturnType());
|
||||
caller._return(lc.getCurrentFunction().getReturnType());
|
||||
} else {
|
||||
// Clear split state.
|
||||
caller.loadCompilerConstant(SCOPE);
|
||||
caller.checkcast(Scope.class);
|
||||
caller.load(-1);
|
||||
caller.invoke(Scope.SET_SPLIT_STATE);
|
||||
caller.splitAwareGoto(getLexicalContext(), targets.get(i - 1));
|
||||
caller.splitAwareGoto(lc, targets.get(i - 1));
|
||||
}
|
||||
}
|
||||
caller.label(breakLabel);
|
||||
@ -2028,9 +1918,16 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
public boolean enterThrowNode(final ThrowNode throwNode) {
|
||||
lineNumber(throwNode);
|
||||
|
||||
if (throwNode.isSyntheticRethrow()) {
|
||||
//do not wrap whatever this is in an ecma exception, just rethrow it
|
||||
load(throwNode.getExpression());
|
||||
method.athrow();
|
||||
return false;
|
||||
}
|
||||
|
||||
method._new(ECMAException.class).dup();
|
||||
|
||||
final Source source = getLexicalContext().getCurrentFunction().getSource();
|
||||
final Source source = lc.getCurrentFunction().getSource();
|
||||
|
||||
final Node expression = throwNode.getExpression();
|
||||
final int position = throwNode.position();
|
||||
@ -2081,7 +1978,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
//TODO this is very ugly - try not to call enter/leave methods directly
|
||||
//better to use the implicit lexical context scoping given by the visitor's
|
||||
//accept method.
|
||||
getLexicalContext().push(catchBlock);
|
||||
lc.push(catchBlock);
|
||||
enterBlock(catchBlock);
|
||||
|
||||
final CatchNode catchNode = (CatchNode)catchBlocks.get(i).getStatements().get(0);
|
||||
@ -2094,15 +1991,19 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
protected void storeNonDiscard() {
|
||||
return;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void evaluate() {
|
||||
if (catchNode.isSyntheticRethrow()) {
|
||||
method.load(symbol);
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* If caught object is an instance of ECMAException, then
|
||||
* bind obj.thrown to the script catch var. Or else bind the
|
||||
* caught object itself to the script catch var.
|
||||
*/
|
||||
final Label notEcmaException = new Label("no_ecma_exception");
|
||||
|
||||
method.load(symbol).dup()._instanceof(ECMAException.class).ifeq(notEcmaException);
|
||||
method.checkcast(ECMAException.class); //TODO is this necessary?
|
||||
method.getField(ECMAException.THROWN);
|
||||
@ -2137,7 +2038,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
}
|
||||
|
||||
leaveBlock(catchBlock);
|
||||
getLexicalContext().pop(catchBlock);
|
||||
lc.pop(catchBlock);
|
||||
}
|
||||
|
||||
method.label(skip);
|
||||
@ -2234,7 +2135,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
final boolean hasScope = method.hasScope();
|
||||
|
||||
final Label tryLabel;
|
||||
if(hasScope) {
|
||||
if (hasScope) {
|
||||
tryLabel = new Label("with_try");
|
||||
method.label(tryLabel);
|
||||
method.loadCompilerConstant(SCOPE);
|
||||
@ -2245,7 +2146,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
load(expression);
|
||||
assert expression.getType().isObject() : "with expression needs to be object: " + expression;
|
||||
|
||||
if(hasScope) {
|
||||
if (hasScope) {
|
||||
// Construct a WithObject if we have a scope
|
||||
method.invoke(ScriptRuntime.OPEN_WITH);
|
||||
method.storeCompilerConstant(SCOPE);
|
||||
@ -2285,7 +2186,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
@Override
|
||||
public boolean enterADD(final UnaryNode unaryNode) {
|
||||
load(unaryNode.rhs());
|
||||
assert unaryNode.rhs().getType().isNumber();
|
||||
assert unaryNode.rhs().getType().isNumber() : unaryNode.rhs().getType() + " "+ unaryNode.getSymbol();
|
||||
method.store(unaryNode.getSymbol());
|
||||
|
||||
return false;
|
||||
@ -2320,7 +2221,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
}
|
||||
method.convert(Type.OBJECT);
|
||||
} else if (value instanceof Boolean) {
|
||||
method.getField(staticField(Boolean.class, value.toString().toUpperCase(), Boolean.class));
|
||||
method.getField(staticField(Boolean.class, value.toString().toUpperCase(Locale.ENGLISH), Boolean.class));
|
||||
} else {
|
||||
load(rhs);
|
||||
method.convert(unaryNode.getType());
|
||||
@ -2387,13 +2288,13 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
public boolean enterDISCARD(final UnaryNode unaryNode) {
|
||||
final Node rhs = unaryNode.rhs();
|
||||
|
||||
discard.push(rhs);
|
||||
lc.pushDiscard(rhs);
|
||||
load(rhs);
|
||||
|
||||
if (discard.peek() == rhs) {
|
||||
if (lc.getCurrentDiscard() == rhs) {
|
||||
assert !rhs.isAssignment();
|
||||
method.pop();
|
||||
discard.pop();
|
||||
lc.popDiscard();
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -2445,7 +2346,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
assert lhs.getType().equals(rhs.getType()) && lhs.getType().equals(type) : lhs.getType() + " != " + rhs.getType() + " != " + type + " " + new ASTWriter(lhs) + " " + new ASTWriter(rhs);
|
||||
load(lhs);
|
||||
load(rhs);
|
||||
method.add();
|
||||
method.add(); //if the symbol is optimistic, it always needs to be written, not on the stack?
|
||||
method.store(symbol);
|
||||
return null;
|
||||
}
|
||||
@ -2989,52 +2890,11 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
* Generate all shared scope calls generated during codegen.
|
||||
*/
|
||||
protected void generateScopeCalls() {
|
||||
for (final SharedScopeCall scopeAccess : scopeCalls.values()) {
|
||||
for (final SharedScopeCall scopeAccess : lc.getScopeCalls()) {
|
||||
scopeAccess.generateScopeCall();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a shared static method representing a dynamic scope callsite.
|
||||
*
|
||||
* @param symbol the symbol
|
||||
* @param valueType the value type of the symbol
|
||||
* @param returnType the return type
|
||||
* @param paramTypes the parameter types
|
||||
* @param flags the callsite flags
|
||||
* @return an object representing a shared scope call
|
||||
*/
|
||||
private SharedScopeCall getScopeCall(final Symbol symbol, final Type valueType, final Type returnType,
|
||||
final Type[] paramTypes, final int flags) {
|
||||
|
||||
final SharedScopeCall scopeCall = new SharedScopeCall(symbol, valueType, returnType, paramTypes, flags);
|
||||
if (scopeCalls.containsKey(scopeCall)) {
|
||||
return scopeCalls.get(scopeCall);
|
||||
}
|
||||
scopeCall.setClassAndName(unit, getLexicalContext().getCurrentFunction().uniqueName("scopeCall"));
|
||||
scopeCalls.put(scopeCall, scopeCall);
|
||||
return scopeCall;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a shared static method representing a dynamic scope get access.
|
||||
*
|
||||
* @param type the type of the variable
|
||||
* @param symbol the symbol
|
||||
* @param flags the callsite flags
|
||||
* @return an object representing a shared scope call
|
||||
*/
|
||||
private SharedScopeCall getScopeGet(final Type type, final Symbol symbol, final int flags) {
|
||||
|
||||
final SharedScopeCall scopeCall = new SharedScopeCall(symbol, type, type, null, flags);
|
||||
if (scopeCalls.containsKey(scopeCall)) {
|
||||
return scopeCalls.get(scopeCall);
|
||||
}
|
||||
scopeCall.setClassAndName(unit, getLexicalContext().getCurrentFunction().uniqueName("scopeCall"));
|
||||
scopeCalls.put(scopeCall, scopeCall);
|
||||
return scopeCall;
|
||||
}
|
||||
|
||||
/**
|
||||
* Debug code used to print symbols
|
||||
*
|
||||
@ -3129,14 +2989,14 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
|
||||
private void prologue() {
|
||||
final Symbol targetSymbol = target.getSymbol();
|
||||
final Symbol scopeSymbol = getLexicalContext().getCurrentFunction().compilerConstant(SCOPE);
|
||||
final Symbol scopeSymbol = lc.getCurrentFunction().compilerConstant(SCOPE);
|
||||
|
||||
/**
|
||||
* This loads the parts of the target, e.g base and index. they are kept
|
||||
* on the stack throughout the store and used at the end to execute it
|
||||
*/
|
||||
|
||||
target.accept(new NodeVisitor() {
|
||||
target.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
|
||||
@Override
|
||||
public boolean enterIdentNode(final IdentNode node) {
|
||||
if (targetSymbol.isScope()) {
|
||||
@ -3203,22 +3063,21 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
* @return the quick symbol
|
||||
*/
|
||||
private Symbol quickSymbol(final Type type, final String prefix) {
|
||||
final String name = getLexicalContext().getCurrentFunction().uniqueName(prefix);
|
||||
final String name = lc.getCurrentFunction().uniqueName(prefix);
|
||||
final Symbol symbol = new Symbol(name, IS_TEMP | IS_INTERNAL);
|
||||
|
||||
symbol.setType(type);
|
||||
final int quickSlot = nextFreeSlots[nextFreeSlotsSize - 1];
|
||||
nextFreeSlots[nextFreeSlotsSize - 1] = quickSlot + symbol.slotCount();
|
||||
symbol.setSlot(quickSlot);
|
||||
|
||||
symbol.setSlot(lc.quickSlot(symbol));
|
||||
|
||||
return symbol;
|
||||
}
|
||||
|
||||
// store the result that "lives on" after the op, e.g. "i" in i++ postfix.
|
||||
protected void storeNonDiscard() {
|
||||
if (discard.peek() == assignNode) {
|
||||
if (lc.getCurrentDiscard() == assignNode) {
|
||||
assert assignNode.isAssignment();
|
||||
discard.pop();
|
||||
lc.popDiscard();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -3246,7 +3105,7 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
*/
|
||||
method.convert(target.getType());
|
||||
|
||||
target.accept(new NodeVisitor() {
|
||||
target.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
|
||||
@Override
|
||||
protected boolean enterDefault(Node node) {
|
||||
throw new AssertionError("Unexpected node " + node + " in store epilogue");
|
||||
@ -3308,7 +3167,6 @@ final class CodeGenerator extends NodeOperatorVisitor {
|
||||
}
|
||||
|
||||
private void newFunctionObject(final FunctionNode functionNode, final FunctionNode originalFunctionNode) {
|
||||
final LexicalContext lc = getLexicalContext();
|
||||
assert lc.peek() == functionNode;
|
||||
// We don't emit a ScriptFunction on stack for:
|
||||
// 1. the outermost compiled function (as there's no code being generated in its outer context that'd need it
|
||||
|
@ -0,0 +1,235 @@
|
||||
/*
|
||||
* 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.ArrayDeque;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.Block;
|
||||
import jdk.nashorn.internal.ir.FunctionNode;
|
||||
import jdk.nashorn.internal.ir.LexicalContext;
|
||||
import jdk.nashorn.internal.ir.LexicalContextNode;
|
||||
import jdk.nashorn.internal.ir.Node;
|
||||
import jdk.nashorn.internal.ir.Symbol;
|
||||
import jdk.nashorn.internal.ir.WithNode;
|
||||
|
||||
/**
|
||||
* A lexical context that also tracks if we have any dynamic scopes in the context. Such scopes can have new
|
||||
* variables introduced into them at run time - a with block or a function directly containing an eval call.
|
||||
* Furthermore, this class keeps track of current discard state, which the current method emitter being used is,
|
||||
* the current compile unit, and local variable indexes
|
||||
*/
|
||||
final class CodeGeneratorLexicalContext extends LexicalContext {
|
||||
private int dynamicScopeCount;
|
||||
|
||||
/** Map of shared scope call sites */
|
||||
private final Map<SharedScopeCall, SharedScopeCall> scopeCalls = new HashMap<>();
|
||||
|
||||
/** Compile unit stack - every time we start a sub method (e.g. a split) we push one */
|
||||
private final Deque<CompileUnit> compileUnits = new ArrayDeque<>();
|
||||
|
||||
/** Method emitter stack - every time we start a sub method (e.g. a split) we push one */
|
||||
private final Deque<MethodEmitter> methodEmitters = new ArrayDeque<>();
|
||||
|
||||
/** The discard stack - whenever we enter a discard node we keep track of its return value status -
|
||||
* i.e. should we keep it or throw it away */
|
||||
private final Deque<Node> discard = new ArrayDeque<>();
|
||||
|
||||
/** A stack tracking the next free local variable slot in the blocks. There's one entry for every block
|
||||
* currently on the lexical context stack. */
|
||||
private int[] nextFreeSlots = new int[16];
|
||||
|
||||
/** size of next free slot vector */
|
||||
private int nextFreeSlotsSize;
|
||||
|
||||
@Override
|
||||
public <T extends LexicalContextNode> T push(final T node) {
|
||||
if (isDynamicScopeBoundary(node)) {
|
||||
++dynamicScopeCount;
|
||||
}
|
||||
return super.push(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends LexicalContextNode> T pop(final T node) {
|
||||
final T popped = super.pop(node);
|
||||
if (isDynamicScopeBoundary(popped)) {
|
||||
--dynamicScopeCount;
|
||||
}
|
||||
if (node instanceof Block) {
|
||||
--nextFreeSlotsSize;
|
||||
}
|
||||
return popped;
|
||||
}
|
||||
|
||||
private boolean isDynamicScopeBoundary(final LexicalContextNode node) {
|
||||
if (node instanceof Block) {
|
||||
// Block's immediate parent is a with node. Note we aren't testing for a WithNode, as that'd capture
|
||||
// processing of WithNode.expression too, but it should be unaffected.
|
||||
return !isEmpty() && peek() instanceof WithNode;
|
||||
} else if (node instanceof FunctionNode) {
|
||||
// Function has a direct eval in it (so a top-level "var ..." in the eval code can introduce a new
|
||||
// variable into the function's scope), and it isn't strict (as evals in strict functions get an
|
||||
// isolated scope).
|
||||
return isFunctionDynamicScope((FunctionNode)node);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean inDynamicScope() {
|
||||
return dynamicScopeCount > 0;
|
||||
}
|
||||
|
||||
static boolean isFunctionDynamicScope(FunctionNode fn) {
|
||||
return fn.hasEval() && !fn.isStrict();
|
||||
}
|
||||
|
||||
MethodEmitter pushMethodEmitter(final MethodEmitter newMethod) {
|
||||
methodEmitters.push(newMethod);
|
||||
return newMethod;
|
||||
}
|
||||
|
||||
MethodEmitter popMethodEmitter(final MethodEmitter oldMethod) {
|
||||
assert methodEmitters.peek() == oldMethod;
|
||||
methodEmitters.pop();
|
||||
return methodEmitters.isEmpty() ? null : methodEmitters.peek();
|
||||
}
|
||||
|
||||
CompileUnit pushCompileUnit(final CompileUnit newUnit) {
|
||||
compileUnits.push(newUnit);
|
||||
return newUnit;
|
||||
}
|
||||
|
||||
CompileUnit popCompileUnit(final CompileUnit oldUnit) {
|
||||
assert compileUnits.peek() == oldUnit;
|
||||
compileUnits.pop();
|
||||
return compileUnits.isEmpty() ? null : compileUnits.peek();
|
||||
}
|
||||
|
||||
boolean hasCompileUnits() {
|
||||
return !compileUnits.isEmpty();
|
||||
}
|
||||
|
||||
Collection<SharedScopeCall> getScopeCalls() {
|
||||
return Collections.unmodifiableCollection(scopeCalls.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a shared static method representing a dynamic scope callsite.
|
||||
*
|
||||
* @param unit current compile unit
|
||||
* @param symbol the symbol
|
||||
* @param valueType the value type of the symbol
|
||||
* @param returnType the return type
|
||||
* @param paramTypes the parameter types
|
||||
* @param flags the callsite flags
|
||||
* @return an object representing a shared scope call
|
||||
*/
|
||||
SharedScopeCall getScopeCall(final CompileUnit unit, final Symbol symbol, final Type valueType, final Type returnType, final Type[] paramTypes, final int flags) {
|
||||
final SharedScopeCall scopeCall = new SharedScopeCall(symbol, valueType, returnType, paramTypes, flags);
|
||||
if (scopeCalls.containsKey(scopeCall)) {
|
||||
return scopeCalls.get(scopeCall);
|
||||
}
|
||||
scopeCall.setClassAndName(unit, getCurrentFunction().uniqueName("scopeCall"));
|
||||
scopeCalls.put(scopeCall, scopeCall);
|
||||
return scopeCall;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a shared static method representing a dynamic scope get access.
|
||||
*
|
||||
* @param unit current compile unit
|
||||
* @param type the type of the variable
|
||||
* @param symbol the symbol
|
||||
* @param flags the callsite flags
|
||||
* @return an object representing a shared scope call
|
||||
*/
|
||||
SharedScopeCall getScopeGet(final CompileUnit unit, final Type type, final Symbol symbol, final int flags) {
|
||||
final SharedScopeCall scopeCall = new SharedScopeCall(symbol, type, type, null, flags);
|
||||
if (scopeCalls.containsKey(scopeCall)) {
|
||||
return scopeCalls.get(scopeCall);
|
||||
}
|
||||
scopeCall.setClassAndName(unit, getCurrentFunction().uniqueName("scopeCall"));
|
||||
scopeCalls.put(scopeCall, scopeCall);
|
||||
return scopeCall;
|
||||
}
|
||||
|
||||
|
||||
void nextFreeSlot(final Block block) {
|
||||
final boolean isFunctionBody = isFunctionBody();
|
||||
|
||||
final int nextFreeSlot;
|
||||
if (isFunctionBody) {
|
||||
// On entry to function, start with slot 0
|
||||
nextFreeSlot = 0;
|
||||
} else {
|
||||
// Otherwise, continue from previous block's first free slot
|
||||
nextFreeSlot = nextFreeSlots[nextFreeSlotsSize - 1];
|
||||
}
|
||||
if (nextFreeSlotsSize == nextFreeSlots.length) {
|
||||
final int[] newNextFreeSlots = new int[nextFreeSlotsSize * 2];
|
||||
System.arraycopy(nextFreeSlots, 0, newNextFreeSlots, 0, nextFreeSlotsSize);
|
||||
nextFreeSlots = newNextFreeSlots;
|
||||
}
|
||||
nextFreeSlots[nextFreeSlotsSize++] = assignSlots(block, nextFreeSlot);
|
||||
}
|
||||
|
||||
private static int assignSlots(final Block block, final int firstSlot) {
|
||||
int nextSlot = firstSlot;
|
||||
for (final Symbol symbol : block.getSymbols()) {
|
||||
if (symbol.hasSlot()) {
|
||||
symbol.setSlot(nextSlot);
|
||||
nextSlot += symbol.slotCount();
|
||||
}
|
||||
}
|
||||
return nextSlot;
|
||||
}
|
||||
|
||||
void pushDiscard(final Node node) {
|
||||
discard.push(node);
|
||||
}
|
||||
|
||||
Node popDiscard() {
|
||||
return discard.pop();
|
||||
}
|
||||
|
||||
Node getCurrentDiscard() {
|
||||
return discard.peek();
|
||||
}
|
||||
|
||||
int quickSlot(final Symbol symbol) {
|
||||
final int quickSlot = nextFreeSlots[nextFreeSlotsSize - 1];
|
||||
nextFreeSlots[nextFreeSlotsSize - 1] = quickSlot + symbol.slotCount();
|
||||
return quickSlot;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -11,20 +11,27 @@ import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SPLIT;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Deque;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import jdk.nashorn.internal.codegen.types.Range;
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.Block;
|
||||
import jdk.nashorn.internal.ir.CallNode;
|
||||
import jdk.nashorn.internal.ir.FunctionNode;
|
||||
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
|
||||
import jdk.nashorn.internal.ir.LexicalContext;
|
||||
import jdk.nashorn.internal.ir.ReturnNode;
|
||||
import jdk.nashorn.internal.ir.Symbol;
|
||||
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
|
||||
import jdk.nashorn.internal.ir.Node;
|
||||
import jdk.nashorn.internal.ir.TemporarySymbols;
|
||||
import jdk.nashorn.internal.ir.debug.ASTWriter;
|
||||
import jdk.nashorn.internal.ir.debug.PrintVisitor;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.runtime.ECMAErrors;
|
||||
import jdk.nashorn.internal.runtime.ScriptEnvironment;
|
||||
@ -66,7 +73,7 @@ enum CompilationPhase {
|
||||
|
||||
FunctionNode newFunctionNode = outermostFunctionNode;
|
||||
|
||||
newFunctionNode = (FunctionNode)newFunctionNode.accept(new NodeVisitor() {
|
||||
newFunctionNode = (FunctionNode)newFunctionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
|
||||
// self references are done with invokestatic and thus cannot
|
||||
// have trampolines - never lazy
|
||||
@Override
|
||||
@ -99,10 +106,9 @@ enum CompilationPhase {
|
||||
lazy.remove(node);
|
||||
}
|
||||
|
||||
newFunctionNode = (FunctionNode)newFunctionNode.accept(new NodeOperatorVisitor() {
|
||||
newFunctionNode = (FunctionNode)newFunctionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
|
||||
@Override
|
||||
public Node leaveFunctionNode(final FunctionNode functionNode) {
|
||||
final LexicalContext lc = getLexicalContext();
|
||||
if (lazy.contains(functionNode)) {
|
||||
Compiler.LOG.fine(
|
||||
"Marking ",
|
||||
@ -174,7 +180,7 @@ enum CompilationPhase {
|
||||
FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
|
||||
final TemporarySymbols ts = compiler.getTemporarySymbols();
|
||||
final FunctionNode newFunctionNode = (FunctionNode)enterAttr(fn, ts).accept(new Attr(ts));
|
||||
if(compiler.getEnv()._print_mem_usage) {
|
||||
if (compiler.getEnv()._print_mem_usage) {
|
||||
Compiler.LOG.info("Attr temporary symbol count: " + ts.getTotalSymbolCount());
|
||||
}
|
||||
return newFunctionNode;
|
||||
@ -186,12 +192,11 @@ enum CompilationPhase {
|
||||
* @param functionNode node where to start iterating
|
||||
*/
|
||||
private FunctionNode enterAttr(final FunctionNode functionNode, final TemporarySymbols ts) {
|
||||
return (FunctionNode)functionNode.accept(new NodeVisitor() {
|
||||
return (FunctionNode)functionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
|
||||
@Override
|
||||
public Node leaveFunctionNode(final FunctionNode node) {
|
||||
final LexicalContext lc = getLexicalContext();
|
||||
if (node.isLazy()) {
|
||||
FunctionNode newNode = node.setReturnType(getLexicalContext(), Type.OBJECT);
|
||||
FunctionNode newNode = node.setReturnType(lc, Type.OBJECT);
|
||||
return ts.ensureSymbol(lc, Type.OBJECT, newNode);
|
||||
}
|
||||
//node may have a reference here that needs to be nulled if it was referred to by
|
||||
@ -207,6 +212,89 @@ enum CompilationPhase {
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* Range analysis
|
||||
* Conservatively prove that certain variables can be narrower than
|
||||
* the most generic number type
|
||||
*/
|
||||
RANGE_ANALYSIS_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR)) {
|
||||
@Override
|
||||
FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
|
||||
if (!compiler.getEnv()._range_analysis) {
|
||||
return fn;
|
||||
}
|
||||
|
||||
FunctionNode newFunctionNode = (FunctionNode)fn.accept(new RangeAnalyzer());
|
||||
final List<ReturnNode> returns = new ArrayList<>();
|
||||
|
||||
newFunctionNode = (FunctionNode)newFunctionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
|
||||
private final Deque<ArrayList<ReturnNode>> returnStack = new ArrayDeque<>();
|
||||
|
||||
@Override
|
||||
public boolean enterFunctionNode(final FunctionNode functionNode) {
|
||||
returnStack.push(new ArrayList<ReturnNode>());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveFunctionNode(final FunctionNode functionNode) {
|
||||
Type returnType = Type.UNKNOWN;
|
||||
for (final ReturnNode ret : returnStack.pop()) {
|
||||
if (ret.getExpression() == null) {
|
||||
returnType = Type.OBJECT;
|
||||
break;
|
||||
}
|
||||
returnType = Type.widest(returnType, ret.getExpression().getType());
|
||||
}
|
||||
return functionNode.setReturnType(lc, returnType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveReturnNode(final ReturnNode returnNode) {
|
||||
final ReturnNode result = (ReturnNode)leaveDefault(returnNode);
|
||||
returns.add(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveDefault(final Node node) {
|
||||
final Symbol symbol = node.getSymbol();
|
||||
if (symbol != null) {
|
||||
final Range range = symbol.getRange();
|
||||
final Type symbolType = symbol.getSymbolType();
|
||||
if (!symbolType.isNumeric()) {
|
||||
return node;
|
||||
}
|
||||
final Type rangeType = range.getType();
|
||||
if (!Type.areEquivalent(symbolType, rangeType) && Type.widest(symbolType, rangeType) == symbolType) { //we can narrow range
|
||||
RangeAnalyzer.LOG.info("[", lc.getCurrentFunction().getName(), "] ", symbol, " can be ", range.getType(), " ", symbol.getRange());
|
||||
return node.setSymbol(lc, symbol.setTypeOverrideShared(range.getType(), compiler.getTemporarySymbols()));
|
||||
}
|
||||
}
|
||||
return node;
|
||||
}
|
||||
});
|
||||
|
||||
Type returnType = Type.UNKNOWN;
|
||||
for (final ReturnNode node : returns) {
|
||||
if (node.getExpression() != null) {
|
||||
returnType = Type.widest(returnType, node.getExpression().getType());
|
||||
} else {
|
||||
returnType = Type.OBJECT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return newFunctionNode.setReturnType(null, returnType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[Range Analysis]";
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
* Splitter Split the AST into several compile units based on a size
|
||||
* heuristic Splitter needs attributed AST for weight calculations (e.g. is
|
||||
@ -218,7 +306,6 @@ enum CompilationPhase {
|
||||
FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
|
||||
final CompileUnit outermostCompileUnit = compiler.addCompileUnit(compiler.firstCompileUnitName());
|
||||
|
||||
// assert fn.isProgram() ;
|
||||
final FunctionNode newFunctionNode = new Splitter(compiler, fn, outermostCompileUnit).split(fn);
|
||||
|
||||
assert newFunctionNode.getCompileUnit() == outermostCompileUnit : "fn.compileUnit (" + newFunctionNode.getCompileUnit() + ") != " + outermostCompileUnit;
|
||||
|
@ -99,7 +99,7 @@ public final class Compiler {
|
||||
|
||||
private boolean strict;
|
||||
|
||||
private CodeInstaller<ScriptEnvironment> installer;
|
||||
private final CodeInstaller<ScriptEnvironment> installer;
|
||||
|
||||
private final TemporarySymbols temporarySymbols = new TemporarySymbols();
|
||||
|
||||
@ -219,6 +219,7 @@ public final class Compiler {
|
||||
CompilationPhase.CONSTANT_FOLDING_PHASE,
|
||||
CompilationPhase.LOWERING_PHASE,
|
||||
CompilationPhase.ATTRIBUTION_PHASE,
|
||||
CompilationPhase.RANGE_ANALYSIS_PHASE,
|
||||
CompilationPhase.SPLITTING_PHASE,
|
||||
CompilationPhase.TYPE_FINALIZATION_PHASE,
|
||||
CompilationPhase.BYTECODE_GENERATION_PHASE);
|
||||
@ -384,6 +385,8 @@ public final class Compiler {
|
||||
if (info) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append("Compile job for '").
|
||||
append(newFunctionNode.getSource()).
|
||||
append(':').
|
||||
append(newFunctionNode.getName()).
|
||||
append("' finished");
|
||||
|
||||
@ -487,7 +490,7 @@ public final class Compiler {
|
||||
}
|
||||
|
||||
if (sb != null) {
|
||||
LOG.info(sb);
|
||||
LOG.fine(sb);
|
||||
}
|
||||
|
||||
return rootClass;
|
||||
|
@ -262,7 +262,7 @@ public enum CompilerConstants {
|
||||
* @return the internal descriptor for this type
|
||||
*/
|
||||
public static String typeDescriptor(final Class<?> clazz) {
|
||||
return Type.getDescriptor(clazz);
|
||||
return Type.typeFor(clazz).getDescriptor();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -31,6 +31,7 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.AccessNode;
|
||||
import jdk.nashorn.internal.ir.Assignment;
|
||||
@ -84,13 +85,14 @@ import jdk.nashorn.internal.runtime.JSType;
|
||||
* and frame optimizations
|
||||
*/
|
||||
|
||||
final class FinalizeTypes extends NodeOperatorVisitor {
|
||||
final class FinalizeTypes extends NodeOperatorVisitor<LexicalContext> {
|
||||
|
||||
private static final DebugLogger LOG = new DebugLogger("finalize");
|
||||
|
||||
private final TemporarySymbols temporarySymbols;
|
||||
|
||||
FinalizeTypes(final TemporarySymbols temporarySymbols) {
|
||||
super(new LexicalContext());
|
||||
this.temporarySymbols = temporarySymbols;
|
||||
}
|
||||
|
||||
@ -233,7 +235,7 @@ final class FinalizeTypes extends NodeOperatorVisitor {
|
||||
|
||||
private boolean symbolIsInteger(Node node) {
|
||||
final Symbol symbol = node.getSymbol();
|
||||
assert symbol != null && symbol.getSymbolType().isInteger() : "int coercion expected: " + Debug.id(symbol) + " " + symbol + " " + getLexicalContext().getCurrentFunction().getSource();
|
||||
assert symbol != null && symbol.getSymbolType().isInteger() : "int coercion expected: " + Debug.id(symbol) + " " + symbol + " " + lc.getCurrentFunction().getSource();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -382,12 +384,10 @@ final class FinalizeTypes extends NodeOperatorVisitor {
|
||||
final Node test = forNode.getTest();
|
||||
final Node modify = forNode.getModify();
|
||||
|
||||
final LexicalContext lc = getLexicalContext();
|
||||
|
||||
if (forNode.isForIn()) {
|
||||
return forNode.setModify(lc, convert(forNode.getModify(), Type.OBJECT)); // NASHORN-400
|
||||
}
|
||||
assert test != null || forNode.hasGoto() : "forNode " + forNode + " needs goto and is missing it in " + getLexicalContext().getCurrentFunction();
|
||||
assert test != null || forNode.hasGoto() : "forNode " + forNode + " needs goto and is missing it in " + lc.getCurrentFunction();
|
||||
|
||||
return forNode.
|
||||
setInit(lc, init == null ? null : discard(init)).
|
||||
@ -419,7 +419,7 @@ final class FinalizeTypes extends NodeOperatorVisitor {
|
||||
|
||||
@Override
|
||||
public Node leaveFunctionNode(final FunctionNode functionNode) {
|
||||
return functionNode.setState(getLexicalContext(), CompilationState.FINALIZED);
|
||||
return functionNode.setState(lc, CompilationState.FINALIZED);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -450,7 +450,7 @@ final class FinalizeTypes extends NodeOperatorVisitor {
|
||||
public Node leaveReturnNode(final ReturnNode returnNode) {
|
||||
final Node expr = returnNode.getExpression();
|
||||
if (expr != null) {
|
||||
return returnNode.setExpression(convert(expr, getLexicalContext().getCurrentFunction().getReturnType()));
|
||||
return returnNode.setExpression(convert(expr, lc.getCurrentFunction().getReturnType()));
|
||||
}
|
||||
return returnNode;
|
||||
}
|
||||
@ -482,8 +482,8 @@ final class FinalizeTypes extends NodeOperatorVisitor {
|
||||
}
|
||||
|
||||
return switchNode.
|
||||
setExpression(getLexicalContext(), convert(expression, Type.OBJECT)).
|
||||
setCases(getLexicalContext(), newCases);
|
||||
setExpression(lc, convert(expression, Type.OBJECT)).
|
||||
setCases(lc, newCases);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -519,14 +519,14 @@ final class FinalizeTypes extends NodeOperatorVisitor {
|
||||
public Node leaveWhileNode(final WhileNode whileNode) {
|
||||
final Node test = whileNode.getTest();
|
||||
if (test != null) {
|
||||
return whileNode.setTest(getLexicalContext(), convert(test, Type.BOOLEAN));
|
||||
return whileNode.setTest(lc, convert(test, Type.BOOLEAN));
|
||||
}
|
||||
return whileNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveWithNode(final WithNode withNode) {
|
||||
return withNode.setExpression(getLexicalContext(), convert(withNode.getExpression(), Type.OBJECT));
|
||||
return withNode.setExpression(lc, convert(withNode.getExpression(), Type.OBJECT));
|
||||
}
|
||||
|
||||
private static void updateSymbolsLog(final FunctionNode functionNode, final Symbol symbol, final boolean loseSlot) {
|
||||
@ -550,7 +550,6 @@ final class FinalizeTypes extends NodeOperatorVisitor {
|
||||
return; // nothing to do
|
||||
}
|
||||
|
||||
final LexicalContext lc = getLexicalContext();
|
||||
final FunctionNode functionNode = lc.getFunction(block);
|
||||
final boolean allVarsInScope = functionNode.allVarsInScope();
|
||||
final boolean isVarArg = functionNode.isVarArg();
|
||||
@ -652,7 +651,7 @@ final class FinalizeTypes extends NodeOperatorVisitor {
|
||||
private static void setCanBePrimitive(final Node node, final Type to) {
|
||||
final HashSet<Node> exclude = new HashSet<>();
|
||||
|
||||
node.accept(new NodeVisitor() {
|
||||
node.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
|
||||
private void setCanBePrimitive(final Symbol symbol) {
|
||||
LOG.info("*** can be primitive symbol ", symbol, " ", Debug.id(symbol));
|
||||
symbol.setCanBePrimitive(to);
|
||||
@ -762,7 +761,7 @@ final class FinalizeTypes extends NodeOperatorVisitor {
|
||||
}
|
||||
}
|
||||
LOG.info("Type override for lhs in '", node, "' => ", to);
|
||||
return ((TypeOverride<T>)node).setType(temporarySymbols, getLexicalContext(), to);
|
||||
return ((TypeOverride<T>)node).setType(temporarySymbols, lc, to);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -785,8 +784,8 @@ final class FinalizeTypes extends NodeOperatorVisitor {
|
||||
private Node convert(final Node node, final Type to) {
|
||||
assert !to.isUnknown() : "unknown type for " + node + " class=" + node.getClass();
|
||||
assert node != null : "node is null";
|
||||
assert node.getSymbol() != null : "node " + node + " " + node.getClass() + " has no symbol! " + getLexicalContext().getCurrentFunction();
|
||||
assert node.tokenType() != TokenType.CONVERT : "assert convert in convert " + node + " in " + getLexicalContext().getCurrentFunction();
|
||||
assert node.getSymbol() != null : "node " + node + " " + node.getClass() + " has no symbol! " + lc.getCurrentFunction();
|
||||
assert node.tokenType() != TokenType.CONVERT : "assert convert in convert " + node + " in " + lc.getCurrentFunction();
|
||||
|
||||
final Type from = node.getType();
|
||||
|
||||
@ -800,7 +799,7 @@ final class FinalizeTypes extends NodeOperatorVisitor {
|
||||
|
||||
Node resultNode = node;
|
||||
|
||||
if (node instanceof LiteralNode && !to.isObject()) {
|
||||
if (node instanceof LiteralNode && !(node instanceof ArrayLiteralNode) && !to.isObject()) {
|
||||
final LiteralNode<?> newNode = new LiteralNodeConstantEvaluator((LiteralNode<?>)node, to).eval();
|
||||
if (newNode != null) {
|
||||
resultNode = newNode;
|
||||
@ -817,7 +816,6 @@ final class FinalizeTypes extends NodeOperatorVisitor {
|
||||
|
||||
assert !node.isTerminal();
|
||||
|
||||
final LexicalContext lc = getLexicalContext();
|
||||
//This is the only place in this file that can create new temporaries
|
||||
//FinalizeTypes may not introduce ANY node that is not a conversion.
|
||||
return temporarySymbols.ensureSymbol(lc, to, resultNode);
|
||||
@ -854,7 +852,7 @@ final class FinalizeTypes extends NodeOperatorVisitor {
|
||||
symbol = symbol.setTypeOverrideShared(to, temporarySymbols);
|
||||
LOG.info("Type override for temporary in '", node, "' => ", to);
|
||||
}
|
||||
return node.setSymbol(getLexicalContext(), symbol);
|
||||
return node.setSymbol(lc, symbol);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -907,7 +905,7 @@ final class FinalizeTypes extends NodeOperatorVisitor {
|
||||
|
||||
if (literalNode != null) {
|
||||
//inherit literal symbol for attr.
|
||||
literalNode = (LiteralNode<?>)literalNode.setSymbol(getLexicalContext(), parent.getSymbol());
|
||||
literalNode = (LiteralNode<?>)literalNode.setSymbol(lc, parent.getSymbol());
|
||||
}
|
||||
|
||||
return literalNode;
|
||||
|
@ -33,7 +33,9 @@ import jdk.nashorn.internal.ir.ExecuteNode;
|
||||
import jdk.nashorn.internal.ir.FunctionNode;
|
||||
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
|
||||
import jdk.nashorn.internal.ir.IfNode;
|
||||
import jdk.nashorn.internal.ir.LexicalContext;
|
||||
import jdk.nashorn.internal.ir.LiteralNode;
|
||||
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
|
||||
import jdk.nashorn.internal.ir.Node;
|
||||
import jdk.nashorn.internal.ir.TernaryNode;
|
||||
import jdk.nashorn.internal.ir.UnaryNode;
|
||||
@ -45,11 +47,12 @@ import jdk.nashorn.internal.runtime.ScriptRuntime;
|
||||
/**
|
||||
* Simple constant folding pass, executed before IR is starting to be lowered.
|
||||
*/
|
||||
final class FoldConstants extends NodeVisitor {
|
||||
final class FoldConstants extends NodeVisitor<LexicalContext> {
|
||||
|
||||
private static final DebugLogger LOG = new DebugLogger("fold");
|
||||
|
||||
FoldConstants() {
|
||||
super(new LexicalContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -79,7 +82,7 @@ final class FoldConstants extends NodeVisitor {
|
||||
|
||||
@Override
|
||||
public Node leaveFunctionNode(final FunctionNode functionNode) {
|
||||
return functionNode.setState(getLexicalContext(), CompilationState.CONSTANT_FOLDED);
|
||||
return functionNode.setState(lc, CompilationState.CONSTANT_FOLDED);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -141,6 +144,10 @@ final class FoldConstants extends NodeVisitor {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (rhsNode instanceof ArrayLiteralNode) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final LiteralNode<?> rhs = (LiteralNode<?>)rhsNode;
|
||||
final boolean rhsInteger = rhs.getType().isInteger();
|
||||
|
||||
@ -212,6 +219,10 @@ final class FoldConstants extends NodeVisitor {
|
||||
final LiteralNode<?> lhs = (LiteralNode<?>)parent.lhs();
|
||||
final LiteralNode<?> rhs = (LiteralNode<?>)parent.rhs();
|
||||
|
||||
if (lhs instanceof ArrayLiteralNode || rhs instanceof ArrayLiteralNode) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Type widest = Type.widest(lhs.getType(), rhs.getType());
|
||||
|
||||
boolean isInteger = widest.isInteger();
|
||||
@ -279,9 +290,9 @@ final class FoldConstants extends NodeVisitor {
|
||||
isLong &= value != 0.0 && JSType.isRepresentableAsLong(value);
|
||||
|
||||
if (isInteger) {
|
||||
return LiteralNode.newInstance(token, finish, JSType.toInt32(value));
|
||||
return LiteralNode.newInstance(token, finish, (int)value);
|
||||
} else if (isLong) {
|
||||
return LiteralNode.newInstance(token, finish, JSType.toLong(value));
|
||||
return LiteralNode.newInstance(token, finish, (long)value);
|
||||
}
|
||||
|
||||
return LiteralNode.newInstance(token, finish, value);
|
||||
|
@ -80,7 +80,7 @@ import jdk.nashorn.internal.runtime.Source;
|
||||
* finalized.
|
||||
*/
|
||||
|
||||
final class Lower extends NodeOperatorVisitor {
|
||||
final class Lower extends NodeOperatorVisitor<BlockLexicalContext> {
|
||||
|
||||
private static final DebugLogger LOG = new DebugLogger("lower");
|
||||
|
||||
@ -105,7 +105,7 @@ final class Lower extends NodeOperatorVisitor {
|
||||
terminated = true;
|
||||
}
|
||||
} else {
|
||||
statement.accept(new NodeVisitor() {
|
||||
statement.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
|
||||
@Override
|
||||
public boolean enterVarNode(final VarNode varNode) {
|
||||
newStatements.add(varNode.setInit(null));
|
||||
@ -121,7 +121,6 @@ final class Lower extends NodeOperatorVisitor {
|
||||
|
||||
@Override
|
||||
public boolean enterBlock(final Block block) {
|
||||
final LexicalContext lc = getLexicalContext();
|
||||
final FunctionNode function = lc.getCurrentFunction();
|
||||
if (lc.isFunctionBody() && function.isProgram() && !function.hasDeclaredFunctions()) {
|
||||
new ExecuteNode(block.getLineNumber(), block.getToken(), block.getFinish(), LiteralNode.newInstance(block, ScriptRuntime.UNDEFINED)).accept(this);
|
||||
@ -134,12 +133,10 @@ final class Lower extends NodeOperatorVisitor {
|
||||
//now we have committed the entire statement list to the block, but we need to truncate
|
||||
//whatever is after the last terminal. block append won't append past it
|
||||
|
||||
final BlockLexicalContext lc = (BlockLexicalContext)getLexicalContext();
|
||||
|
||||
Statement last = lc.getLastStatement();
|
||||
|
||||
if (lc.isFunctionBody()) {
|
||||
final FunctionNode currentFunction = getLexicalContext().getCurrentFunction();
|
||||
final FunctionNode currentFunction = lc.getCurrentFunction();
|
||||
final boolean isProgram = currentFunction.isProgram();
|
||||
final ReturnNode returnNode = new ReturnNode(
|
||||
last == null ? block.getLineNumber() : last.getLineNumber(), //TODO?
|
||||
@ -191,7 +188,7 @@ final class Lower extends NodeOperatorVisitor {
|
||||
final Node expr = executeNode.getExpression();
|
||||
ExecuteNode node = executeNode;
|
||||
|
||||
final FunctionNode currentFunction = getLexicalContext().getCurrentFunction();
|
||||
final FunctionNode currentFunction = lc.getCurrentFunction();
|
||||
|
||||
if (currentFunction.isProgram()) {
|
||||
if (!(expr instanceof Block) || expr instanceof FunctionNode) { // it's not a block, but can be a function
|
||||
@ -216,7 +213,7 @@ final class Lower extends NodeOperatorVisitor {
|
||||
|
||||
final Node test = forNode.getTest();
|
||||
if (!forNode.isForIn() && conservativeAlwaysTrue(test)) {
|
||||
newForNode = forNode.setTest(getLexicalContext(), null);
|
||||
newForNode = forNode.setTest(lc, null);
|
||||
}
|
||||
|
||||
return addStatement(checkEscape(newForNode));
|
||||
@ -230,7 +227,7 @@ final class Lower extends NodeOperatorVisitor {
|
||||
@Override
|
||||
public Node leaveFunctionNode(final FunctionNode functionNode) {
|
||||
LOG.info("END FunctionNode: ", functionNode.getName());
|
||||
return functionNode.setState(getLexicalContext(), CompilationState.LOWERED);
|
||||
return functionNode.setState(lc, CompilationState.LOWERED);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -261,19 +258,25 @@ final class Lower extends NodeOperatorVisitor {
|
||||
return throwNode;
|
||||
}
|
||||
|
||||
private static Node ensureUniqueLabelsIn(final Node node) {
|
||||
return node.accept(new NodeVisitor() {
|
||||
@Override
|
||||
public Node leaveDefault(final Node labelledNode) {
|
||||
return labelledNode.ensureUniqueLabels(getLexicalContext());
|
||||
}
|
||||
private static Node ensureUniqueNamesIn(final LexicalContext lc, final Node node) {
|
||||
return node.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
|
||||
@Override
|
||||
public Node leaveFunctionNode(final FunctionNode functionNode) {
|
||||
final String name = functionNode.getName();
|
||||
return functionNode.setName(lc, lc.getCurrentFunction().uniqueName(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveDefault(final Node labelledNode) {
|
||||
return labelledNode.ensureUniqueLabels(lc);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static List<Statement> copyFinally(final Block finallyBody) {
|
||||
private static List<Statement> copyFinally(final LexicalContext lc, final Block finallyBody) {
|
||||
final List<Statement> newStatements = new ArrayList<>();
|
||||
for (final Statement statement : finallyBody.getStatements()) {
|
||||
newStatements.add((Statement)ensureUniqueLabelsIn(statement));
|
||||
newStatements.add((Statement)ensureUniqueNamesIn(lc, statement));
|
||||
if (statement.hasTerminalFlags()) {
|
||||
return newStatements;
|
||||
}
|
||||
@ -286,12 +289,12 @@ final class Lower extends NodeOperatorVisitor {
|
||||
final long token = tryNode.getToken();
|
||||
final int finish = tryNode.getFinish();
|
||||
|
||||
final IdentNode exception = new IdentNode(token, finish, getLexicalContext().getCurrentFunction().uniqueName("catch_all"));
|
||||
final IdentNode exception = new IdentNode(token, finish, lc.getCurrentFunction().uniqueName("catch_all"));
|
||||
|
||||
final Block catchBody = new Block(lineNumber, token, finish, new ThrowNode(lineNumber, token, finish, new IdentNode(exception))).
|
||||
setIsTerminal(getLexicalContext(), true); //ends with throw, so terminal
|
||||
final Block catchBody = new Block(lineNumber, token, finish, new ThrowNode(lineNumber, token, finish, new IdentNode(exception), ThrowNode.IS_SYNTHETIC_RETHROW)).
|
||||
setIsTerminal(lc, true); //ends with throw, so terminal
|
||||
|
||||
final CatchNode catchAllNode = new CatchNode(lineNumber, token, finish, new IdentNode(exception), null, catchBody);
|
||||
final CatchNode catchAllNode = new CatchNode(lineNumber, token, finish, new IdentNode(exception), null, catchBody, CatchNode.IS_SYNTHETIC_RETHROW);
|
||||
final Block catchAllBlock = new Block(lineNumber, token, finish, catchAllNode);
|
||||
|
||||
//catchallblock -> catchallnode (catchnode) -> exception -> throw
|
||||
@ -300,7 +303,7 @@ final class Lower extends NodeOperatorVisitor {
|
||||
}
|
||||
|
||||
private IdentNode compilerConstant(final CompilerConstants cc) {
|
||||
final FunctionNode functionNode = getLexicalContext().getCurrentFunction();
|
||||
final FunctionNode functionNode = lc.getCurrentFunction();
|
||||
return new IdentNode(functionNode.getToken(), functionNode.getFinish(), cc.symbolName());
|
||||
}
|
||||
|
||||
@ -316,11 +319,10 @@ final class Lower extends NodeOperatorVisitor {
|
||||
* @return new try node after splicing finally code (same if nop)
|
||||
*/
|
||||
private Node spliceFinally(final TryNode tryNode, final List<ThrowNode> rethrows, final Block finallyBody) {
|
||||
final int finish = tryNode.getFinish();
|
||||
|
||||
assert tryNode.getFinallyBody() == null;
|
||||
final int finish = tryNode.getFinish();
|
||||
|
||||
final TryNode newTryNode = (TryNode)tryNode.accept(new NodeVisitor() {
|
||||
final TryNode newTryNode = (TryNode)tryNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
|
||||
final List<Node> insideTry = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
@ -338,7 +340,7 @@ final class Lower extends NodeOperatorVisitor {
|
||||
@Override
|
||||
public Node leaveThrowNode(final ThrowNode throwNode) {
|
||||
if (rethrows.contains(throwNode)) {
|
||||
final List<Statement> newStatements = copyFinally(finallyBody);
|
||||
final List<Statement> newStatements = copyFinally(lc, finallyBody);
|
||||
if (!isTerminal(newStatements)) {
|
||||
newStatements.add(throwNode);
|
||||
}
|
||||
@ -349,12 +351,12 @@ final class Lower extends NodeOperatorVisitor {
|
||||
|
||||
@Override
|
||||
public Node leaveBreakNode(final BreakNode breakNode) {
|
||||
return copy(breakNode, Lower.this.getLexicalContext().getBreakable(breakNode.getLabel()));
|
||||
return copy(breakNode, Lower.this.lc.getBreakable(breakNode.getLabel()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveContinueNode(final ContinueNode continueNode) {
|
||||
return copy(continueNode, Lower.this.getLexicalContext().getContinueTo(continueNode.getLabel()));
|
||||
return copy(continueNode, Lower.this.lc.getContinueTo(continueNode.getLabel()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -372,17 +374,17 @@ final class Lower extends NodeOperatorVisitor {
|
||||
resultNode = null;
|
||||
}
|
||||
|
||||
newStatements.addAll(copyFinally(finallyBody));
|
||||
newStatements.addAll(copyFinally(lc, finallyBody));
|
||||
if (!isTerminal(newStatements)) {
|
||||
newStatements.add(expr == null ? returnNode : returnNode.setExpression(resultNode));
|
||||
}
|
||||
|
||||
return new ExecuteNode(returnNode.getLineNumber(), returnNode.getToken(), returnNode.getFinish(), new Block(returnNode.getLineNumber(), returnNode.getToken(), getLexicalContext().getCurrentBlock().getFinish(), newStatements));
|
||||
return new ExecuteNode(returnNode.getLineNumber(), returnNode.getToken(), returnNode.getFinish(), new Block(returnNode.getLineNumber(), returnNode.getToken(), lc.getCurrentBlock().getFinish(), newStatements));
|
||||
}
|
||||
|
||||
private Node copy(final Statement endpoint, final Node targetNode) {
|
||||
if (!insideTry.contains(targetNode)) {
|
||||
final List<Statement> newStatements = copyFinally(finallyBody);
|
||||
final List<Statement> newStatements = copyFinally(lc, finallyBody);
|
||||
if (!isTerminal(newStatements)) {
|
||||
newStatements.add(endpoint);
|
||||
}
|
||||
@ -436,7 +438,7 @@ final class Lower extends NodeOperatorVisitor {
|
||||
final Block catchAll = catchAllBlock(tryNode);
|
||||
|
||||
final List<ThrowNode> rethrows = new ArrayList<>();
|
||||
catchAll.accept(new NodeVisitor() {
|
||||
catchAll.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
|
||||
@Override
|
||||
public boolean enterThrowNode(final ThrowNode throwNode) {
|
||||
rethrows.add(throwNode);
|
||||
@ -464,7 +466,7 @@ final class Lower extends NodeOperatorVisitor {
|
||||
@Override
|
||||
public Node leaveVarNode(final VarNode varNode) {
|
||||
addStatement(varNode);
|
||||
if (varNode.getFlag(VarNode.IS_LAST_FUNCTION_DECLARATION) && getLexicalContext().getCurrentFunction().isProgram()) {
|
||||
if (varNode.getFlag(VarNode.IS_LAST_FUNCTION_DECLARATION) && lc.getCurrentFunction().isProgram()) {
|
||||
new ExecuteNode(varNode.getLineNumber(), varNode.getToken(), varNode.getFinish(), new IdentNode(varNode.getName())).accept(this);
|
||||
}
|
||||
return varNode;
|
||||
@ -478,7 +480,7 @@ final class Lower extends NodeOperatorVisitor {
|
||||
if (conservativeAlwaysTrue(test)) {
|
||||
//turn it into a for node without a test.
|
||||
final ForNode forNode = (ForNode)new ForNode(whileNode.getLineNumber(), whileNode.getToken(), whileNode.getFinish(), null, null, body, null, ForNode.IS_FOR).accept(this);
|
||||
getLexicalContext().replace(whileNode, forNode);
|
||||
lc.replace(whileNode, forNode);
|
||||
return forNode;
|
||||
}
|
||||
|
||||
@ -513,7 +515,7 @@ final class Lower extends NodeOperatorVisitor {
|
||||
* @return eval location
|
||||
*/
|
||||
private String evalLocation(final IdentNode node) {
|
||||
final Source source = getLexicalContext().getCurrentFunction().getSource();
|
||||
final Source source = lc.getCurrentFunction().getSource();
|
||||
return new StringBuilder().
|
||||
append(source.getName()).
|
||||
append('#').
|
||||
@ -545,10 +547,10 @@ final class Lower extends NodeOperatorVisitor {
|
||||
|
||||
// 'eval' call with at least one argument
|
||||
if (args.size() >= 1 && EVAL.symbolName().equals(callee.getName())) {
|
||||
final FunctionNode currentFunction = getLexicalContext().getCurrentFunction();
|
||||
final FunctionNode currentFunction = lc.getCurrentFunction();
|
||||
return callNode.setEvalArgs(
|
||||
new CallNode.EvalArgs(
|
||||
ensureUniqueLabelsIn(args.get(0)).accept(this),
|
||||
ensureUniqueNamesIn(lc, args.get(0)).accept(this),
|
||||
compilerConstant(THIS),
|
||||
evalLocation(callee),
|
||||
currentFunction.isStrict()));
|
||||
@ -574,7 +576,7 @@ final class Lower extends NodeOperatorVisitor {
|
||||
private static boolean controlFlowEscapes(final LexicalContext lex, final Block loopBody) {
|
||||
final List<Node> escapes = new ArrayList<>();
|
||||
|
||||
loopBody.accept(new NodeVisitor() {
|
||||
loopBody.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
|
||||
@Override
|
||||
public Node leaveBreakNode(final BreakNode node) {
|
||||
escapes.add(node);
|
||||
@ -595,7 +597,6 @@ final class Lower extends NodeOperatorVisitor {
|
||||
}
|
||||
|
||||
private LoopNode checkEscape(final LoopNode loopNode) {
|
||||
final LexicalContext lc = getLexicalContext();
|
||||
final boolean escapes = controlFlowEscapes(lc, loopNode.getBody());
|
||||
if (escapes) {
|
||||
return loopNode.
|
||||
@ -607,7 +608,7 @@ final class Lower extends NodeOperatorVisitor {
|
||||
|
||||
|
||||
private Node addStatement(final Statement statement) {
|
||||
((BlockLexicalContext)getLexicalContext()).appendStatement(statement);
|
||||
lc.appendStatement(statement);
|
||||
return statement;
|
||||
}
|
||||
|
||||
|
@ -2081,7 +2081,9 @@ public class MethodEmitter implements Emitter {
|
||||
* @param args debug information to print
|
||||
*/
|
||||
private void debug(final Object... args) {
|
||||
debug(30, args);
|
||||
if (DEBUG) {
|
||||
debug(30, args);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2091,7 +2093,9 @@ public class MethodEmitter implements Emitter {
|
||||
* @param args debug information to print
|
||||
*/
|
||||
private void debug_label(final Object... args) {
|
||||
debug(26, args);
|
||||
if (DEBUG) {
|
||||
debug(22, args);
|
||||
}
|
||||
}
|
||||
|
||||
private void debug(final int padConstant, final Object... args) {
|
||||
@ -2164,7 +2168,6 @@ public class MethodEmitter implements Emitter {
|
||||
new Throwable().printStackTrace(LOG.getOutputStream());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
476
nashorn/src/jdk/nashorn/internal/codegen/RangeAnalyzer.java
Normal file
476
nashorn/src/jdk/nashorn/internal/codegen/RangeAnalyzer.java
Normal file
@ -0,0 +1,476 @@
|
||||
/*
|
||||
* 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.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
|
||||
import jdk.nashorn.internal.codegen.types.Range;
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.Assignment;
|
||||
import jdk.nashorn.internal.ir.BinaryNode;
|
||||
import jdk.nashorn.internal.ir.ForNode;
|
||||
import jdk.nashorn.internal.ir.IdentNode;
|
||||
import jdk.nashorn.internal.ir.LexicalContext;
|
||||
import jdk.nashorn.internal.ir.LiteralNode;
|
||||
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
|
||||
import jdk.nashorn.internal.ir.LoopNode;
|
||||
import jdk.nashorn.internal.ir.Node;
|
||||
import jdk.nashorn.internal.ir.RuntimeNode;
|
||||
import jdk.nashorn.internal.ir.Symbol;
|
||||
import jdk.nashorn.internal.ir.UnaryNode;
|
||||
import jdk.nashorn.internal.ir.VarNode;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.parser.TokenType;
|
||||
import jdk.nashorn.internal.runtime.DebugLogger;
|
||||
|
||||
/**
|
||||
* Range analysis and narrowing of type where it can be proven
|
||||
* that there is no spillover, e.g.
|
||||
*
|
||||
* function func(c) {
|
||||
* var v = c & 0xfff;
|
||||
* var w = c & 0xeee;
|
||||
* var x = v * w;
|
||||
* return x;
|
||||
* }
|
||||
*
|
||||
* Proves that the multiplication never exceeds 24 bits and can thus be an int
|
||||
*/
|
||||
final class RangeAnalyzer extends NodeOperatorVisitor<LexicalContext> {
|
||||
static final DebugLogger LOG = new DebugLogger("ranges");
|
||||
|
||||
private static final Range.Functionality RANGE = new Range.Functionality(LOG);
|
||||
|
||||
private final Map<LoopNode, Symbol> loopCounters = new HashMap<>();
|
||||
|
||||
RangeAnalyzer() {
|
||||
super(new LexicalContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enterForNode(final ForNode forNode) {
|
||||
//conservatively attempt to identify the loop counter. Null means that it wasn't
|
||||
//properly identified and that no optimizations can be made with it - its range is
|
||||
//simply unknown in that case, if it is assigned in the loop
|
||||
final Symbol counter = findLoopCounter(forNode);
|
||||
LOG.fine("Entering forNode " + forNode + " counter = " + counter);
|
||||
if (counter != null && !assignedInLoop(forNode, counter)) {
|
||||
loopCounters.put(forNode, counter);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//destination visited
|
||||
private Symbol setRange(final Node dest, final Range range) {
|
||||
if (range.isUnknown()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Symbol symbol = dest.getSymbol();
|
||||
assert symbol != null : dest + " " + dest.getClass() + " has no symbol";
|
||||
assert symbol.getRange() != null : symbol + " has no range";
|
||||
final Range symRange = RANGE.join(symbol.getRange(), range);
|
||||
|
||||
//anything assigned in the loop, not being the safe loop counter(s) invalidates its entire range
|
||||
if (lc.inLoop() && !isLoopCounter(lc.getCurrentLoop(), symbol)) {
|
||||
symbol.setRange(Range.createGenericRange());
|
||||
return symbol;
|
||||
}
|
||||
|
||||
if (!symRange.equals(symbol.getRange())) {
|
||||
LOG.fine("Modify range for " + dest + " " + symbol + " from " + symbol.getRange() + " to " + symRange + " (in node = " + dest + ")" );
|
||||
symbol.setRange(symRange);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveADD(final BinaryNode node) {
|
||||
setRange(node, RANGE.add(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveSUB(final BinaryNode node) {
|
||||
setRange(node, RANGE.sub(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveMUL(final BinaryNode node) {
|
||||
setRange(node, RANGE.mul(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveDIV(final BinaryNode node) {
|
||||
setRange(node, RANGE.div(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveMOD(final BinaryNode node) {
|
||||
setRange(node, RANGE.mod(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveBIT_AND(final BinaryNode node) {
|
||||
setRange(node, RANGE.and(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveBIT_OR(final BinaryNode node) {
|
||||
setRange(node, RANGE.or(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveBIT_XOR(final BinaryNode node) {
|
||||
setRange(node, RANGE.xor(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveSAR(final BinaryNode node) {
|
||||
setRange(node, RANGE.sar(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveSHL(final BinaryNode node) {
|
||||
setRange(node, RANGE.shl(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveSHR(final BinaryNode node) {
|
||||
setRange(node, RANGE.shr(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
|
||||
return node;
|
||||
}
|
||||
|
||||
private Node leaveCmp(final BinaryNode node) {
|
||||
setRange(node, Range.createTypeRange(Type.BOOLEAN));
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveEQ(final BinaryNode node) {
|
||||
return leaveCmp(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveEQ_STRICT(final BinaryNode node) {
|
||||
return leaveCmp(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveNE(final BinaryNode node) {
|
||||
return leaveCmp(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveNE_STRICT(final BinaryNode node) {
|
||||
return leaveCmp(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveLT(final BinaryNode node) {
|
||||
return leaveCmp(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveLE(final BinaryNode node) {
|
||||
return leaveCmp(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveGT(final BinaryNode node) {
|
||||
return leaveCmp(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveGE(final BinaryNode node) {
|
||||
return leaveCmp(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveASSIGN(final BinaryNode node) {
|
||||
Range range = node.rhs().getSymbol().getRange();
|
||||
if (range.isUnknown()) {
|
||||
range = Range.createGenericRange();
|
||||
}
|
||||
|
||||
setRange(node.lhs(), range);
|
||||
setRange(node, range);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
private Node leaveSelfModifyingAssign(final BinaryNode node, final Range range) {
|
||||
setRange(node.lhs(), range);
|
||||
setRange(node, range);
|
||||
return node;
|
||||
}
|
||||
|
||||
private Node leaveSelfModifyingAssign(final UnaryNode node, final Range range) {
|
||||
setRange(node.rhs(), range);
|
||||
setRange(node, range);
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveASSIGN_ADD(final BinaryNode node) {
|
||||
return leaveSelfModifyingAssign(node, RANGE.add(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveASSIGN_SUB(final BinaryNode node) {
|
||||
return leaveSelfModifyingAssign(node, RANGE.sub(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveASSIGN_MUL(final BinaryNode node) {
|
||||
return leaveSelfModifyingAssign(node, RANGE.mul(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveASSIGN_DIV(final BinaryNode node) {
|
||||
return leaveSelfModifyingAssign(node, Range.createTypeRange(Type.NUMBER));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveASSIGN_MOD(final BinaryNode node) {
|
||||
return leaveSelfModifyingAssign(node, Range.createTypeRange(Type.NUMBER));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveASSIGN_BIT_AND(final BinaryNode node) {
|
||||
return leaveSelfModifyingAssign(node, RANGE.and(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveASSIGN_BIT_OR(final BinaryNode node) {
|
||||
return leaveSelfModifyingAssign(node, RANGE.or(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveASSIGN_BIT_XOR(final BinaryNode node) {
|
||||
return leaveSelfModifyingAssign(node, RANGE.xor(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveASSIGN_SAR(final BinaryNode node) {
|
||||
return leaveSelfModifyingAssign(node, RANGE.sar(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveASSIGN_SHR(final BinaryNode node) {
|
||||
return leaveSelfModifyingAssign(node, RANGE.shr(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveASSIGN_SHL(final BinaryNode node) {
|
||||
return leaveSelfModifyingAssign(node, RANGE.shl(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveDECINC(final UnaryNode node) {
|
||||
switch (node.tokenType()) {
|
||||
case DECPREFIX:
|
||||
case DECPOSTFIX:
|
||||
return leaveSelfModifyingAssign(node, RANGE.sub(node.rhs().getSymbol().getRange(), Range.createRange(1)));
|
||||
case INCPREFIX:
|
||||
case INCPOSTFIX:
|
||||
return leaveSelfModifyingAssign(node, RANGE.add(node.rhs().getSymbol().getRange(), Range.createRange(1)));
|
||||
default:
|
||||
assert false;
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveADD(final UnaryNode node) {
|
||||
Range range = node.rhs().getSymbol().getRange();
|
||||
if (!range.getType().isNumeric()) {
|
||||
range = Range.createTypeRange(Type.NUMBER);
|
||||
}
|
||||
setRange(node, range);
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveBIT_NOT(final UnaryNode node) {
|
||||
setRange(node, Range.createTypeRange(Type.INT));
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveNOT(final UnaryNode node) {
|
||||
setRange(node, Range.createTypeRange(Type.BOOLEAN));
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveSUB(final UnaryNode node) {
|
||||
setRange(node, RANGE.neg(node.rhs().getSymbol().getRange()));
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveVarNode(final VarNode node) {
|
||||
if (node.isAssignment()) {
|
||||
Range range = node.getInit().getSymbol().getRange();
|
||||
range = range.isUnknown() ? Range.createGenericRange() : range;
|
||||
|
||||
setRange(node.getName(), range);
|
||||
setRange(node, range);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public boolean enterLiteralNode(final LiteralNode node) {
|
||||
// ignore array literals
|
||||
return !(node instanceof ArrayLiteralNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveLiteralNode(@SuppressWarnings("rawtypes") final LiteralNode node) {
|
||||
if (node.getType().isInteger()) {
|
||||
setRange(node, Range.createRange(node.getInt32()));
|
||||
} else if (node.getType().isNumber()) {
|
||||
setRange(node, Range.createRange(node.getNumber()));
|
||||
} else if (node.getType().isLong()) {
|
||||
setRange(node, Range.createRange(node.getLong()));
|
||||
} else if (node.getType().isBoolean()) {
|
||||
setRange(node, Range.createTypeRange(Type.BOOLEAN));
|
||||
} else {
|
||||
setRange(node, Range.createGenericRange());
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enterRuntimeNode(final RuntimeNode node) {
|
||||
// a runtime node that cannot be specialized is no point entering
|
||||
return node.getRequest().canSpecialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a symbol is unsafely assigned in a loop - i.e. repeteadly assigned and
|
||||
* not being identified as the loop counter. That means we don't really know anything
|
||||
* about its range.
|
||||
* @param loopNode loop node
|
||||
* @param symbol symbol
|
||||
* @return true if assigned in loop
|
||||
*/
|
||||
// TODO - this currently checks for nodes only - needs to be augmented for while nodes
|
||||
// assignment analysis is also very conservative
|
||||
private static boolean assignedInLoop(final LoopNode loopNode, final Symbol symbol) {
|
||||
final HashSet<Node> skip = new HashSet<>();
|
||||
final HashSet<Node> assignmentsInLoop = new HashSet<>();
|
||||
|
||||
loopNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
|
||||
private boolean assigns(final Node node, final Symbol s) {
|
||||
return node.isAssignment() && ((Assignment<?>)node).getAssignmentDest().getSymbol() == s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enterForNode(final ForNode forNode) {
|
||||
if (forNode.getInit() != null) {
|
||||
skip.add(forNode.getInit());
|
||||
}
|
||||
if (forNode.getModify() != null) {
|
||||
skip.add(forNode.getModify());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveDefault(final Node node) {
|
||||
//if this is an assignment to symbol
|
||||
if (!skip.contains(node) && assigns(node, symbol)) {
|
||||
assignmentsInLoop.add(node);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
});
|
||||
|
||||
return !assignmentsInLoop.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for a loop counter. This is currently quite conservative, in that it only handles
|
||||
* x <= counter and x < counter.
|
||||
*
|
||||
* @param node loop node to check
|
||||
* @return
|
||||
*/
|
||||
private static Symbol findLoopCounter(final LoopNode node) {
|
||||
final Node test = node.getTest();
|
||||
|
||||
if (test != null && test.isComparison()) {
|
||||
final BinaryNode binaryNode = (BinaryNode)test;
|
||||
final Node lhs = binaryNode.lhs();
|
||||
final Node rhs = binaryNode.rhs();
|
||||
|
||||
//detect ident cmp int_literal
|
||||
if (lhs instanceof IdentNode && rhs instanceof LiteralNode && ((LiteralNode<?>)rhs).getType().isInteger()) {
|
||||
final Symbol symbol = lhs.getSymbol();
|
||||
final int margin = ((LiteralNode<?>)rhs).getInt32();
|
||||
final TokenType op = test.tokenType();
|
||||
|
||||
switch (op) {
|
||||
case LT:
|
||||
case LE:
|
||||
symbol.setRange(RANGE.join(symbol.getRange(), Range.createRange(op == TokenType.LT ? margin - 1 : margin)));
|
||||
return symbol;
|
||||
case GT:
|
||||
case GE:
|
||||
//setRange(lhs, Range.createRange(op == TokenType.GT ? margin + 1 : margin));
|
||||
//return symbol;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isLoopCounter(final LoopNode loopNode, final Symbol symbol) {
|
||||
//this only works if loop nodes aren't replaced by other ones during this transform, but they are not
|
||||
return loopCounters.get(loopNode) == symbol;
|
||||
}
|
||||
}
|
@ -116,9 +116,10 @@ class SharedScopeCall {
|
||||
/**
|
||||
* Generate the invoke instruction for this shared scope call.
|
||||
* @param method the method emitter
|
||||
* @return the method emitter
|
||||
*/
|
||||
public void generateInvoke(final MethodEmitter method) {
|
||||
method.invokestatic(compileUnit.getUnitClassName(), methodName, getStaticSignature());
|
||||
public MethodEmitter generateInvoke(final MethodEmitter method) {
|
||||
return method.invokestatic(compileUnit.getUnitClassName(), methodName, getStaticSignature());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -49,12 +49,12 @@ import jdk.nashorn.internal.runtime.options.Options;
|
||||
/**
|
||||
* Split the IR into smaller compile units.
|
||||
*/
|
||||
final class Splitter extends NodeVisitor {
|
||||
final class Splitter extends NodeVisitor<LexicalContext> {
|
||||
/** Current compiler. */
|
||||
private final Compiler compiler;
|
||||
|
||||
/** IR to be broken down. */
|
||||
private FunctionNode outermost;
|
||||
private final FunctionNode outermost;
|
||||
|
||||
/** Compile unit for the main script. */
|
||||
private final CompileUnit outermostCompileUnit;
|
||||
@ -75,6 +75,7 @@ final class Splitter extends NodeVisitor {
|
||||
* @param outermostCompileUnit compile unit for outermost function, if non-lazy this is the script's compile unit
|
||||
*/
|
||||
public Splitter(final Compiler compiler, final FunctionNode functionNode, final CompileUnit outermostCompileUnit) {
|
||||
super(new LexicalContext());
|
||||
this.compiler = compiler;
|
||||
this.outermost = functionNode;
|
||||
this.outermostCompileUnit = outermostCompileUnit;
|
||||
@ -93,8 +94,6 @@ final class Splitter extends NodeVisitor {
|
||||
|
||||
LOG.finest("Initiating split of '", functionNode.getName(), "'");
|
||||
|
||||
final LexicalContext lc = getLexicalContext();
|
||||
|
||||
long weight = WeighNodes.weigh(functionNode);
|
||||
final boolean top = fn.isProgram(); //compiler.getFunctionNode() == outermost;
|
||||
|
||||
@ -127,7 +126,7 @@ final class Splitter extends NodeVisitor {
|
||||
final Block body = functionNode.getBody();
|
||||
final List<FunctionNode> dc = directChildren(functionNode);
|
||||
|
||||
final Block newBody = (Block)body.accept(new NodeVisitor() {
|
||||
final Block newBody = (Block)body.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
|
||||
@Override
|
||||
public boolean enterFunctionNode(final FunctionNode nestedFunction) {
|
||||
return dc.contains(nestedFunction);
|
||||
@ -136,7 +135,7 @@ final class Splitter extends NodeVisitor {
|
||||
@Override
|
||||
public Node leaveFunctionNode(final FunctionNode nestedFunction) {
|
||||
FunctionNode split = new Splitter(compiler, nestedFunction, outermostCompileUnit).split(nestedFunction);
|
||||
getLexicalContext().replace(nestedFunction, split);
|
||||
lc.replace(nestedFunction, split);
|
||||
return split;
|
||||
}
|
||||
});
|
||||
@ -149,13 +148,13 @@ final class Splitter extends NodeVisitor {
|
||||
|
||||
private static List<FunctionNode> directChildren(final FunctionNode functionNode) {
|
||||
final List<FunctionNode> dc = new ArrayList<>();
|
||||
functionNode.accept(new NodeVisitor() {
|
||||
functionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
|
||||
@Override
|
||||
public boolean enterFunctionNode(final FunctionNode child) {
|
||||
if (child == functionNode) {
|
||||
return true;
|
||||
}
|
||||
if (getLexicalContext().getParentFunction(child) == functionNode) {
|
||||
if (lc.getParentFunction(child) == functionNode) {
|
||||
dc.add(child);
|
||||
}
|
||||
return false;
|
||||
@ -181,7 +180,7 @@ final class Splitter extends NodeVisitor {
|
||||
* @return new weight for the resulting block.
|
||||
*/
|
||||
private Block splitBlock(final Block block, final FunctionNode function) {
|
||||
getLexicalContext().setFlag(getLexicalContext().getCurrentFunction(), FunctionNode.IS_SPLIT);
|
||||
lc.setFlag(lc.getCurrentFunction(), FunctionNode.IS_SPLIT);
|
||||
|
||||
final List<Statement> splits = new ArrayList<>();
|
||||
List<Statement> statements = new ArrayList<>();
|
||||
@ -210,7 +209,7 @@ final class Splitter extends NodeVisitor {
|
||||
splits.add(createBlockSplitNode(block, function, statements, statementsWeight));
|
||||
}
|
||||
|
||||
return block.setStatements(getLexicalContext(), splits);
|
||||
return block.setStatements(lc, splits);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -258,7 +257,7 @@ final class Splitter extends NodeVisitor {
|
||||
// been split already, so weigh again before splitting.
|
||||
long weight = WeighNodes.weigh(block, weightCache);
|
||||
if (weight >= SPLIT_THRESHOLD) {
|
||||
newBlock = splitBlock(block, getLexicalContext().getFunction(block));
|
||||
newBlock = splitBlock(block, lc.getFunction(block));
|
||||
weight = WeighNodes.weigh(newBlock, weightCache);
|
||||
}
|
||||
weightCache.put(newBlock, weight);
|
||||
@ -274,9 +273,9 @@ final class Splitter extends NodeVisitor {
|
||||
return literal;
|
||||
}
|
||||
|
||||
final FunctionNode functionNode = getLexicalContext().getCurrentFunction();
|
||||
final FunctionNode functionNode = lc.getCurrentFunction();
|
||||
|
||||
getLexicalContext().setFlag(functionNode, FunctionNode.IS_SPLIT);
|
||||
lc.setFlag(functionNode, FunctionNode.IS_SPLIT);
|
||||
|
||||
if (literal instanceof ArrayLiteralNode) {
|
||||
final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode) literal;
|
||||
|
@ -27,6 +27,7 @@ package jdk.nashorn.internal.codegen;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.AccessNode;
|
||||
import jdk.nashorn.internal.ir.BinaryNode;
|
||||
@ -41,6 +42,7 @@ import jdk.nashorn.internal.ir.FunctionNode;
|
||||
import jdk.nashorn.internal.ir.IdentNode;
|
||||
import jdk.nashorn.internal.ir.IfNode;
|
||||
import jdk.nashorn.internal.ir.IndexNode;
|
||||
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;
|
||||
@ -63,7 +65,7 @@ import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
|
||||
* Computes the "byte code" weight of an AST segment. This is used
|
||||
* for Splitting too large class files
|
||||
*/
|
||||
final class WeighNodes extends NodeOperatorVisitor {
|
||||
final class WeighNodes extends NodeOperatorVisitor<LexicalContext> {
|
||||
/*
|
||||
* Weight constants.
|
||||
*/
|
||||
@ -100,7 +102,7 @@ final class WeighNodes extends NodeOperatorVisitor {
|
||||
* @param weightCache cache of already calculated block weights
|
||||
*/
|
||||
private WeighNodes(FunctionNode topFunction, final Map<Node, Long> weightCache) {
|
||||
super();
|
||||
super(new LexicalContext());
|
||||
this.topFunction = topFunction;
|
||||
this.weightCache = weightCache;
|
||||
}
|
||||
|
705
nashorn/src/jdk/nashorn/internal/codegen/types/Range.java
Normal file
705
nashorn/src/jdk/nashorn/internal/codegen/types/Range.java
Normal file
@ -0,0 +1,705 @@
|
||||
/*
|
||||
* 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.types;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.nashorn.internal.runtime.DebugLogger;
|
||||
import jdk.nashorn.internal.runtime.JSType;
|
||||
|
||||
/**
|
||||
* Represents the value range of a symbol.
|
||||
*/
|
||||
public abstract class Range {
|
||||
|
||||
private static final Range GENERIC_RANGE = new Range() {
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.OBJECT;
|
||||
}
|
||||
};
|
||||
|
||||
private static final Range NUMBER_RANGE = new Range() {
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.NUMBER;
|
||||
}
|
||||
};
|
||||
|
||||
private static final Range UNKNOWN_RANGE = new Range() {
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.UNKNOWN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUnknown() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
private static class IntegerRange extends Range {
|
||||
private final long min;
|
||||
private final long max;
|
||||
private final Type type;
|
||||
|
||||
private IntegerRange(final long min, final long max) {
|
||||
assert min <= max;
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
this.type = typeFromRange(min, max);
|
||||
}
|
||||
|
||||
private static Type typeFromRange(final long from, final long to) {
|
||||
if (from >= Integer.MIN_VALUE && to <= Integer.MAX_VALUE) {
|
||||
return Type.INT;
|
||||
}
|
||||
return Type.LONG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public long getMin() {
|
||||
return min;
|
||||
}
|
||||
|
||||
public long getMax() {
|
||||
return max;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIntegerConst() {
|
||||
return getMin() == getMax();
|
||||
}
|
||||
|
||||
private long getBitMask() {
|
||||
if (min == max) {
|
||||
return min;
|
||||
}
|
||||
|
||||
if (min < 0) {
|
||||
return ~0L;
|
||||
}
|
||||
|
||||
long mask = 1;
|
||||
while (mask < max) {
|
||||
mask = (mask << 1) | 1;
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (obj instanceof IntegerRange) {
|
||||
final IntegerRange other = (IntegerRange)obj;
|
||||
return this.type == other.type && this.min == other.min && this.max == other.max;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Long.hashCode(min) ^ Long.hashCode(max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + "[" + min +", " + max + "]";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get narrowest type for this range
|
||||
* @return type
|
||||
*/
|
||||
public abstract Type getType();
|
||||
|
||||
/**
|
||||
* Is this range unknown
|
||||
* @return true if unknown
|
||||
*/
|
||||
public boolean isUnknown() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an integer is enough to span this range
|
||||
* @return true if integer is enough
|
||||
*/
|
||||
public boolean isIntegerType() {
|
||||
return this instanceof IntegerRange;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an integer is enough to span this range
|
||||
* @return true if integer is enough
|
||||
*/
|
||||
public boolean isIntegerConst() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an unknown range - this is most likely a singleton object
|
||||
* and it represents "we have no known range information"
|
||||
* @return the range
|
||||
*/
|
||||
public static Range createUnknownRange() {
|
||||
return UNKNOWN_RANGE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a constant range: [value, value]
|
||||
* @param value value
|
||||
* @return the range
|
||||
*/
|
||||
public static Range createRange(final int value) {
|
||||
return createIntegerRange(value, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a constant range: [value, value]
|
||||
* @param value value
|
||||
* @return the range
|
||||
*/
|
||||
public static Range createRange(final long value) {
|
||||
return createIntegerRange(value, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a constant range: [value, value]
|
||||
* @param value value
|
||||
* @return the range
|
||||
*/
|
||||
public static Range createRange(final double value) {
|
||||
if (isRepresentableAsLong(value)) {
|
||||
return createIntegerRange((long) value, (long) value);
|
||||
}
|
||||
return createNumberRange();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a constant range: [value, value]
|
||||
* @param value value
|
||||
* @return the range
|
||||
*/
|
||||
public static Range createRange(final Object value) {
|
||||
if (value instanceof Integer) {
|
||||
return createRange((int)value);
|
||||
} else if (value instanceof Long) {
|
||||
return createRange((long)value);
|
||||
} else if (value instanceof Double) {
|
||||
return createRange((double)value);
|
||||
}
|
||||
|
||||
return createGenericRange();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a generic range - object symbol that carries no range
|
||||
* information
|
||||
* @return the range
|
||||
*/
|
||||
public static Range createGenericRange() {
|
||||
return GENERIC_RANGE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a number range - number symbol that carries no range
|
||||
* information
|
||||
* @return the range
|
||||
*/
|
||||
public static Range createNumberRange() {
|
||||
return NUMBER_RANGE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an integer range [min, max]
|
||||
* @param min minimum value, inclusive
|
||||
* @param max maximum value, inclusive
|
||||
* @return the range
|
||||
*/
|
||||
public static IntegerRange createIntegerRange(final long min, final long max) {
|
||||
return new IntegerRange(min, max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an integer range of maximum type width for the given type
|
||||
* @param type the type
|
||||
* @return the range
|
||||
*/
|
||||
public static IntegerRange createIntegerRange(final Type type) {
|
||||
assert type.isNumeric() && !type.isNumber();
|
||||
final long min;
|
||||
final long max;
|
||||
if (type.isInteger()) {
|
||||
min = Integer.MIN_VALUE;
|
||||
max = Integer.MAX_VALUE;
|
||||
} else if (type.isLong()) {
|
||||
min = Long.MIN_VALUE;
|
||||
max = Long.MAX_VALUE;
|
||||
} else {
|
||||
throw new AssertionError(); //type incompatible with integer range
|
||||
}
|
||||
return new IntegerRange(min, max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an range of maximum type width for the given type
|
||||
* @param type the type
|
||||
* @return the range
|
||||
*/
|
||||
public static Range createTypeRange(final Type type) {
|
||||
if (type.isNumber()) {
|
||||
return createNumberRange();
|
||||
} else if (type.isNumeric()) {
|
||||
return createIntegerRange(type);
|
||||
} else {
|
||||
return createGenericRange();
|
||||
}
|
||||
}
|
||||
|
||||
// check that add doesn't overflow
|
||||
private static boolean checkAdd(final long a, final long b) {
|
||||
final long result = a + b;
|
||||
return ((a ^ result) & (b ^ result)) >= 0;
|
||||
}
|
||||
|
||||
// check that sub doesn't overflow
|
||||
private static boolean checkSub(final long a, final long b) {
|
||||
final long result = a - b;
|
||||
return ((a ^ result) & (b ^ result)) >= 0;
|
||||
}
|
||||
|
||||
private static boolean checkMul(final long a, final long b) {
|
||||
// TODO correct overflow check
|
||||
return a >= Integer.MIN_VALUE && a <= Integer.MAX_VALUE && b >= Integer.MIN_VALUE && b <= Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* The range functionality class responsible for merging ranges and drawing
|
||||
* range conclusions from operations executed
|
||||
*/
|
||||
public static class Functionality {
|
||||
/** logger */
|
||||
protected final DebugLogger log;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param log logger
|
||||
*/
|
||||
public Functionality(final DebugLogger log) {
|
||||
this.log = log;
|
||||
}
|
||||
|
||||
/**
|
||||
* Join two ranges
|
||||
* @param a first range
|
||||
* @param b second range
|
||||
* @return the joined range
|
||||
*/
|
||||
public Range join(final Range a, final Range b) {
|
||||
if (a.equals(b)) {
|
||||
return a;
|
||||
}
|
||||
|
||||
Type joinedType = a.getType();
|
||||
if (a.getType() != b.getType()) {
|
||||
if (a.isUnknown()) {
|
||||
return b;
|
||||
}
|
||||
if (b.isUnknown()) {
|
||||
return a;
|
||||
}
|
||||
|
||||
joinedType = Type.widest(a.getType(), b.getType());
|
||||
}
|
||||
|
||||
if (joinedType.isInteger() || joinedType.isLong()) {
|
||||
return createIntegerRange(
|
||||
Math.min(((IntegerRange) a).getMin(), ((IntegerRange) b).getMin()),
|
||||
Math.max(((IntegerRange) a).getMax(), ((IntegerRange) b).getMax()));
|
||||
}
|
||||
|
||||
return createTypeRange(joinedType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add operation
|
||||
* @param a range of first symbol to be added
|
||||
* @param b range of second symbol to be added
|
||||
* @return resulting range representing the value range after add
|
||||
*/
|
||||
public Range add(final Range a, final Range b) {
|
||||
if (a.isIntegerType() && b.isIntegerType()) {
|
||||
final IntegerRange lhs = (IntegerRange)a;
|
||||
final IntegerRange rhs = (IntegerRange)b;
|
||||
if (checkAdd(lhs.getMin(), rhs.getMin()) && checkAdd(lhs.getMax(), rhs.getMax())) {
|
||||
return createIntegerRange(lhs.getMin() + rhs.getMin(), lhs.getMax() + rhs.getMax());
|
||||
}
|
||||
}
|
||||
|
||||
if (a.getType().isNumeric() && b.getType().isNumeric()) {
|
||||
return createNumberRange();
|
||||
}
|
||||
|
||||
return createGenericRange();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sub operation
|
||||
* @param a range of first symbol to be subtracted
|
||||
* @param b range of second symbol to be subtracted
|
||||
* @return resulting range representing the value range after subtraction
|
||||
*/
|
||||
public Range sub(final Range a, final Range b) {
|
||||
if (a.isIntegerType() && b.isIntegerType()) {
|
||||
final IntegerRange lhs = (IntegerRange)a;
|
||||
final IntegerRange rhs = (IntegerRange)b;
|
||||
if (checkSub(lhs.getMin(), rhs.getMax()) && checkSub(lhs.getMax(), rhs.getMin())) {
|
||||
return createIntegerRange(lhs.getMin() - rhs.getMax(), lhs.getMax() - rhs.getMin());
|
||||
}
|
||||
}
|
||||
|
||||
if (a.getType().isNumeric() && b.getType().isNumeric()) {
|
||||
return createNumberRange();
|
||||
}
|
||||
|
||||
return createGenericRange();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mul operation
|
||||
* @param a range of first symbol to be multiplied
|
||||
* @param b range of second symbol to be multiplied
|
||||
* @return resulting range representing the value range after multiplication
|
||||
*/
|
||||
public Range mul(final Range a, final Range b) {
|
||||
if (a.isIntegerType() && b.isIntegerType()) {
|
||||
final IntegerRange lhs = (IntegerRange)a;
|
||||
final IntegerRange rhs = (IntegerRange)b;
|
||||
|
||||
//ensure that nothing ever overflows or underflows
|
||||
if (checkMul(lhs.getMin(), rhs.getMin()) &&
|
||||
checkMul(lhs.getMax(), rhs.getMax()) &&
|
||||
checkMul(lhs.getMin(), rhs.getMax()) &&
|
||||
checkMul(lhs.getMax(), rhs.getMin())) {
|
||||
|
||||
final List<Long> results =
|
||||
Arrays.asList(
|
||||
lhs.getMin() * rhs.getMin(),
|
||||
lhs.getMin() * rhs.getMax(),
|
||||
lhs.getMax() * rhs.getMin(),
|
||||
lhs.getMax() * rhs.getMax());
|
||||
return createIntegerRange(Collections.min(results), Collections.max(results));
|
||||
}
|
||||
}
|
||||
|
||||
if (a.getType().isNumeric() && b.getType().isNumeric()) {
|
||||
return createNumberRange();
|
||||
}
|
||||
|
||||
return createGenericRange();
|
||||
}
|
||||
|
||||
/**
|
||||
* Neg operation
|
||||
* @param a range of value symbol to be negated
|
||||
* @return resulting range representing the value range after neg
|
||||
*/
|
||||
public Range neg(final Range a) {
|
||||
if (a.isIntegerType()) {
|
||||
final IntegerRange rhs = (IntegerRange)a;
|
||||
if (rhs.getMin() != Long.MIN_VALUE && rhs.getMax() != Long.MIN_VALUE) {
|
||||
return createIntegerRange(-rhs.getMax(), -rhs.getMin());
|
||||
}
|
||||
}
|
||||
|
||||
if (a.getType().isNumeric()) {
|
||||
return createNumberRange();
|
||||
}
|
||||
|
||||
return createGenericRange();
|
||||
}
|
||||
|
||||
/**
|
||||
* Bitwise and operation
|
||||
* @param a range of first symbol to be and:ed
|
||||
* @param b range of second symbol to be and:ed
|
||||
* @return resulting range representing the value range after and
|
||||
*/
|
||||
public Range and(final Range a, final Range b) {
|
||||
if (a.isIntegerType() && b.isIntegerType()) {
|
||||
final int resultMask = (int) (((IntegerRange)a).getBitMask() & ((IntegerRange)b).getBitMask());
|
||||
if (resultMask >= 0) {
|
||||
return createIntegerRange(0, resultMask);
|
||||
}
|
||||
} else if (a.isUnknown() && b.isIntegerType()) {
|
||||
final long operandMask = ((IntegerRange)b).getBitMask();
|
||||
if (operandMask >= 0) {
|
||||
return createIntegerRange(0, operandMask);
|
||||
}
|
||||
} else if (a.isIntegerType() && b.isUnknown()) {
|
||||
final long operandMask = ((IntegerRange)a).getBitMask();
|
||||
if (operandMask >= 0) {
|
||||
return createIntegerRange(0, operandMask);
|
||||
}
|
||||
}
|
||||
|
||||
return createTypeRange(Type.INT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bitwise or operation
|
||||
* @param a range of first symbol to be or:ed
|
||||
* @param b range of second symbol to be or:ed
|
||||
* @return resulting range representing the value range after or
|
||||
*/
|
||||
public Range or(final Range a, final Range b) {
|
||||
if (a.isIntegerType() && b.isIntegerType()) {
|
||||
final int resultMask = (int)(((IntegerRange)a).getBitMask() | ((IntegerRange)b).getBitMask());
|
||||
if (resultMask >= 0) {
|
||||
return createIntegerRange(0, resultMask);
|
||||
}
|
||||
}
|
||||
|
||||
return createTypeRange(Type.INT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bitwise xor operation
|
||||
* @param a range of first symbol to be xor:ed
|
||||
* @param b range of second symbol to be xor:ed
|
||||
* @return resulting range representing the value range after and
|
||||
*/
|
||||
public Range xor(final Range a, final Range b) {
|
||||
if (a.isIntegerConst() && b.isIntegerConst()) {
|
||||
return createRange(((IntegerRange)a).getMin() ^ ((IntegerRange)b).getMin());
|
||||
}
|
||||
|
||||
if (a.isIntegerType() && b.isIntegerType()) {
|
||||
final int resultMask = (int)(((IntegerRange)a).getBitMask() | ((IntegerRange)b).getBitMask());
|
||||
if (resultMask >= 0) {
|
||||
return createIntegerRange(0, createIntegerRange(0, resultMask).getBitMask());
|
||||
}
|
||||
}
|
||||
return createTypeRange(Type.INT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bitwise shl operation
|
||||
* @param a range of first symbol to be shl:ed
|
||||
* @param b range of second symbol to be shl:ed
|
||||
* @return resulting range representing the value range after shl
|
||||
*/
|
||||
public Range shl(final Range a, final Range b) {
|
||||
if (b.isIntegerType() && b.isIntegerConst()) {
|
||||
final IntegerRange left = (IntegerRange)(a.isIntegerType() ? a : createTypeRange(Type.INT));
|
||||
final int shift = (int)((IntegerRange) b).getMin() & 0x1f;
|
||||
final int min = (int)left.getMin() << shift;
|
||||
final int max = (int)left.getMax() << shift;
|
||||
if (min >> shift == left.getMin() && max >> shift == left.getMax()) {
|
||||
return createIntegerRange(min, max);
|
||||
}
|
||||
}
|
||||
|
||||
return createTypeRange(Type.INT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bitwise shr operation
|
||||
* @param a range of first symbol to be shr:ed
|
||||
* @param b range of second symbol to be shr:ed
|
||||
* @return resulting range representing the value range after shr
|
||||
*/
|
||||
public Range shr(final Range a, final Range b) {
|
||||
if (b.isIntegerType() && b.isIntegerConst()) {
|
||||
final long shift = ((IntegerRange) b).getMin() & 0x1f;
|
||||
final IntegerRange left = (IntegerRange)(a.isIntegerType() ? a : createTypeRange(Type.INT));
|
||||
if (left.getMin() >= 0) {
|
||||
long min = left.getMin() >>> shift;
|
||||
long max = left.getMax() >>> shift;
|
||||
return createIntegerRange(min, max);
|
||||
} else if (shift >= 1) {
|
||||
return createIntegerRange(0, JSType.MAX_UINT >>> shift);
|
||||
}
|
||||
}
|
||||
|
||||
return createTypeRange(Type.INT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bitwise sar operation
|
||||
* @param a range of first symbol to be sar:ed
|
||||
* @param b range of second symbol to be sar:ed
|
||||
* @return resulting range representing the value range after sar
|
||||
*/
|
||||
public Range sar(final Range a, final Range b) {
|
||||
if (b.isIntegerType() && b.isIntegerConst()) {
|
||||
final IntegerRange left = (IntegerRange)(a.isIntegerType() ? a : createTypeRange(Type.INT));
|
||||
final long shift = ((IntegerRange) b).getMin() & 0x1f;
|
||||
final long min = left.getMin() >> shift;
|
||||
final long max = left.getMax() >> shift;
|
||||
return createIntegerRange(min, max);
|
||||
}
|
||||
|
||||
return createTypeRange(Type.INT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modulo operation
|
||||
* @param a range of first symbol to the mod operation
|
||||
* @param b range of second symbol to be mod operation
|
||||
* @return resulting range representing the value range after mod
|
||||
*/
|
||||
public Range mod(final Range a, final Range b) {
|
||||
if (a.isIntegerType() && b.isIntegerType()) {
|
||||
final IntegerRange rhs = (IntegerRange) b;
|
||||
if (rhs.getMin() > 0 || rhs.getMax() < 0) { // divisor range must not include 0
|
||||
final long absmax = Math.max(Math.abs(rhs.getMin()), Math.abs(rhs.getMax())) - 1;
|
||||
return createIntegerRange(rhs.getMin() > 0 ? 0 : -absmax, rhs.getMax() < 0 ? 0 : +absmax);
|
||||
}
|
||||
}
|
||||
return createTypeRange(Type.NUMBER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Division operation
|
||||
* @param a range of first symbol to the division
|
||||
* @param b range of second symbol to be division
|
||||
* @return resulting range representing the value range after division
|
||||
*/
|
||||
public Range div(final Range a, final Range b) {
|
||||
// TODO
|
||||
return createTypeRange(Type.NUMBER);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple trace functionality that will log range creation
|
||||
*/
|
||||
public static class TraceFunctionality extends Functionality {
|
||||
TraceFunctionality(final DebugLogger log) {
|
||||
super(log);
|
||||
}
|
||||
|
||||
private Range trace(final Range result, final String operation, final Range... operands) {
|
||||
log.fine("range::" + operation + Arrays.toString(operands) + " => " + result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Range join(final Range a, final Range b) {
|
||||
final Range result = super.join(a, b);
|
||||
if (!a.equals(b)) {
|
||||
trace(result, "join", a, b);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Range add(final Range a, final Range b) {
|
||||
return trace(super.add(a, b), "add", a, b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Range sub(final Range a, final Range b) {
|
||||
return trace(super.sub(a, b), "sub", a, b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Range mul(final Range a, final Range b) {
|
||||
return trace(super.mul(a, b), "mul", a, b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Range neg(final Range a) {
|
||||
return trace(super.neg(a), "neg", a);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Range and(final Range a, final Range b) {
|
||||
return trace(super.and(a, b), "and", a, b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Range or(final Range a, final Range b) {
|
||||
return trace(super.or(a, b), "or", a, b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Range xor(final Range a, final Range b) {
|
||||
return trace(super.xor(a, b), "xor", a, b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Range shl(final Range a, final Range b) {
|
||||
return trace(super.shl(a, b), "shl", a, b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Range shr(final Range a, final Range b) {
|
||||
return trace(super.shr(a, b), "shr", a, b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Range sar(final Range a, final Range b) {
|
||||
return trace(super.sar(a, b), "sar", a, b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Range mod(final Range a, final Range b) {
|
||||
return trace(super.mod(a, b), "mod", a, b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Range div(final Range a, final Range b) {
|
||||
return trace(super.div(a, b), "div", a, b);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.valueOf(getType());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static boolean isRepresentableAsInt(final double number) {
|
||||
return (int)number == number && !isNegativeZero(number);
|
||||
}
|
||||
|
||||
private static boolean isRepresentableAsLong(final double number) {
|
||||
return (long)number == number && !isNegativeZero(number);
|
||||
}
|
||||
|
||||
private static boolean isNegativeZero(final double number) {
|
||||
return Double.doubleToLongBits(number) == Double.doubleToLongBits(-0.0);
|
||||
}
|
||||
}
|
@ -106,22 +106,12 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
|
||||
Type(final String name, final Class<?> clazz, final int weight, final int slots) {
|
||||
this.name = name;
|
||||
this.clazz = clazz;
|
||||
this.descriptor = Type.getDescriptor(clazz);
|
||||
this.descriptor = jdk.internal.org.objectweb.asm.Type.getDescriptor(clazz);
|
||||
this.weight = weight;
|
||||
assert weight >= MIN_WEIGHT && weight <= MAX_WEIGHT : "illegal type weight: " + weight;
|
||||
this.slots = slots;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an internal descriptor for a type
|
||||
*
|
||||
* @param type the type
|
||||
* @return descriptor string
|
||||
*/
|
||||
public static String getDescriptor(final Class<?> type) {
|
||||
return jdk.internal.org.objectweb.asm.Type.getDescriptor(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the weight of this type - use this e.g. for sorting method descriptors
|
||||
* @return the weight
|
||||
|
@ -60,7 +60,7 @@ public final class AccessNode extends BaseNode {
|
||||
* @param visitor IR navigating visitor.
|
||||
*/
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
|
||||
if (visitor.enterAccessNode(this)) {
|
||||
return visitor.leaveAccessNode(
|
||||
setBase(base.accept(visitor)).
|
||||
@ -110,7 +110,6 @@ public final class AccessNode extends BaseNode {
|
||||
return new AccessNode(this, base, property, isFunction(), hasCallSiteType());
|
||||
}
|
||||
|
||||
|
||||
private AccessNode setProperty(final IdentNode property) {
|
||||
if (this.property == property) {
|
||||
return this;
|
||||
|
@ -59,6 +59,23 @@ public final class BinaryNode extends Node implements Assignment<Node> {
|
||||
this.rhs = rhs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isComparison() {
|
||||
switch (tokenType()) {
|
||||
case EQ:
|
||||
case EQ_STRICT:
|
||||
case NE:
|
||||
case NE_STRICT:
|
||||
case LE:
|
||||
case LT:
|
||||
case GE:
|
||||
case GT:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the widest possible type for this operation. This is used for compile time
|
||||
* static type inference
|
||||
@ -143,7 +160,7 @@ public final class BinaryNode extends Node implements Assignment<Node> {
|
||||
* @param visitor IR navigating visitor.
|
||||
*/
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
|
||||
if (visitor.enterBinaryNode(this)) {
|
||||
return visitor.leaveBinaryNode(setLHS(lhs.accept(visitor)).setRHS(rhs.accept(visitor)));
|
||||
}
|
||||
|
@ -131,7 +131,7 @@ public class Block extends BreakableNode implements Flags<Block> {
|
||||
* @return new or same node
|
||||
*/
|
||||
@Override
|
||||
public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
|
||||
public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) {
|
||||
if (visitor.enterBlock(this)) {
|
||||
return visitor.leaveBlock(setStatements(lc, Node.accept(visitor, Statement.class, statements)));
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ public final class BreakNode extends Statement {
|
||||
* @param visitor IR navigating visitor.
|
||||
*/
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
|
||||
if (visitor.enterBreakNode(this)) {
|
||||
return visitor.leaveBreakNode(this);
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ package jdk.nashorn.internal.ir;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.annotations.Ignore;
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
@ -194,7 +195,7 @@ public final class CallNode extends LexicalContextNode implements TypeOverride<C
|
||||
* @return node or replacement
|
||||
*/
|
||||
@Override
|
||||
public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
|
||||
public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) {
|
||||
if (visitor.enterCallNode(this)) {
|
||||
final CallNode newCallNode = (CallNode)visitor.leaveCallNode(
|
||||
setFunction(function.accept(visitor)).
|
||||
|
@ -78,7 +78,7 @@ public final class CaseNode extends Node {
|
||||
* @param visitor IR navigating visitor.
|
||||
*/
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
|
||||
if (visitor.enterCaseNode(this)) {
|
||||
final Node newTest = test == null ? null : test.accept(visitor);
|
||||
final Block newBody = body == null ? null : (Block)body.accept(visitor);
|
||||
|
@ -42,6 +42,11 @@ public final class CatchNode extends Statement {
|
||||
/** Catch body. */
|
||||
private final Block body;
|
||||
|
||||
private final int flags;
|
||||
|
||||
/** Is this block a synthethic rethrow created by finally inlining? */
|
||||
public static final int IS_SYNTHETIC_RETHROW = 1;
|
||||
|
||||
/**
|
||||
* Constructors
|
||||
*
|
||||
@ -51,19 +56,22 @@ public final class CatchNode extends Statement {
|
||||
* @param exception variable name of exception
|
||||
* @param exceptionCondition exception condition
|
||||
* @param body catch body
|
||||
* @param flags flags
|
||||
*/
|
||||
public CatchNode(final int lineNumber, final long token, final int finish, final IdentNode exception, final Node exceptionCondition, final Block body) {
|
||||
public CatchNode(final int lineNumber, final long token, final int finish, final IdentNode exception, final Node exceptionCondition, final Block body, final int flags) {
|
||||
super(lineNumber, token, finish);
|
||||
this.exception = exception;
|
||||
this.exceptionCondition = exceptionCondition;
|
||||
this.body = body;
|
||||
this.flags = flags;
|
||||
}
|
||||
|
||||
private CatchNode(final CatchNode catchNode, final IdentNode exception, final Node exceptionCondition, final Block body) {
|
||||
private CatchNode(final CatchNode catchNode, final IdentNode exception, final Node exceptionCondition, final Block body, final int flags) {
|
||||
super(catchNode);
|
||||
this.exception = exception;
|
||||
this.exceptionCondition = exceptionCondition;
|
||||
this.body = body;
|
||||
this.flags = flags;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -71,7 +79,7 @@ public final class CatchNode extends Statement {
|
||||
* @param visitor IR navigating visitor.
|
||||
*/
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
|
||||
if (visitor.enterCatchNode(this)) {
|
||||
return visitor.leaveCatchNode(
|
||||
setException((IdentNode)exception.accept(visitor)).
|
||||
@ -124,7 +132,7 @@ public final class CatchNode extends Statement {
|
||||
if (this.exceptionCondition == exceptionCondition) {
|
||||
return this;
|
||||
}
|
||||
return new CatchNode(this, exception, exceptionCondition, body);
|
||||
return new CatchNode(this, exception, exceptionCondition, body, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -144,13 +152,25 @@ public final class CatchNode extends Statement {
|
||||
if (this.exception == exception) {
|
||||
return this;
|
||||
}
|
||||
return new CatchNode(this, exception, exceptionCondition, body);
|
||||
return new CatchNode(this, exception, exceptionCondition, body, flags);
|
||||
}
|
||||
|
||||
private CatchNode setBody(final Block body) {
|
||||
if (this.body == body) {
|
||||
return this;
|
||||
}
|
||||
return new CatchNode(this, exception, exceptionCondition, body);
|
||||
return new CatchNode(this, exception, exceptionCondition, body, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this catch block a non-JavaScript constructor, for example created as
|
||||
* part of the rethrow mechanism of a finally block in Lower? Then we just
|
||||
* pass the exception on and need not unwrap whatever is in the ECMAException
|
||||
* object catch symbol
|
||||
* @return true if a finally synthetic rethrow
|
||||
*/
|
||||
public boolean isSyntheticRethrow() {
|
||||
return (flags & IS_SYNTHETIC_RETHROW) == IS_SYNTHETIC_RETHROW;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ public class ContinueNode extends Statement {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
|
||||
if (visitor.enterContinueNode(this)) {
|
||||
return visitor.leaveContinueNode(this);
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ public final class EmptyNode extends Statement {
|
||||
|
||||
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
|
||||
if (visitor.enterEmptyNode(this)) {
|
||||
return visitor.leaveEmptyNode(this);
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ public final class ExecuteNode extends Statement {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
|
||||
if (visitor.enterExecuteNode(this)) {
|
||||
return visitor.leaveExecuteNode(setExpression(expression.accept(visitor)));
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ public final class ForNode extends LoopNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node accept(final LexicalContext lc, final NodeVisitor visitor) {
|
||||
protected Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) {
|
||||
if (visitor.enterForNode(this)) {
|
||||
return visitor.leaveForNode(
|
||||
setInit(lc, init == null ? null : init.accept(visitor)).
|
||||
|
@ -250,6 +250,7 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
|
||||
final FunctionNode functionNode,
|
||||
final long lastToken,
|
||||
final int flags,
|
||||
final String name,
|
||||
final Type returnType,
|
||||
final CompileUnit compileUnit,
|
||||
final EnumSet<CompilationState> compilationState,
|
||||
@ -260,6 +261,7 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
|
||||
super(functionNode);
|
||||
|
||||
this.flags = flags;
|
||||
this.name = name;
|
||||
this.returnType = returnType;
|
||||
this.compileUnit = compileUnit;
|
||||
this.lastToken = lastToken;
|
||||
@ -271,7 +273,6 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
|
||||
|
||||
// the fields below never change - they are final and assigned in constructor
|
||||
this.source = functionNode.source;
|
||||
this.name = functionNode.name;
|
||||
this.ident = functionNode.ident;
|
||||
this.namespace = functionNode.namespace;
|
||||
this.declaredSymbols = functionNode.declaredSymbols;
|
||||
@ -280,7 +281,7 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
|
||||
public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) {
|
||||
if (visitor.enterFunctionNode(this)) {
|
||||
return visitor.leaveFunctionNode(setBody(lc, (Block)body.accept(visitor)));
|
||||
}
|
||||
@ -315,7 +316,7 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
|
||||
if (this.snapshot == null) {
|
||||
return this;
|
||||
}
|
||||
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body, parameters, null, hints));
|
||||
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, name, returnType, compileUnit, compilationState, body, parameters, null, hints));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -331,7 +332,7 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
|
||||
if (isProgram() || parameters.isEmpty()) {
|
||||
return this; //never specialize anything that won't be recompiled
|
||||
}
|
||||
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body, parameters, this, hints));
|
||||
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, name, returnType, compileUnit, compilationState, body, parameters, this, hints));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -339,7 +340,7 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
|
||||
* @return true if specialization is possible
|
||||
*/
|
||||
public boolean canSpecialize() {
|
||||
return getFlag(CAN_SPECIALIZE);
|
||||
return snapshot != null && getFlag(CAN_SPECIALIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -389,7 +390,7 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
|
||||
}
|
||||
final EnumSet<CompilationState> newState = EnumSet.copyOf(this.compilationState);
|
||||
newState.add(state);
|
||||
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, newState, body, parameters, snapshot, hints));
|
||||
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, name, returnType, compileUnit, newState, body, parameters, snapshot, hints));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -410,7 +411,7 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
|
||||
if (this.hints == hints) {
|
||||
return this;
|
||||
}
|
||||
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
|
||||
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -463,7 +464,7 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
|
||||
if (this.flags == flags) {
|
||||
return this;
|
||||
}
|
||||
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
|
||||
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -529,7 +530,7 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the identifier for this function
|
||||
* Get the identifier for this function, this is its symbol.
|
||||
* @return the identifier as an IdentityNode
|
||||
*/
|
||||
public IdentNode getIdent() {
|
||||
@ -572,7 +573,7 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
|
||||
if(this.body == body) {
|
||||
return this;
|
||||
}
|
||||
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
|
||||
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -640,7 +641,7 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
|
||||
if (this.lastToken == lastToken) {
|
||||
return this;
|
||||
}
|
||||
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
|
||||
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -651,6 +652,20 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the internal name for this function
|
||||
* @param lc lexical context
|
||||
* @param name new name
|
||||
* @return new function node if changed, otherwise the same
|
||||
*/
|
||||
public FunctionNode setName(final LexicalContext lc, final String name) {
|
||||
if (this.name.equals(name)) {
|
||||
return this;
|
||||
}
|
||||
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this function should have all its variables in its own scope. Scripts, split sub-functions, and
|
||||
* functions having with and/or eval blocks are such.
|
||||
@ -698,7 +713,7 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
|
||||
if (this.parameters == parameters) {
|
||||
return this;
|
||||
}
|
||||
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
|
||||
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -762,6 +777,7 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
|
||||
this,
|
||||
lastToken,
|
||||
flags,
|
||||
name,
|
||||
Type.widest(this.returnType, returnType.isObject() ?
|
||||
Type.OBJECT :
|
||||
returnType),
|
||||
@ -801,7 +817,7 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
|
||||
if (this.compileUnit == compileUnit) {
|
||||
return this;
|
||||
}
|
||||
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
|
||||
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -29,7 +29,6 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.__DIR__;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.__FILE__;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.__LINE__;
|
||||
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.DEBUG_FIELDS;
|
||||
|
||||
import jdk.nashorn.internal.codegen.ObjectClassGenerator;
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
@ -119,7 +118,7 @@ public final class IdentNode extends Node implements PropertyKey, TypeOverride<I
|
||||
* @param visitor IR navigating visitor.
|
||||
*/
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
|
||||
if (visitor.enterIdentNode(this)) {
|
||||
return visitor.leaveIdentNode(this);
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ public final class IfNode extends Statement {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
|
||||
if (visitor.enterIfNode(this)) {
|
||||
return visitor.leaveIfNode(
|
||||
setTest(test.accept(visitor)).
|
||||
|
@ -56,19 +56,12 @@ public final class IndexNode extends BaseNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
|
||||
if (visitor.enterIndexNode(this)) {
|
||||
final Node newBase = base.accept(visitor);
|
||||
final Node newIndex = index.accept(visitor);
|
||||
final IndexNode newNode;
|
||||
if (newBase != base || newIndex != index) {
|
||||
newNode = new IndexNode(this, newBase, newIndex, isFunction(), hasCallSiteType());
|
||||
} else {
|
||||
newNode = this;
|
||||
}
|
||||
return visitor.leaveIndexNode(newNode);
|
||||
return visitor.leaveIndexNode(
|
||||
setBase(base.accept(visitor)).
|
||||
setIndex(index.accept(visitor)));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -106,6 +99,13 @@ public final class IndexNode extends BaseNode {
|
||||
return index;
|
||||
}
|
||||
|
||||
private IndexNode setBase(final Node base) {
|
||||
if (this.base == base) {
|
||||
return this;
|
||||
}
|
||||
return new IndexNode(this, base, index, isFunction(), hasCallSiteType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the index expression for this node
|
||||
* @param index new index expression
|
||||
|
@ -67,11 +67,11 @@ public final class LabelNode extends LexicalContextNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
|
||||
public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) {
|
||||
if (visitor.enterLabelNode(this)) {
|
||||
return visitor.leaveLabelNode(
|
||||
setLabel(visitor.getLexicalContext(), (IdentNode)label.accept(visitor)).
|
||||
setBody(visitor.getLexicalContext(), (Block)body.accept(visitor)));
|
||||
setLabel(lc, (IdentNode)label.accept(visitor)).
|
||||
setBody(lc, (Block)body.accept(visitor)));
|
||||
}
|
||||
|
||||
return this;
|
||||
|
@ -439,6 +439,23 @@ public class LexicalContext {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the lexical context is currently inside a loop
|
||||
* @return true if inside a loop
|
||||
*/
|
||||
public boolean inLoop() {
|
||||
return getCurrentLoop() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the loop header of the current loop, or null if not inside a loop
|
||||
* @return loop header
|
||||
*/
|
||||
public LoopNode getCurrentLoop() {
|
||||
final Iterator<LoopNode> iter = new NodeIterator<>(LoopNode.class, getCurrentFunction());
|
||||
return iter.hasNext() ? iter.next() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the breakable node corresponding to this label.
|
||||
* @param label label to search for, if null the closest breakable node will be returned unconditionally, e.g. a while loop with no label
|
||||
@ -461,8 +478,7 @@ public class LexicalContext {
|
||||
}
|
||||
|
||||
private LoopNode getContinueTo() {
|
||||
final Iterator<LoopNode> iter = new NodeIterator<>(LoopNode.class, getCurrentFunction());
|
||||
return iter.hasNext() ? iter.next() : null;
|
||||
return getCurrentLoop();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -60,10 +60,10 @@ public abstract class LexicalContextNode extends Statement {
|
||||
*
|
||||
* @return new node or same node depending on state change
|
||||
*/
|
||||
protected abstract Node accept(final LexicalContext lc, final NodeVisitor visitor);
|
||||
protected abstract Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor);
|
||||
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
|
||||
final LexicalContext lc = visitor.getLexicalContext();
|
||||
lc.push(this);
|
||||
final LexicalContextNode newNode = (LexicalContextNode)accept(lc, visitor);
|
||||
|
@ -28,6 +28,7 @@ package jdk.nashorn.internal.ir;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.nashorn.internal.codegen.CompileUnit;
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
@ -208,7 +209,7 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
|
||||
* @param visitor IR navigating visitor.
|
||||
*/
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
|
||||
if (visitor.enterLiteralNode(this)) {
|
||||
return visitor.leaveLiteralNode(this);
|
||||
}
|
||||
@ -514,7 +515,7 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
|
||||
if (visitor.enterLiteralNode(this)) {
|
||||
if (value != null) {
|
||||
final Node newValue = value.accept(visitor);
|
||||
@ -840,7 +841,7 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
|
||||
if (visitor.enterLiteralNode(this)) {
|
||||
final List<Node> oldValue = Arrays.asList(value);
|
||||
final List<Node> newValue = Node.accept(visitor, Node.class, oldValue);
|
||||
|
@ -27,6 +27,7 @@ package jdk.nashorn.internal.ir;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.parser.Token;
|
||||
@ -152,6 +153,14 @@ public abstract class Node implements Cloneable {
|
||||
return Type.OBJECT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this node represents a comparison operator
|
||||
* @return true if comparison
|
||||
*/
|
||||
public boolean isComparison() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* For reference copies - ensure that labels in the copy node are unique
|
||||
* using an appropriate copy constructor
|
||||
@ -167,7 +176,7 @@ public abstract class Node implements Cloneable {
|
||||
* @param visitor Node visitor.
|
||||
* @return node the node or its replacement after visitation, null if no further visitations are required
|
||||
*/
|
||||
public abstract Node accept(NodeVisitor visitor);
|
||||
public abstract Node accept(NodeVisitor<? extends LexicalContext> visitor);
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
@ -329,7 +338,7 @@ public abstract class Node implements Cloneable {
|
||||
}
|
||||
|
||||
//on change, we have to replace the entire list, that's we can't simple do ListIterator.set
|
||||
static <T extends Node> List<T> accept(final NodeVisitor visitor, final Class<T> clazz, final List<T> list) {
|
||||
static <T extends Node> List<T> accept(final NodeVisitor<? extends LexicalContext> visitor, final Class<T> clazz, final List<T> list) {
|
||||
boolean changed = false;
|
||||
final List<T> newList = new ArrayList<>();
|
||||
|
||||
|
@ -58,7 +58,7 @@ public final class ObjectNode extends Node {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
|
||||
if (visitor.enterObjectNode(this)) {
|
||||
return visitor.leaveObjectNode(setElements(Node.accept(visitor, Node.class, elements)));
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ public final class PropertyNode extends Node {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
|
||||
if (visitor.enterPropertyNode(this)) {
|
||||
return visitor.leavePropertyNode(
|
||||
setKey((PropertyKey)((Node)key).accept(visitor)).
|
||||
|
@ -86,7 +86,7 @@ public class ReturnNode extends Statement {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
|
||||
if (visitor.enterReturnNode(this)) {
|
||||
if (expression != null) {
|
||||
return visitor.leaveReturnNode(setExpression(expression.accept(visitor)));
|
||||
|
@ -29,6 +29,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
@ -407,7 +408,7 @@ public class RuntimeNode extends Node implements TypeOverride<RuntimeNode> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
|
||||
if (visitor.enterRuntimeNode(this)) {
|
||||
final List<Node> newArgs = new ArrayList<>();
|
||||
for (final Node arg : args) {
|
||||
|
@ -81,7 +81,7 @@ public class SplitNode extends LexicalContextNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
|
||||
public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) {
|
||||
if (visitor.enterSplitNode(this)) {
|
||||
return visitor.leaveSplitNode(setBody(lc, body.accept(visitor)));
|
||||
}
|
||||
|
@ -100,11 +100,11 @@ public final class SwitchNode extends BreakableNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
|
||||
public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) {
|
||||
if (visitor.enterSwitchNode(this)) {
|
||||
return visitor.leaveSwitchNode(
|
||||
setExpression(visitor.getLexicalContext(), expression.accept(visitor)).
|
||||
setCases(visitor.getLexicalContext(), Node.accept(visitor, CaseNode.class, cases), defaultCaseIndex));
|
||||
setExpression(lc, expression.accept(visitor)).
|
||||
setCases(lc, Node.accept(visitor, CaseNode.class, cases), defaultCaseIndex));
|
||||
}
|
||||
|
||||
return this;
|
||||
|
@ -29,6 +29,8 @@ import java.io.PrintWriter;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import jdk.nashorn.internal.codegen.types.Range;
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.runtime.Context;
|
||||
import jdk.nashorn.internal.runtime.Debug;
|
||||
@ -89,6 +91,9 @@ public final class Symbol implements Comparable<Symbol> {
|
||||
/** Number of times this symbol is used in code */
|
||||
private int useCount;
|
||||
|
||||
/** Range for symbol */
|
||||
private Range range;
|
||||
|
||||
/** Debugging option - dump info and stack trace when symbols with given names are manipulated */
|
||||
private static final Set<String> TRACE_SYMBOLS;
|
||||
private static final Set<String> TRACE_SYMBOLS_STACKTRACE;
|
||||
@ -131,6 +136,7 @@ public final class Symbol implements Comparable<Symbol> {
|
||||
this.type = type;
|
||||
this.slot = slot;
|
||||
this.fieldIndex = -1;
|
||||
this.range = Range.createUnknownRange();
|
||||
trace("CREATE SYMBOL");
|
||||
}
|
||||
|
||||
@ -157,12 +163,13 @@ public final class Symbol implements Comparable<Symbol> {
|
||||
|
||||
private Symbol(final Symbol base, final String name, final int flags) {
|
||||
this.flags = flags;
|
||||
this.name = name;
|
||||
this.name = name;
|
||||
|
||||
this.fieldIndex = base.fieldIndex;
|
||||
this.slot = base.slot;
|
||||
this.type = base.type;
|
||||
this.useCount = base.useCount;
|
||||
this.slot = base.slot;
|
||||
this.type = base.type;
|
||||
this.useCount = base.useCount;
|
||||
this.range = base.range;
|
||||
}
|
||||
|
||||
private static String align(final String string, final int max) {
|
||||
@ -276,7 +283,7 @@ public final class Symbol implements Comparable<Symbol> {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append(name).
|
||||
append(' ').
|
||||
@ -409,6 +416,22 @@ public final class Symbol implements Comparable<Symbol> {
|
||||
return (flags & KINDMASK) == IS_PARAM;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the range for this symbol
|
||||
* @return range for symbol
|
||||
*/
|
||||
public Range getRange() {
|
||||
return range;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the range for this symbol
|
||||
* @param range range
|
||||
*/
|
||||
public void setRange(final Range range) {
|
||||
this.range = range;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this symbol is a function parameter of known
|
||||
* narrowest type
|
||||
|
@ -63,7 +63,7 @@ public final class TernaryNode extends Node {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
|
||||
if (visitor.enterTernaryNode(this)) {
|
||||
final Node newLhs = lhs().accept(visitor);
|
||||
final Node newRhs = rhs().accept(visitor);
|
||||
|
@ -36,6 +36,11 @@ public final class ThrowNode extends Statement {
|
||||
/** Exception expression. */
|
||||
private final Node expression;
|
||||
|
||||
private final int flags;
|
||||
|
||||
/** Is this block a synthethic rethrow created by finally inlining? */
|
||||
public static final int IS_SYNTHETIC_RETHROW = 1;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
@ -43,15 +48,18 @@ public final class ThrowNode extends Statement {
|
||||
* @param token token
|
||||
* @param finish finish
|
||||
* @param expression expression to throw
|
||||
* @param flags flags
|
||||
*/
|
||||
public ThrowNode(final int lineNumber, final long token, final int finish, final Node expression) {
|
||||
public ThrowNode(final int lineNumber, final long token, final int finish, final Node expression, final int flags) {
|
||||
super(lineNumber, token, finish);
|
||||
this.expression = expression;
|
||||
this.flags = flags;
|
||||
}
|
||||
|
||||
private ThrowNode(final ThrowNode node, final Node expression) {
|
||||
private ThrowNode(final ThrowNode node, final Node expression, final int flags) {
|
||||
super(node);
|
||||
this.expression = expression;
|
||||
this.flags = flags;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -64,7 +72,7 @@ public final class ThrowNode extends Statement {
|
||||
* @param visitor IR navigating visitor.
|
||||
*/
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
|
||||
if (visitor.enterThrowNode(this)) {
|
||||
return visitor.leaveThrowNode(setExpression(expression.accept(visitor)));
|
||||
}
|
||||
@ -98,7 +106,17 @@ public final class ThrowNode extends Statement {
|
||||
if (this.expression == expression) {
|
||||
return this;
|
||||
}
|
||||
return new ThrowNode(this, expression);
|
||||
return new ThrowNode(this, expression, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this a throw a synthetic rethrow in a synthetic catch-all block
|
||||
* created when inlining finally statements? In that case we never
|
||||
* wrap whatever is thrown into an ECMAException, just rethrow it.
|
||||
* @return true if synthetic throw node
|
||||
*/
|
||||
public boolean isSyntheticRethrow() {
|
||||
return (flags & IS_SYNTHETIC_RETHROW) == IS_SYNTHETIC_RETHROW;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ public final class TryNode extends Statement {
|
||||
* @param visitor IR navigating visitor.
|
||||
*/
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
|
||||
if (visitor.enterTryNode(this)) {
|
||||
// Need to do finallybody first for termination analysis. TODO still necessary?
|
||||
final Block newFinallyBody = finallyBody == null ? null : (Block)finallyBody.accept(visitor);
|
||||
|
@ -29,7 +29,6 @@ import static jdk.nashorn.internal.parser.TokenType.BIT_NOT;
|
||||
import static jdk.nashorn.internal.parser.TokenType.CONVERT;
|
||||
import static jdk.nashorn.internal.parser.TokenType.DECPOSTFIX;
|
||||
import static jdk.nashorn.internal.parser.TokenType.INCPOSTFIX;
|
||||
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
@ -121,7 +120,7 @@ public final class UnaryNode extends Node implements Assignment<Node> {
|
||||
* @param visitor IR navigating visitor.
|
||||
*/
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
|
||||
if (visitor.enterUnaryNode(this)) {
|
||||
return visitor.leaveUnaryNode(setRHS(rhs.accept(visitor)));
|
||||
}
|
||||
|
@ -121,7 +121,7 @@ public final class VarNode extends Statement implements Assignment<IdentNode> {
|
||||
* @param visitor IR navigating visitor.
|
||||
*/
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
|
||||
if (visitor.enterVarNode(this)) {
|
||||
final IdentNode newName = (IdentNode)name.accept(visitor);
|
||||
final Node newInit = init == null ? null : init.accept(visitor);
|
||||
|
@ -75,7 +75,7 @@ public final class WhileNode extends LoopNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node accept(final LexicalContext lc, final NodeVisitor visitor) {
|
||||
protected Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) {
|
||||
if (visitor.enterWhileNode(this)) {
|
||||
if (isDoWhile()) {
|
||||
return visitor.leaveWhileNode(
|
||||
|
@ -64,7 +64,7 @@ public final class WithNode extends LexicalContextNode {
|
||||
* @param visitor IR navigating visitor.
|
||||
*/
|
||||
@Override
|
||||
public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
|
||||
public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) {
|
||||
if (visitor.enterWithNode(this)) {
|
||||
return visitor.leaveWithNode(
|
||||
setExpression(lc, expression.accept(visitor)).
|
||||
|
@ -45,6 +45,7 @@ import jdk.nashorn.internal.ir.IdentNode;
|
||||
import jdk.nashorn.internal.ir.IfNode;
|
||||
import jdk.nashorn.internal.ir.IndexNode;
|
||||
import jdk.nashorn.internal.ir.LabelNode;
|
||||
import jdk.nashorn.internal.ir.LexicalContext;
|
||||
import jdk.nashorn.internal.ir.LiteralNode;
|
||||
import jdk.nashorn.internal.ir.Node;
|
||||
import jdk.nashorn.internal.ir.ObjectNode;
|
||||
@ -74,7 +75,8 @@ import jdk.nashorn.internal.runtime.Source;
|
||||
/**
|
||||
* This IR writer produces a JSON string that represents AST as a JSON string.
|
||||
*/
|
||||
public final class JSONWriter extends NodeVisitor {
|
||||
public final class JSONWriter extends NodeVisitor<LexicalContext> {
|
||||
|
||||
/**
|
||||
* Returns AST as JSON compatible string.
|
||||
*
|
||||
@ -867,7 +869,8 @@ public final class JSONWriter extends NodeVisitor {
|
||||
// Internals below
|
||||
|
||||
private JSONWriter(final boolean includeLocation) {
|
||||
this.buf = new StringBuilder();
|
||||
super(new LexicalContext());
|
||||
this.buf = new StringBuilder();
|
||||
this.includeLocation = includeLocation;
|
||||
}
|
||||
|
||||
@ -963,7 +966,7 @@ public final class JSONWriter extends NodeVisitor {
|
||||
objectStart("loc");
|
||||
|
||||
// source name
|
||||
final Source src = getLexicalContext().getCurrentFunction().getSource();
|
||||
final Source src = lc.getCurrentFunction().getSource();
|
||||
property("source", src.getName());
|
||||
comma();
|
||||
|
||||
|
@ -36,6 +36,7 @@ import jdk.nashorn.internal.ir.ForNode;
|
||||
import jdk.nashorn.internal.ir.FunctionNode;
|
||||
import jdk.nashorn.internal.ir.IfNode;
|
||||
import jdk.nashorn.internal.ir.LabelNode;
|
||||
import jdk.nashorn.internal.ir.LexicalContext;
|
||||
import jdk.nashorn.internal.ir.Node;
|
||||
import jdk.nashorn.internal.ir.SplitNode;
|
||||
import jdk.nashorn.internal.ir.Statement;
|
||||
@ -53,7 +54,7 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
*
|
||||
* see the flags --print-parse and --print-lower-parse
|
||||
*/
|
||||
public final class PrintVisitor extends NodeVisitor {
|
||||
public final class PrintVisitor extends NodeVisitor<LexicalContext> {
|
||||
/** Tab width */
|
||||
private static final int TABWIDTH = 4;
|
||||
|
||||
@ -84,6 +85,7 @@ public final class PrintVisitor extends NodeVisitor {
|
||||
* @param printLineNumbers should line number nodes be included in the output?
|
||||
*/
|
||||
public PrintVisitor(final boolean printLineNumbers) {
|
||||
super(new LexicalContext());
|
||||
this.EOLN = System.lineSeparator();
|
||||
this.sb = new StringBuilder();
|
||||
this.printLineNumbers = printLineNumbers;
|
||||
|
@ -32,21 +32,15 @@ import jdk.nashorn.internal.ir.UnaryNode;
|
||||
|
||||
/**
|
||||
* Like NodeVisitor but navigating further into operators.
|
||||
* @param <T> Lexical context class for this NodeOperatorVisitor
|
||||
*/
|
||||
public class NodeOperatorVisitor extends NodeVisitor {
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public NodeOperatorVisitor() {
|
||||
super();
|
||||
}
|
||||
|
||||
public class NodeOperatorVisitor<T extends LexicalContext> extends NodeVisitor<T> {
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param lc a custom lexical context
|
||||
*/
|
||||
public NodeOperatorVisitor(final LexicalContext lc) {
|
||||
public NodeOperatorVisitor(final T lc) {
|
||||
super(lc);
|
||||
}
|
||||
|
||||
|
@ -60,23 +60,18 @@ import jdk.nashorn.internal.ir.WithNode;
|
||||
|
||||
/**
|
||||
* Visitor used to navigate the IR.
|
||||
* @param <T> lexical context class used by this visitor
|
||||
*/
|
||||
public abstract class NodeVisitor {
|
||||
private final LexicalContext lc;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public NodeVisitor() {
|
||||
this(new LexicalContext());
|
||||
}
|
||||
public abstract class NodeVisitor<T extends LexicalContext> {
|
||||
/** lexical context in use */
|
||||
protected final T lc;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param lc a custom lexical context
|
||||
*/
|
||||
public NodeVisitor(final LexicalContext lc) {
|
||||
public NodeVisitor(final T lc) {
|
||||
this.lc = lc;
|
||||
}
|
||||
|
||||
@ -84,7 +79,7 @@ public abstract class NodeVisitor {
|
||||
* Get the lexical context of this node visitor
|
||||
* @return lexical context
|
||||
*/
|
||||
public LexicalContext getLexicalContext() {
|
||||
public T getLexicalContext() {
|
||||
return lc;
|
||||
}
|
||||
|
||||
|
@ -58,11 +58,6 @@ abstract class ArrayBufferView extends ScriptObject {
|
||||
return factory().bytesPerElement;
|
||||
}
|
||||
|
||||
@Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE)
|
||||
public static Object BYTES_PER_ELEMENT(final Object self) {
|
||||
return ((ArrayBufferView)self).bytesPerElement();
|
||||
}
|
||||
|
||||
@Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE)
|
||||
public static Object buffer(final Object self) {
|
||||
return ((ArrayDataImpl)((ArrayBufferView)self).getArray()).buffer;
|
||||
|
@ -32,6 +32,7 @@ import static java.lang.Character.SPACE_SEPARATOR;
|
||||
import static java.lang.Character.UPPERCASE_LETTER;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* JavaScript date parser. This class first tries to parse a date string
|
||||
@ -486,7 +487,7 @@ public class DateParser {
|
||||
while (pos < limit && isAsciiLetter(string.charAt(pos))) {
|
||||
pos++;
|
||||
}
|
||||
final String key = string.substring(start, pos).toLowerCase();
|
||||
final String key = string.substring(start, pos).toLowerCase(Locale.ENGLISH);
|
||||
final Name name = names.get(key);
|
||||
// then advance to end of name
|
||||
while (pos < length && isAsciiLetter(string.charAt(pos))) {
|
||||
@ -683,7 +684,7 @@ public class DateParser {
|
||||
|
||||
Name(final String name, final int type, final int value) {
|
||||
assert name != null;
|
||||
assert name.equals(name.toLowerCase());
|
||||
assert name.equals(name.toLowerCase(Locale.ENGLISH));
|
||||
|
||||
this.name = name;
|
||||
// use first three characters as lookup key
|
||||
|
@ -603,6 +603,11 @@ public final class NativeArguments extends ScriptObject {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
private Object getArgumentsLength() {
|
||||
return length;
|
||||
}
|
||||
|
@ -754,25 +754,11 @@ public final class NativeArray extends ScriptObject {
|
||||
final Object obj = Global.toObject(self);
|
||||
final ScriptObject sobj = (ScriptObject)obj;
|
||||
final long len = JSType.toUint32(sobj.getLength());
|
||||
final double startNum = JSType.toNumber(start);
|
||||
final long relativeStartUint32 = JSType.toUint32(startNum);
|
||||
final long relativeStart = JSType.toInteger(startNum);
|
||||
final long relativeStart = JSType.toLong(start);
|
||||
final long relativeEnd = (end == ScriptRuntime.UNDEFINED) ? len : JSType.toLong(end);
|
||||
|
||||
long k = relativeStart < 0 ?
|
||||
Math.max(len + relativeStart, 0) :
|
||||
Math.min(
|
||||
Math.max(relativeStartUint32, relativeStart),
|
||||
len);
|
||||
|
||||
final double endNum = (end == ScriptRuntime.UNDEFINED)? Double.NaN : JSType.toNumber(end);
|
||||
final long relativeEndUint32 = (end == ScriptRuntime.UNDEFINED)? len : JSType.toUint32(endNum);
|
||||
final long relativeEnd = (end == ScriptRuntime.UNDEFINED)? len : JSType.toInteger(endNum);
|
||||
|
||||
final long finale = relativeEnd < 0 ?
|
||||
Math.max(len + relativeEnd, 0) :
|
||||
Math.min(
|
||||
Math.max(relativeEndUint32, relativeEnd),
|
||||
len);
|
||||
long k = relativeStart < 0 ? Math.max(len + relativeStart, 0) : Math.min(relativeStart, len);
|
||||
final long finale = relativeEnd < 0 ? Math.max(len + relativeEnd, 0) : Math.min(relativeEnd, len);
|
||||
|
||||
if (k >= finale) {
|
||||
return new NativeArray(0);
|
||||
@ -909,21 +895,10 @@ public final class NativeArray extends ScriptObject {
|
||||
final ScriptObject sobj = (ScriptObject)obj;
|
||||
final boolean strict = Global.isStrict();
|
||||
final long len = JSType.toUint32(sobj.getLength());
|
||||
final double startNum = JSType.toNumber(start);
|
||||
final long relativeStartUint32 = JSType.toUint32(startNum);
|
||||
final long relativeStart = JSType.toInteger(startNum);
|
||||
final long relativeStart = JSType.toLong(start);
|
||||
|
||||
//TODO: workaround overflow of relativeStart for start > Integer.MAX_VALUE
|
||||
final long actualStart = relativeStart < 0 ?
|
||||
Math.max(len + relativeStart, 0) :
|
||||
Math.min(
|
||||
Math.max(relativeStartUint32, relativeStart),
|
||||
len);
|
||||
|
||||
final long actualDeleteCount =
|
||||
Math.min(
|
||||
Math.max(JSType.toInteger(deleteCount), 0),
|
||||
len - actualStart);
|
||||
final long actualStart = relativeStart < 0 ? Math.max(len + relativeStart, 0) : Math.min(relativeStart, len);
|
||||
final long actualDeleteCount = Math.min(Math.max(JSType.toLong(deleteCount), 0), len - actualStart);
|
||||
|
||||
final NativeArray array = new NativeArray(actualDeleteCount);
|
||||
|
||||
|
@ -770,7 +770,7 @@ public final class NativeDate extends ScriptObject {
|
||||
nd.setTime(NaN);
|
||||
return nd.getTime();
|
||||
}
|
||||
int yearInt = JSType.toInteger(yearNum);
|
||||
int yearInt = (int)yearNum;
|
||||
if (0 <= yearInt && yearInt <= 99) {
|
||||
yearInt += 1900;
|
||||
}
|
||||
|
@ -28,7 +28,9 @@ package jdk.nashorn.internal.objects;
|
||||
import jdk.nashorn.internal.objects.annotations.Attribute;
|
||||
import jdk.nashorn.internal.objects.annotations.Constructor;
|
||||
import jdk.nashorn.internal.objects.annotations.Function;
|
||||
import jdk.nashorn.internal.objects.annotations.Property;
|
||||
import jdk.nashorn.internal.objects.annotations.ScriptClass;
|
||||
import jdk.nashorn.internal.objects.annotations.Where;
|
||||
import jdk.nashorn.internal.runtime.JSType;
|
||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
import jdk.nashorn.internal.runtime.arrays.ArrayData;
|
||||
@ -38,7 +40,12 @@ import jdk.nashorn.internal.runtime.arrays.ArrayData;
|
||||
*/
|
||||
@ScriptClass("Float32Array")
|
||||
public final class NativeFloat32Array extends ArrayBufferView {
|
||||
private static final int BYTES_PER_ELEMENT = 4;
|
||||
/**
|
||||
* The size in bytes of each element in the array.
|
||||
*/
|
||||
@Property(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE, where = Where.CONSTRUCTOR)
|
||||
public static final int BYTES_PER_ELEMENT = 4;
|
||||
|
||||
private static final Factory FACTORY = new Factory(BYTES_PER_ELEMENT) {
|
||||
@Override
|
||||
public ArrayBufferView construct(final NativeArrayBuffer buffer, final int byteOffset, final int length) {
|
||||
|
@ -28,7 +28,9 @@ package jdk.nashorn.internal.objects;
|
||||
import jdk.nashorn.internal.objects.annotations.Attribute;
|
||||
import jdk.nashorn.internal.objects.annotations.Constructor;
|
||||
import jdk.nashorn.internal.objects.annotations.Function;
|
||||
import jdk.nashorn.internal.objects.annotations.Property;
|
||||
import jdk.nashorn.internal.objects.annotations.ScriptClass;
|
||||
import jdk.nashorn.internal.objects.annotations.Where;
|
||||
import jdk.nashorn.internal.runtime.JSType;
|
||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
import jdk.nashorn.internal.runtime.arrays.ArrayData;
|
||||
@ -38,7 +40,12 @@ import jdk.nashorn.internal.runtime.arrays.ArrayData;
|
||||
*/
|
||||
@ScriptClass("Float64Array")
|
||||
public final class NativeFloat64Array extends ArrayBufferView {
|
||||
private static final int BYTES_PER_ELEMENT = 8;
|
||||
/**
|
||||
* The size in bytes of each element in the array.
|
||||
*/
|
||||
@Property(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE, where = Where.CONSTRUCTOR)
|
||||
public static final int BYTES_PER_ELEMENT = 8;
|
||||
|
||||
private static final Factory FACTORY = new Factory(BYTES_PER_ELEMENT) {
|
||||
@Override
|
||||
public ArrayBufferView construct(final NativeArrayBuffer buffer, final int byteOffset, final int length) {
|
||||
|
@ -28,7 +28,9 @@ package jdk.nashorn.internal.objects;
|
||||
import jdk.nashorn.internal.objects.annotations.Attribute;
|
||||
import jdk.nashorn.internal.objects.annotations.Constructor;
|
||||
import jdk.nashorn.internal.objects.annotations.Function;
|
||||
import jdk.nashorn.internal.objects.annotations.Property;
|
||||
import jdk.nashorn.internal.objects.annotations.ScriptClass;
|
||||
import jdk.nashorn.internal.objects.annotations.Where;
|
||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
import jdk.nashorn.internal.runtime.arrays.ArrayData;
|
||||
|
||||
@ -37,7 +39,12 @@ import jdk.nashorn.internal.runtime.arrays.ArrayData;
|
||||
*/
|
||||
@ScriptClass("Int16Array")
|
||||
public final class NativeInt16Array extends ArrayBufferView {
|
||||
private static final int BYTES_PER_ELEMENT = 2;
|
||||
/**
|
||||
* The size in bytes of each element in the array.
|
||||
*/
|
||||
@Property(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE, where = Where.CONSTRUCTOR)
|
||||
public static final int BYTES_PER_ELEMENT = 2;
|
||||
|
||||
private static final Factory FACTORY = new Factory(BYTES_PER_ELEMENT) {
|
||||
@Override
|
||||
public ArrayBufferView construct(final NativeArrayBuffer buffer, final int byteOffset, final int length) {
|
||||
|
@ -28,7 +28,9 @@ package jdk.nashorn.internal.objects;
|
||||
import jdk.nashorn.internal.objects.annotations.Attribute;
|
||||
import jdk.nashorn.internal.objects.annotations.Constructor;
|
||||
import jdk.nashorn.internal.objects.annotations.Function;
|
||||
import jdk.nashorn.internal.objects.annotations.Property;
|
||||
import jdk.nashorn.internal.objects.annotations.ScriptClass;
|
||||
import jdk.nashorn.internal.objects.annotations.Where;
|
||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
import jdk.nashorn.internal.runtime.arrays.ArrayData;
|
||||
|
||||
@ -37,7 +39,12 @@ import jdk.nashorn.internal.runtime.arrays.ArrayData;
|
||||
*/
|
||||
@ScriptClass("Int32Array")
|
||||
public final class NativeInt32Array extends ArrayBufferView {
|
||||
private static final int BYTES_PER_ELEMENT = 4;
|
||||
/**
|
||||
* The size in bytes of each element in the array.
|
||||
*/
|
||||
@Property(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE, where = Where.CONSTRUCTOR)
|
||||
public static final int BYTES_PER_ELEMENT = 4;
|
||||
|
||||
private static final Factory FACTORY = new Factory(BYTES_PER_ELEMENT) {
|
||||
@Override
|
||||
public ArrayBufferView construct(final NativeArrayBuffer buffer, final int byteOffset, final int length) {
|
||||
|
@ -28,7 +28,9 @@ package jdk.nashorn.internal.objects;
|
||||
import jdk.nashorn.internal.objects.annotations.Attribute;
|
||||
import jdk.nashorn.internal.objects.annotations.Constructor;
|
||||
import jdk.nashorn.internal.objects.annotations.Function;
|
||||
import jdk.nashorn.internal.objects.annotations.Property;
|
||||
import jdk.nashorn.internal.objects.annotations.ScriptClass;
|
||||
import jdk.nashorn.internal.objects.annotations.Where;
|
||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
import jdk.nashorn.internal.runtime.arrays.ArrayData;
|
||||
|
||||
@ -37,7 +39,12 @@ import jdk.nashorn.internal.runtime.arrays.ArrayData;
|
||||
*/
|
||||
@ScriptClass("Int8Array")
|
||||
public final class NativeInt8Array extends ArrayBufferView {
|
||||
private static final int BYTES_PER_ELEMENT = 1;
|
||||
/**
|
||||
* The size in bytes of each element in the array.
|
||||
*/
|
||||
@Property(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE, where = Where.CONSTRUCTOR)
|
||||
public static final int BYTES_PER_ELEMENT = 1;
|
||||
|
||||
private static final Factory FACTORY = new Factory(BYTES_PER_ELEMENT) {
|
||||
@Override
|
||||
public ArrayBufferView construct(final NativeArrayBuffer buffer, final int byteOffset, final int length) {
|
||||
|
@ -30,6 +30,8 @@ import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.Collection;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
import jdk.internal.dynalink.beans.StaticClass;
|
||||
import jdk.internal.dynalink.support.TypeUtilities;
|
||||
import jdk.nashorn.internal.objects.annotations.Attribute;
|
||||
@ -37,6 +39,7 @@ import jdk.nashorn.internal.objects.annotations.Function;
|
||||
import jdk.nashorn.internal.objects.annotations.ScriptClass;
|
||||
import jdk.nashorn.internal.objects.annotations.Where;
|
||||
import jdk.nashorn.internal.runtime.JSType;
|
||||
import jdk.nashorn.internal.runtime.ListAdapter;
|
||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
import jdk.nashorn.internal.runtime.linker.JavaAdapterFactory;
|
||||
|
||||
@ -240,39 +243,56 @@ public final class NativeJava {
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a JavaScript array and a Java type, returns a Java array with the same initial contents, and with the
|
||||
* specified component type. Example:
|
||||
* Given a script object and a Java type, converts the script object into the desired Java type. Currently it
|
||||
* performs shallow creation of Java arrays, as well as wrapping of objects in Lists and Dequeues. Example:
|
||||
* <pre>
|
||||
* var anArray = [1, "13", false]
|
||||
* var javaIntArray = Java.toJavaArray(anArray, "int")
|
||||
* var javaIntArray = Java.to(anArray, "int[]")
|
||||
* print(javaIntArray[0]) // prints 1
|
||||
* print(javaIntArray[1]) // prints 13, as string "13" was converted to number 13 as per ECMAScript ToNumber conversion
|
||||
* print(javaIntArray[2]) // prints 0, as boolean false was converted to number 0 as per ECMAScript ToNumber conversion
|
||||
* </pre>
|
||||
* @param self not used
|
||||
* @param objArray the JavaScript array. Can be null.
|
||||
* @param objType either a {@link #type(Object, Object) type object} or a String describing the component type of
|
||||
* the Java array to create. Can not be null. If undefined, Object is assumed (allowing the argument to be omitted).
|
||||
* @return a Java array with the copy of JavaScript array's contents, converted to the appropriate Java component
|
||||
* type. Returns null if objArray is null.
|
||||
* @param obj the script object. Can be null.
|
||||
* @param objType either a {@link #type(Object, Object) type object} or a String describing the type of the Java
|
||||
* object to create. Can not be null. If undefined, a "default" conversion is presumed (allowing the argument to be
|
||||
* omitted).
|
||||
* @return a Java object whose value corresponds to the original script object's value. Specifically, for array
|
||||
* target types, returns a Java array of the same type with contents converted to the array's component type. Does
|
||||
* not recursively convert for multidimensional arrays. For {@link List} or {@link Deque}, returns a live wrapper
|
||||
* around the object, see {@link ListAdapter} for details. Returns null if obj is null.
|
||||
* @throws ClassNotFoundException if the class described by objType is not found
|
||||
*/
|
||||
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
|
||||
public static Object toJavaArray(final Object self, final Object objArray, final Object objType) throws ClassNotFoundException {
|
||||
final StaticClass componentType =
|
||||
objType instanceof StaticClass ?
|
||||
(StaticClass)objType :
|
||||
objType == UNDEFINED ?
|
||||
StaticClass.forClass(Object.class) :
|
||||
type(objType);
|
||||
|
||||
if (objArray == null) {
|
||||
public static Object to(final Object self, final Object obj, final Object objType) throws ClassNotFoundException {
|
||||
if (obj == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Global.checkObject(objArray);
|
||||
Global.checkObject(obj);
|
||||
|
||||
return ((ScriptObject)objArray).getArray().asArrayOfType(componentType.getRepresentedClass());
|
||||
final Class<?> targetClass;
|
||||
if(objType == UNDEFINED) {
|
||||
targetClass = Object[].class;
|
||||
} else {
|
||||
final StaticClass targetType;
|
||||
if(objType instanceof StaticClass) {
|
||||
targetType = (StaticClass)objType;
|
||||
} else {
|
||||
targetType = type(objType);
|
||||
}
|
||||
targetClass = targetType.getRepresentedClass();
|
||||
}
|
||||
|
||||
if(targetClass.isArray()) {
|
||||
return ((ScriptObject)obj).getArray().asArrayOfType(targetClass.getComponentType());
|
||||
}
|
||||
|
||||
if(targetClass == List.class || targetClass == Deque.class) {
|
||||
return new ListAdapter((ScriptObject)obj);
|
||||
}
|
||||
|
||||
throw typeError("unsupported.java.to.type", targetClass.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -283,7 +303,7 @@ public final class NativeJava {
|
||||
* <pre>
|
||||
* var File = Java.type("java.io.File")
|
||||
* var listHomeDir = new File("~").listFiles()
|
||||
* var jsListHome = Java.toJavaScriptArray(listHomeDir)
|
||||
* var jsListHome = Java.from(listHomeDir)
|
||||
* var jpegModifiedDates = jsListHome
|
||||
* .filter(function(val) { return val.getName().endsWith(".jpg") })
|
||||
* .map(function(val) { return val.lastModified() })
|
||||
@ -294,7 +314,7 @@ public final class NativeJava {
|
||||
* null.
|
||||
*/
|
||||
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
|
||||
public static Object toJavaScriptArray(final Object self, final Object objArray) {
|
||||
public static Object from(final Object self, final Object objArray) {
|
||||
if (objArray == null) {
|
||||
return null;
|
||||
} else if (objArray instanceof Collection) {
|
||||
|
@ -611,13 +611,11 @@ public final class NativeMath extends ScriptObject {
|
||||
*/
|
||||
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
|
||||
public static Object round(final Object self, final Object x) {
|
||||
if (GlobalFunctions.isNaN(self, x)) {
|
||||
return Double.NaN;
|
||||
} else if (!GlobalFunctions.isFinite(self, x)) {
|
||||
return x;
|
||||
final double d = JSType.toNumber(x);
|
||||
if (Math.getExponent(d) >= 52) {
|
||||
return d;
|
||||
}
|
||||
|
||||
return Math.round(JSType.toNumber(x));
|
||||
return Math.copySign(Math.floor(d + 0.5), d);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -38,6 +38,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import jdk.internal.dynalink.CallSiteDescriptor;
|
||||
import jdk.internal.dynalink.linker.GuardedInvocation;
|
||||
import jdk.internal.dynalink.linker.LinkRequest;
|
||||
@ -630,17 +631,24 @@ public final class NativeString extends ScriptObject {
|
||||
|
||||
final String str = checkObjectToString(self);
|
||||
final String searchStr = JSType.toString(search);
|
||||
final int length = str.length();
|
||||
|
||||
int from;
|
||||
int end;
|
||||
|
||||
if (pos == UNDEFINED) {
|
||||
from = str.length();
|
||||
end = length;
|
||||
} else {
|
||||
final double numPos = JSType.toNumber(pos);
|
||||
from = !Double.isNaN(numPos) ? (int)numPos : (int)Double.POSITIVE_INFINITY;
|
||||
end = Double.isNaN(numPos) ? length : (int)numPos;
|
||||
if (end < 0) {
|
||||
end = 0;
|
||||
} else if (end > length) {
|
||||
end = length;
|
||||
}
|
||||
}
|
||||
|
||||
return str.lastIndexOf(searchStr, from);
|
||||
|
||||
return str.lastIndexOf(searchStr, end);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -997,7 +1005,7 @@ public final class NativeString extends ScriptObject {
|
||||
*/
|
||||
@Function(attributes = Attribute.NOT_ENUMERABLE)
|
||||
public static Object toLowerCase(final Object self) {
|
||||
return checkObjectToString(self).toLowerCase();
|
||||
return checkObjectToString(self).toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1017,7 +1025,7 @@ public final class NativeString extends ScriptObject {
|
||||
*/
|
||||
@Function(attributes = Attribute.NOT_ENUMERABLE)
|
||||
public static Object toUpperCase(final Object self) {
|
||||
return checkObjectToString(self).toUpperCase();
|
||||
return checkObjectToString(self).toUpperCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -28,7 +28,9 @@ package jdk.nashorn.internal.objects;
|
||||
import jdk.nashorn.internal.objects.annotations.Attribute;
|
||||
import jdk.nashorn.internal.objects.annotations.Constructor;
|
||||
import jdk.nashorn.internal.objects.annotations.Function;
|
||||
import jdk.nashorn.internal.objects.annotations.Property;
|
||||
import jdk.nashorn.internal.objects.annotations.ScriptClass;
|
||||
import jdk.nashorn.internal.objects.annotations.Where;
|
||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
import jdk.nashorn.internal.runtime.arrays.ArrayData;
|
||||
|
||||
@ -37,7 +39,12 @@ import jdk.nashorn.internal.runtime.arrays.ArrayData;
|
||||
*/
|
||||
@ScriptClass("Uint16Array")
|
||||
public final class NativeUint16Array extends ArrayBufferView {
|
||||
private static final int BYTES_PER_ELEMENT = 2;
|
||||
/**
|
||||
* The size in bytes of each element in the array.
|
||||
*/
|
||||
@Property(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE, where = Where.CONSTRUCTOR)
|
||||
public static final int BYTES_PER_ELEMENT = 2;
|
||||
|
||||
private static final Factory FACTORY = new Factory(BYTES_PER_ELEMENT) {
|
||||
@Override
|
||||
public ArrayBufferView construct(final NativeArrayBuffer buffer, final int byteOffset, final int length) {
|
||||
|
@ -28,7 +28,9 @@ package jdk.nashorn.internal.objects;
|
||||
import jdk.nashorn.internal.objects.annotations.Attribute;
|
||||
import jdk.nashorn.internal.objects.annotations.Constructor;
|
||||
import jdk.nashorn.internal.objects.annotations.Function;
|
||||
import jdk.nashorn.internal.objects.annotations.Property;
|
||||
import jdk.nashorn.internal.objects.annotations.ScriptClass;
|
||||
import jdk.nashorn.internal.objects.annotations.Where;
|
||||
import jdk.nashorn.internal.runtime.JSType;
|
||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
import jdk.nashorn.internal.runtime.arrays.ArrayData;
|
||||
@ -38,7 +40,12 @@ import jdk.nashorn.internal.runtime.arrays.ArrayData;
|
||||
*/
|
||||
@ScriptClass("Uint32Array")
|
||||
public final class NativeUint32Array extends ArrayBufferView {
|
||||
private static final int BYTES_PER_ELEMENT = 4;
|
||||
/**
|
||||
* The size in bytes of each element in the array.
|
||||
*/
|
||||
@Property(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE, where = Where.CONSTRUCTOR)
|
||||
public static final int BYTES_PER_ELEMENT = 4;
|
||||
|
||||
private static final Factory FACTORY = new Factory(BYTES_PER_ELEMENT) {
|
||||
@Override
|
||||
public ArrayBufferView construct(final NativeArrayBuffer buffer, final int byteBegin, final int length) {
|
||||
|
@ -28,7 +28,9 @@ package jdk.nashorn.internal.objects;
|
||||
import jdk.nashorn.internal.objects.annotations.Attribute;
|
||||
import jdk.nashorn.internal.objects.annotations.Constructor;
|
||||
import jdk.nashorn.internal.objects.annotations.Function;
|
||||
import jdk.nashorn.internal.objects.annotations.Property;
|
||||
import jdk.nashorn.internal.objects.annotations.ScriptClass;
|
||||
import jdk.nashorn.internal.objects.annotations.Where;
|
||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
import jdk.nashorn.internal.runtime.arrays.ArrayData;
|
||||
|
||||
@ -37,7 +39,12 @@ import jdk.nashorn.internal.runtime.arrays.ArrayData;
|
||||
*/
|
||||
@ScriptClass("Uint8Array")
|
||||
public final class NativeUint8Array extends ArrayBufferView {
|
||||
private static final int BYTES_PER_ELEMENT = 1;
|
||||
/**
|
||||
* The size in bytes of each element in the array.
|
||||
*/
|
||||
@Property(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE, where = Where.CONSTRUCTOR)
|
||||
public static final int BYTES_PER_ELEMENT = 1;
|
||||
|
||||
private static final Factory FACTORY = new Factory(BYTES_PER_ELEMENT) {
|
||||
@Override
|
||||
public ArrayBufferView construct(final NativeArrayBuffer buffer, final int byteOffset, final int length) {
|
||||
|
@ -28,7 +28,9 @@ package jdk.nashorn.internal.objects;
|
||||
import jdk.nashorn.internal.objects.annotations.Attribute;
|
||||
import jdk.nashorn.internal.objects.annotations.Constructor;
|
||||
import jdk.nashorn.internal.objects.annotations.Function;
|
||||
import jdk.nashorn.internal.objects.annotations.Property;
|
||||
import jdk.nashorn.internal.objects.annotations.ScriptClass;
|
||||
import jdk.nashorn.internal.objects.annotations.Where;
|
||||
import jdk.nashorn.internal.runtime.JSType;
|
||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
import jdk.nashorn.internal.runtime.arrays.ArrayData;
|
||||
@ -38,7 +40,12 @@ import jdk.nashorn.internal.runtime.arrays.ArrayData;
|
||||
*/
|
||||
@ScriptClass("Uint8ClampedArray")
|
||||
public final class NativeUint8ClampedArray extends ArrayBufferView {
|
||||
private static final int BYTES_PER_ELEMENT = 1;
|
||||
/**
|
||||
* The size in bytes of each element in the array.
|
||||
*/
|
||||
@Property(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE, where = Where.CONSTRUCTOR)
|
||||
public static final int BYTES_PER_ELEMENT = 1;
|
||||
|
||||
private static final Factory FACTORY = new Factory(BYTES_PER_ELEMENT) {
|
||||
@Override
|
||||
public ArrayBufferView construct(final NativeArrayBuffer buffer, final int byteOffset, final int length) {
|
||||
|
@ -1537,7 +1537,7 @@ loop:
|
||||
|
||||
endOfLine();
|
||||
|
||||
appendStatement(new ThrowNode(throwLine, throwToken, finish, expression));
|
||||
appendStatement(new ThrowNode(throwLine, throwToken, finish, expression, 0));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1597,7 +1597,7 @@ loop:
|
||||
try {
|
||||
// Get CATCH body.
|
||||
final Block catchBody = getBlock(true);
|
||||
final CatchNode catchNode = new CatchNode(catchLine, catchToken, finish, exception, ifExpression, catchBody);
|
||||
final CatchNode catchNode = new CatchNode(catchLine, catchToken, finish, exception, ifExpression, catchBody, 0);
|
||||
appendStatement(catchNode);
|
||||
} finally {
|
||||
catchBlock = restoreBlock(catchBlock);
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
package jdk.nashorn.internal.parser;
|
||||
|
||||
import java.util.Locale;
|
||||
import static jdk.nashorn.internal.parser.TokenKind.BINARY;
|
||||
import static jdk.nashorn.internal.parser.TokenKind.BRACKET;
|
||||
import static jdk.nashorn.internal.parser.TokenKind.FUTURE;
|
||||
@ -249,7 +250,7 @@ public enum TokenType {
|
||||
}
|
||||
|
||||
public String getNameOrType() {
|
||||
return name == null ? super.name().toLowerCase() : name;
|
||||
return name == null ? super.name().toLowerCase(Locale.ENGLISH) : name;
|
||||
}
|
||||
|
||||
public TokenType getNext() {
|
||||
|
@ -75,7 +75,23 @@ public class AccessorProperty extends Property {
|
||||
|
||||
private static final MethodType[] ACCESSOR_GETTER_TYPES = new MethodType[NOOF_TYPES];
|
||||
private static final MethodType[] ACCESSOR_SETTER_TYPES = new MethodType[NOOF_TYPES];
|
||||
private static final MethodHandle SPILLGETTER = MH.asType(MH.getter(MethodHandles.lookup(), ScriptObject.class, "spill", Object[].class), Lookup.GET_OBJECT_TYPE);
|
||||
private static final MethodHandle SPILL_ELEMENT_GETTER;
|
||||
private static final MethodHandle SPILL_ELEMENT_SETTER;
|
||||
|
||||
private static final int SPILL_CACHE_SIZE = 8;
|
||||
private static final MethodHandle[] SPILL_ACCESSORS = new MethodHandle[SPILL_CACHE_SIZE * 2];
|
||||
|
||||
static {
|
||||
for (int i = 0; i < NOOF_TYPES; i++) {
|
||||
final Type type = ACCESSOR_TYPES.get(i);
|
||||
ACCESSOR_GETTER_TYPES[i] = MH.type(type.getTypeClass(), Object.class);
|
||||
ACCESSOR_SETTER_TYPES[i] = MH.type(void.class, Object.class, type.getTypeClass());
|
||||
}
|
||||
|
||||
final MethodHandle spillGetter = MH.getter(MethodHandles.lookup(), ScriptObject.class, "spill", Object[].class);
|
||||
SPILL_ELEMENT_GETTER = MH.filterArguments(MH.arrayElementGetter(Object[].class), 0, spillGetter);
|
||||
SPILL_ELEMENT_SETTER = MH.filterArguments(MH.arrayElementSetter(Object[].class), 0, spillGetter);
|
||||
}
|
||||
|
||||
/** Seed getter for the primitive version of this field (in -Dnashorn.fields.dual=true mode) */
|
||||
private MethodHandle primitiveGetter;
|
||||
@ -96,14 +112,6 @@ public class AccessorProperty extends Property {
|
||||
*/
|
||||
private Class<?> currentType;
|
||||
|
||||
static {
|
||||
for (int i = 0; i < NOOF_TYPES; i++) {
|
||||
final Type type = ACCESSOR_TYPES.get(i);
|
||||
ACCESSOR_GETTER_TYPES[i] = MH.type(type.getTypeClass(), Object.class);
|
||||
ACCESSOR_SETTER_TYPES[i] = MH.type(void.class, Object.class, type.getTypeClass());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate constructor. This is used when adding properties to the Global scope, which
|
||||
* is necessary for outermost levels in a script (the ScriptObject is represented by
|
||||
@ -114,18 +122,30 @@ public class AccessorProperty extends Property {
|
||||
* @param delegate delegate script object to rebind receiver to
|
||||
*/
|
||||
public AccessorProperty(final AccessorProperty property, final ScriptObject delegate) {
|
||||
this(property);
|
||||
super(property);
|
||||
|
||||
this.getters = new MethodHandle[NOOF_TYPES];
|
||||
|
||||
this.primitiveGetter = bindTo(primitiveGetter, delegate);
|
||||
this.primitiveSetter = bindTo(primitiveSetter, delegate);
|
||||
this.objectGetter = bindTo(objectGetter, delegate);
|
||||
this.objectSetter = bindTo(objectSetter, delegate);
|
||||
this.primitiveGetter = bindTo(property.primitiveGetter, delegate);
|
||||
this.primitiveSetter = bindTo(property.primitiveSetter, delegate);
|
||||
this.objectGetter = bindTo(property.objectGetter, delegate);
|
||||
this.objectSetter = bindTo(property.objectSetter, delegate);
|
||||
|
||||
setCurrentType(property.getCurrentType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for spill properties. Array getters and setters will be created on demand.
|
||||
*
|
||||
* @param key the property key
|
||||
* @param flags the property flags
|
||||
* @param slot spill slot
|
||||
*/
|
||||
public AccessorProperty(final String key, final int flags, final int slot) {
|
||||
super(key, flags, slot);
|
||||
assert (flags & IS_SPILL) == IS_SPILL;
|
||||
|
||||
setCurrentType(Object.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor. Similar to the constructor with both primitive getters and setters, the difference
|
||||
* here being that only one getter and setter (setter is optional for non writable fields) is given
|
||||
@ -267,8 +287,41 @@ public class AccessorProperty extends Property {
|
||||
return new AccessorProperty(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setObjectValue(final ScriptObject self, final ScriptObject owner, final Object value, final boolean strict) {
|
||||
if (isSpill()) {
|
||||
self.spill[getSlot()] = value;
|
||||
} else {
|
||||
try {
|
||||
getSetter(Object.class, self.getMap()).invokeExact((Object)self, value);
|
||||
} catch (final Error|RuntimeException e) {
|
||||
throw e;
|
||||
} catch (final Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object getObjectValue(final ScriptObject self, final ScriptObject owner) {
|
||||
if (isSpill()) {
|
||||
return self.spill[getSlot()];
|
||||
}
|
||||
|
||||
try {
|
||||
return getGetter(Object.class).invokeExact((Object)self);
|
||||
} catch (final Error|RuntimeException e) {
|
||||
throw e;
|
||||
} catch (final Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodHandle getGetter(final Class<?> type) {
|
||||
if (isSpill() && objectGetter == null) {
|
||||
objectGetter = getSpillGetter();
|
||||
}
|
||||
final int i = getAccessorTypeIndex(type);
|
||||
if (getters[i] == null) {
|
||||
getters[i] = debug(
|
||||
@ -284,7 +337,7 @@ public class AccessorProperty extends Property {
|
||||
"get");
|
||||
}
|
||||
|
||||
return isSpill() ? MH.filterArguments(getters[i], 0, SPILLGETTER) : getters[i];
|
||||
return getters[i];
|
||||
}
|
||||
|
||||
private Property getWiderProperty(final Class<?> type) {
|
||||
@ -313,6 +366,9 @@ public class AccessorProperty extends Property {
|
||||
}
|
||||
|
||||
private MethodHandle generateSetter(final Class<?> forType, final Class<?> type) {
|
||||
if (isSpill() && objectSetter == null) {
|
||||
objectSetter = getSpillSetter();
|
||||
}
|
||||
MethodHandle mh = createSetter(forType, type, primitiveSetter, objectSetter);
|
||||
mh = MH.asType(mh, ACCESSOR_SETTER_TYPES[getAccessorTypeIndex(type)]); //has to be the case for invokeexact to work in ScriptObject
|
||||
mh = debug(mh, currentType, type, "set");
|
||||
@ -343,7 +399,7 @@ public class AccessorProperty extends Property {
|
||||
mh = generateSetter(forType, type);
|
||||
}
|
||||
|
||||
return isSpill() ? MH.filterArguments(mh, 0, SPILLGETTER) : mh;
|
||||
return mh;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -363,6 +419,30 @@ public class AccessorProperty extends Property {
|
||||
setCurrentType(newType);
|
||||
}
|
||||
|
||||
private MethodHandle getSpillGetter() {
|
||||
final int slot = getSlot();
|
||||
MethodHandle getter = slot < SPILL_CACHE_SIZE ? SPILL_ACCESSORS[slot * 2] : null;
|
||||
if (getter == null) {
|
||||
getter = MH.asType(MH.insertArguments(SPILL_ELEMENT_GETTER, 1, slot), Lookup.GET_OBJECT_TYPE);
|
||||
if (slot < SPILL_CACHE_SIZE) {
|
||||
SPILL_ACCESSORS[slot * 2] = getter;
|
||||
}
|
||||
}
|
||||
return getter;
|
||||
}
|
||||
|
||||
private MethodHandle getSpillSetter() {
|
||||
final int slot = getSlot();
|
||||
MethodHandle setter = slot < SPILL_CACHE_SIZE ? SPILL_ACCESSORS[slot * 2 + 1] : null;
|
||||
if (setter == null) {
|
||||
setter = MH.asType(MH.insertArguments(SPILL_ELEMENT_SETTER, 1, slot), Lookup.SET_OBJECT_TYPE);
|
||||
if (slot < SPILL_CACHE_SIZE) {
|
||||
SPILL_ACCESSORS[slot * 2 + 1] = setter;
|
||||
}
|
||||
}
|
||||
return setter;
|
||||
}
|
||||
|
||||
private static void finest(final String str) {
|
||||
if (DEBUG_FIELDS) {
|
||||
LOG.finest(str);
|
||||
|
@ -35,21 +35,27 @@ import jdk.nashorn.internal.codegen.types.Type;
|
||||
*/
|
||||
final class CompiledFunction implements Comparable<CompiledFunction> {
|
||||
|
||||
/** The method type may be more specific than the invoker, if. e.g.
|
||||
* the invoker is guarded, and a guard with a generic object only
|
||||
* fallback, while the target is more specific, we still need the
|
||||
* more specific type for sorting */
|
||||
private final MethodType type;
|
||||
private final MethodHandle invoker;
|
||||
private MethodHandle constructor;
|
||||
|
||||
CompiledFunction(final MethodHandle invoker) {
|
||||
this(invoker, null);
|
||||
CompiledFunction(final MethodType type, final MethodHandle invoker) {
|
||||
this(type, invoker, null);
|
||||
}
|
||||
|
||||
CompiledFunction(final MethodHandle invoker, final MethodHandle constructor) {
|
||||
this.invoker = invoker;
|
||||
this.constructor = constructor; //isConstructor
|
||||
CompiledFunction(final MethodType type, final MethodHandle invoker, final MethodHandle constructor) {
|
||||
this.type = type;
|
||||
this.invoker = invoker;
|
||||
this.constructor = constructor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "<invoker=" + invoker + " ctor=" + constructor + ">";
|
||||
return "<callSiteType= " + type + " invoker=" + invoker + " ctor=" + constructor + ">";
|
||||
}
|
||||
|
||||
MethodHandle getInvoker() {
|
||||
@ -69,7 +75,7 @@ final class CompiledFunction implements Comparable<CompiledFunction> {
|
||||
}
|
||||
|
||||
MethodType type() {
|
||||
return invoker.type();
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -103,8 +109,8 @@ final class CompiledFunction implements Comparable<CompiledFunction> {
|
||||
return weight() > o.weight();
|
||||
}
|
||||
|
||||
boolean moreGenericThan(final MethodType type) {
|
||||
return weight() > weight(type);
|
||||
boolean moreGenericThan(final MethodType mt) {
|
||||
return weight() > weight(mt);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -112,15 +118,15 @@ final class CompiledFunction implements Comparable<CompiledFunction> {
|
||||
* It is compatible if the types are narrower than the invocation type so that
|
||||
* a semantically equivalent linkage can be performed.
|
||||
*
|
||||
* @param typesc
|
||||
* @param mt type to check against
|
||||
* @return
|
||||
*/
|
||||
boolean typeCompatible(final MethodType type) {
|
||||
final Class<?>[] wantedParams = type.parameterArray();
|
||||
boolean typeCompatible(final MethodType mt) {
|
||||
final Class<?>[] wantedParams = mt.parameterArray();
|
||||
final Class<?>[] existingParams = type().parameterArray();
|
||||
|
||||
//if we are not examining a varargs type, the number of parameters must be the same
|
||||
if (wantedParams.length != existingParams.length && !isVarArgsType(type)) {
|
||||
if (wantedParams.length != existingParams.length && !isVarArgsType(mt)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,6 @@ import jdk.nashorn.internal.runtime.options.Options;
|
||||
*/
|
||||
|
||||
public final class DebugLogger {
|
||||
@SuppressWarnings("NonConstantLogger")
|
||||
private final Logger logger;
|
||||
private final boolean isEnabled;
|
||||
|
||||
|
@ -78,9 +78,9 @@ public final class FinalScriptFunctionData extends ScriptFunctionData {
|
||||
//only nasgen constructors: (boolean, self, args) are subject to binding a boolean newObj. isConstructor
|
||||
//is too conservative a check. However, isConstructor(mh) always implies isConstructor param
|
||||
assert isConstructor();
|
||||
code.add(new CompiledFunction(MH.insertArguments(mh, 0, false), composeConstructor(MH.insertArguments(mh, 0, true), needsCallee))); //make sure callee state can be determined when we reach constructor
|
||||
code.add(new CompiledFunction(mh.type(), MH.insertArguments(mh, 0, false), composeConstructor(MH.insertArguments(mh, 0, true), needsCallee))); //make sure callee state can be determined when we reach constructor
|
||||
} else {
|
||||
code.add(new CompiledFunction(mh));
|
||||
code.add(new CompiledFunction(mh.type(), mh));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -153,5 +153,24 @@ public final class FindProperty {
|
||||
return prototype.isScope();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the property value from self as object.
|
||||
*
|
||||
* @return the property value
|
||||
*/
|
||||
public Object getObjectValue() {
|
||||
return property.getObjectValue(getGetterReceiver(), getOwner());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the property value in self.
|
||||
*
|
||||
* @param value the new value
|
||||
* @param strict strict flag
|
||||
*/
|
||||
public void setObjectValue(final Object value, final boolean strict) {
|
||||
property.setObjectValue(getSetterReceiver(), getOwner(), value, strict);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,7 @@ import static jdk.nashorn.internal.lookup.Lookup.MH;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Utilities used by Global class.
|
||||
@ -373,10 +374,10 @@ loop:
|
||||
} else if (ch < 256) {
|
||||
sb.append('%');
|
||||
final byte b = (byte)ch;
|
||||
sb.append(Integer.toHexString(b & 0xFF).toUpperCase());
|
||||
sb.append(Integer.toHexString(b & 0xFF).toUpperCase(Locale.ENGLISH));
|
||||
} else {
|
||||
sb.append("%u");
|
||||
sb.append(Integer.toHexString(ch & 0xFFFF).toUpperCase());
|
||||
sb.append(Integer.toHexString(ch & 0xFFFF).toUpperCase(Locale.ENGLISH));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,8 @@ import jdk.nashorn.internal.ir.UnaryNode;
|
||||
import jdk.nashorn.internal.parser.JSONParser;
|
||||
import jdk.nashorn.internal.parser.TokenType;
|
||||
import jdk.nashorn.internal.runtime.linker.Bootstrap;
|
||||
import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.getArrayIndexNoThrow;
|
||||
import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex;
|
||||
|
||||
/**
|
||||
* Utilities used by "JSON" object implementation.
|
||||
@ -94,7 +96,7 @@ public final class JSONFunctions {
|
||||
if (reviver instanceof ScriptFunction) {
|
||||
assert global instanceof GlobalObject;
|
||||
final ScriptObject root = ((GlobalObject)global).newObject();
|
||||
root.set("", unfiltered, root.isStrictContext());
|
||||
root.addOwnProperty("", Property.WRITABLE_ENUMERABLE_CONFIGURABLE, unfiltered);
|
||||
return walk(root, "", (ScriptFunction)reviver);
|
||||
}
|
||||
return unfiltered;
|
||||
@ -115,7 +117,7 @@ public final class JSONFunctions {
|
||||
if (newElement == ScriptRuntime.UNDEFINED) {
|
||||
valueObj.delete(key, strict);
|
||||
} else {
|
||||
valueObj.set(key, newElement, strict);
|
||||
setPropertyValue(valueObj, key, newElement, strict);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -175,7 +177,9 @@ public final class JSONFunctions {
|
||||
final PropertyNode pNode = (PropertyNode) elem;
|
||||
final Node valueNode = pNode.getValue();
|
||||
|
||||
object.set(pNode.getKeyName(), convertNode(global, valueNode), strict);
|
||||
final String name = pNode.getKeyName();
|
||||
final Object value = convertNode(global, valueNode);
|
||||
setPropertyValue(object, name, value, strict);
|
||||
}
|
||||
|
||||
return object;
|
||||
@ -188,6 +192,21 @@ public final class JSONFunctions {
|
||||
}
|
||||
}
|
||||
|
||||
// add a new property if does not exist already, or else set old property
|
||||
private static void setPropertyValue(final ScriptObject sobj, final String name, final Object value, final boolean strict) {
|
||||
final int index = getArrayIndexNoThrow(name);
|
||||
if (isValidArrayIndex(index)) {
|
||||
// array index key
|
||||
sobj.defineOwnProperty(index, value);
|
||||
} else if (sobj.getMap().findProperty(name) != null) {
|
||||
// pre-existing non-inherited property, call set
|
||||
sobj.set(name, value, strict);
|
||||
} else {
|
||||
// add new property
|
||||
sobj.addOwnProperty(name, Property.WRITABLE_ENUMERABLE_CONFIGURABLE, value);
|
||||
}
|
||||
}
|
||||
|
||||
// does the given IR node represent a numeric array?
|
||||
private static boolean isNumericArray(final Node[] values) {
|
||||
for (final Node node : values) {
|
||||
|
@ -28,6 +28,7 @@ package jdk.nashorn.internal.runtime;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
|
||||
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
|
||||
|
||||
import java.util.Locale;
|
||||
import jdk.internal.dynalink.beans.StaticClass;
|
||||
import jdk.nashorn.internal.codegen.CompilerConstants.Call;
|
||||
import jdk.nashorn.internal.parser.Lexer;
|
||||
@ -111,7 +112,7 @@ public enum JSType {
|
||||
*/
|
||||
public final String typeName() {
|
||||
// For NULL, "object" has to be returned!
|
||||
return ((this == NULL) ? OBJECT : this).name().toLowerCase();
|
||||
return ((this == NULL) ? OBJECT : this).name().toLowerCase(Locale.ENGLISH);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -565,8 +566,11 @@ public enum JSType {
|
||||
}
|
||||
|
||||
/**
|
||||
* JavaScript compliant Object to integer conversion
|
||||
* See ECMA 9.4 ToInteger
|
||||
* JavaScript compliant Object to integer conversion. See ECMA 9.4 ToInteger
|
||||
*
|
||||
* <p>Note that this returns {@link java.lang.Integer#MAX_VALUE} or {@link java.lang.Integer#MIN_VALUE}
|
||||
* for double values that exceed the int range, including positive and negative Infinity. It is the
|
||||
* caller's responsibility to handle such values correctly.</p>
|
||||
*
|
||||
* @param obj an object
|
||||
* @return an integer
|
||||
@ -576,8 +580,11 @@ public enum JSType {
|
||||
}
|
||||
|
||||
/**
|
||||
* JavaScript compliant Object to long conversion
|
||||
* See ECMA 9.4 ToInteger
|
||||
* JavaScript compliant Object to long conversion. See ECMA 9.4 ToInteger
|
||||
*
|
||||
* <p>Note that this returns {@link java.lang.Long#MAX_VALUE} or {@link java.lang.Long#MIN_VALUE}
|
||||
* for double values that exceed the long range, including positive and negative Infinity. It is the
|
||||
* caller's responsibility to handle such values correctly.</p>
|
||||
*
|
||||
* @param obj an object
|
||||
* @return a long
|
||||
|
337
nashorn/src/jdk/nashorn/internal/runtime/ListAdapter.java
Normal file
337
nashorn/src/jdk/nashorn/internal/runtime/ListAdapter.java
Normal file
@ -0,0 +1,337 @@
|
||||
package jdk.nashorn.internal.runtime;
|
||||
|
||||
import java.util.AbstractList;
|
||||
import java.util.Deque;
|
||||
import java.util.Iterator;
|
||||
import java.util.ListIterator;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.RandomAccess;
|
||||
import jdk.nashorn.internal.runtime.linker.InvokeByName;
|
||||
|
||||
/**
|
||||
* An adapter that can wrap any ECMAScript Array-like object (that adheres to the array rules for the property
|
||||
* {@code length} and having conforming {@code push}, {@code pop}, {@code shift}, {@code unshift}, and {@code splice}
|
||||
* methods) and expose it as both a Java list and double-ended queue. While script arrays aren't necessarily efficient
|
||||
* as dequeues, it's still slightly more efficient to be able to translate dequeue operations into pushes, pops, shifts,
|
||||
* and unshifts, than to blindly translate all list's add/remove operations into splices. Also, it is conceivable that a
|
||||
* custom script object that implements an Array-like API can have a background data representation that is optimized
|
||||
* for dequeue-like access. Note that with ECMAScript arrays, {@code push} and {@pop} operate at the end of the array,
|
||||
* while in Java {@code Deque} they operate on the front of the queue and as such the Java dequeue {@link #push(Object)}
|
||||
* and {@link #pop()} operations will translate to {@code unshift} and {@code shift} script operations respectively,
|
||||
* while {@link #addLast(Object)} and {@link #removeLast()} will translate to {@code push} and {@code pop}.
|
||||
*/
|
||||
public class ListAdapter extends AbstractList<Object> implements RandomAccess, Deque<Object> {
|
||||
// These add to the back and front of the list
|
||||
private static final InvokeByName PUSH = new InvokeByName("push", ScriptObject.class, void.class, Object.class);
|
||||
private static final InvokeByName UNSHIFT = new InvokeByName("unshift", ScriptObject.class, void.class, Object.class);
|
||||
|
||||
// These remove from the back and front of the list
|
||||
private static final InvokeByName POP = new InvokeByName("pop", ScriptObject.class, Object.class);
|
||||
private static final InvokeByName SHIFT = new InvokeByName("shift", ScriptObject.class, Object.class);
|
||||
|
||||
// These insert and remove in the middle of the list
|
||||
private static final InvokeByName SPLICE_ADD = new InvokeByName("splice", ScriptObject.class, void.class, int.class, int.class, Object.class);
|
||||
private static final InvokeByName SPLICE_REMOVE = new InvokeByName("splice", ScriptObject.class, void.class, int.class, int.class);
|
||||
|
||||
private final ScriptObject obj;
|
||||
|
||||
/**
|
||||
* Creates a new list wrapper for the specified script object.
|
||||
* @param obj script the object to wrap
|
||||
*/
|
||||
public ListAdapter(ScriptObject obj) {
|
||||
this.obj = obj;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return JSType.toInt32(obj.getLength());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get(int index) {
|
||||
checkRange(index);
|
||||
return obj.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object set(int index, Object element) {
|
||||
checkRange(index);
|
||||
final Object prevValue = get(index);
|
||||
obj.set(index, element, false);
|
||||
return prevValue;
|
||||
}
|
||||
|
||||
private void checkRange(int index) {
|
||||
if(index < 0 || index >= size()) {
|
||||
throw invalidIndex(index);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void push(Object e) {
|
||||
addFirst(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(Object e) {
|
||||
addLast(e);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addFirst(Object e) {
|
||||
try {
|
||||
final Object fn = UNSHIFT.getGetter().invokeExact(obj);
|
||||
checkFunction(fn, UNSHIFT);
|
||||
UNSHIFT.getInvoker().invokeExact(fn, obj, e);
|
||||
} catch(RuntimeException | Error ex) {
|
||||
throw ex;
|
||||
} catch(Throwable t) {
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLast(Object e) {
|
||||
try {
|
||||
final Object fn = PUSH.getGetter().invokeExact(obj);
|
||||
checkFunction(fn, PUSH);
|
||||
PUSH.getInvoker().invokeExact(fn, obj, e);
|
||||
} catch(RuntimeException | Error ex) {
|
||||
throw ex;
|
||||
} catch(Throwable t) {
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(int index, Object e) {
|
||||
try {
|
||||
if(index < 0) {
|
||||
throw invalidIndex(index);
|
||||
} else if(index == 0) {
|
||||
addFirst(e);
|
||||
} else {
|
||||
final int size = size();
|
||||
if(index < size) {
|
||||
final Object fn = SPLICE_ADD.getGetter().invokeExact(obj);
|
||||
checkFunction(fn, SPLICE_ADD);
|
||||
SPLICE_ADD.getInvoker().invokeExact(fn, obj, index, 0, e);
|
||||
} else if(index == size) {
|
||||
addLast(e);
|
||||
} else {
|
||||
throw invalidIndex(index);
|
||||
}
|
||||
}
|
||||
} catch(RuntimeException | Error ex) {
|
||||
throw ex;
|
||||
} catch(Throwable t) {
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
}
|
||||
private static void checkFunction(Object fn, InvokeByName invoke) {
|
||||
if(!(fn instanceof ScriptFunction)) {
|
||||
throw new UnsupportedOperationException("The script object doesn't have a function named " + invoke.getName());
|
||||
}
|
||||
}
|
||||
|
||||
private static IndexOutOfBoundsException invalidIndex(int index) {
|
||||
return new IndexOutOfBoundsException(String.valueOf(index));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean offer(Object e) {
|
||||
return offerLast(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean offerFirst(Object e) {
|
||||
addFirst(e);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean offerLast(Object e) {
|
||||
addLast(e);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object pop() {
|
||||
return removeFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object remove() {
|
||||
return removeFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object removeFirst() {
|
||||
checkNonEmpty();
|
||||
return invokeShift();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object removeLast() {
|
||||
checkNonEmpty();
|
||||
return invokePop();
|
||||
}
|
||||
|
||||
private void checkNonEmpty() {
|
||||
if(isEmpty()) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object remove(int index) {
|
||||
if(index < 0) {
|
||||
throw invalidIndex(index);
|
||||
} else if (index == 0) {
|
||||
return invokeShift();
|
||||
} else {
|
||||
final int maxIndex = size() - 1;
|
||||
if(index < maxIndex) {
|
||||
final Object prevValue = get(index);
|
||||
invokeSpliceRemove(index, 1);
|
||||
return prevValue;
|
||||
} else if(index == maxIndex) {
|
||||
return invokePop();
|
||||
} else {
|
||||
throw invalidIndex(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Object invokeShift() {
|
||||
try {
|
||||
final Object fn = SHIFT.getGetter().invokeExact(obj);
|
||||
checkFunction(fn, SHIFT);
|
||||
return SHIFT.getInvoker().invokeExact(fn, obj);
|
||||
} catch(RuntimeException | Error ex) {
|
||||
throw ex;
|
||||
} catch(Throwable t) {
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
}
|
||||
|
||||
private Object invokePop() {
|
||||
try {
|
||||
final Object fn = POP.getGetter().invokeExact(obj);
|
||||
checkFunction(fn, POP);
|
||||
return POP.getInvoker().invokeExact(fn, obj);
|
||||
} catch(RuntimeException | Error ex) {
|
||||
throw ex;
|
||||
} catch(Throwable t) {
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removeRange(int fromIndex, int toIndex) {
|
||||
invokeSpliceRemove(fromIndex, toIndex - fromIndex);
|
||||
}
|
||||
|
||||
private void invokeSpliceRemove(int fromIndex, int count) {
|
||||
try {
|
||||
final Object fn = SPLICE_REMOVE.getGetter().invokeExact(obj);
|
||||
checkFunction(fn, SPLICE_REMOVE);
|
||||
SPLICE_REMOVE.getInvoker().invokeExact(fn, obj, fromIndex, count);
|
||||
} catch(RuntimeException | Error ex) {
|
||||
throw ex;
|
||||
} catch(Throwable t) {
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object poll() {
|
||||
return pollFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object pollFirst() {
|
||||
return isEmpty() ? null : invokeShift();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object pollLast() {
|
||||
return isEmpty() ? null : invokePop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object peek() {
|
||||
return peekFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object peekFirst() {
|
||||
return isEmpty() ? null : get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object peekLast() {
|
||||
return isEmpty() ? null : get(size() - 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object element() {
|
||||
return getFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getFirst() {
|
||||
checkNonEmpty();
|
||||
return get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getLast() {
|
||||
checkNonEmpty();
|
||||
return get(size() - 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Object> descendingIterator() {
|
||||
final ListIterator<Object> it = listIterator(size());
|
||||
return new Iterator<Object>() {
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return it.hasPrevious();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object next() {
|
||||
return it.previous();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
it.remove();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeFirstOccurrence(Object o) {
|
||||
return removeOccurrence(o, iterator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeLastOccurrence(Object o) {
|
||||
return removeOccurrence(o, descendingIterator());
|
||||
}
|
||||
|
||||
private static boolean removeOccurrence(Object o, Iterator<Object> it) {
|
||||
while(it.hasNext()) {
|
||||
final Object e = it.next();
|
||||
if(o == null ? e == null : o.equals(e)) {
|
||||
it.remove();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@ -26,6 +26,7 @@
|
||||
package jdk.nashorn.internal.runtime;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.logging.ConsoleHandler;
|
||||
@ -117,7 +118,7 @@ public final class Logging {
|
||||
if ("".equals(value)) {
|
||||
level = Level.INFO;
|
||||
} else {
|
||||
level = Level.parse(value.toUpperCase());
|
||||
level = Level.parse(value.toUpperCase(Locale.ENGLISH));
|
||||
}
|
||||
|
||||
final String name = Logging.lastPart(key);
|
||||
|
@ -25,10 +25,16 @@
|
||||
|
||||
package jdk.nashorn.internal.runtime;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import jdk.internal.dynalink.CallSiteDescriptor;
|
||||
import jdk.internal.dynalink.beans.StaticClass;
|
||||
import jdk.internal.dynalink.linker.GuardedInvocation;
|
||||
import jdk.internal.dynalink.linker.LinkRequest;
|
||||
import jdk.internal.dynalink.support.Guards;
|
||||
import jdk.nashorn.internal.lookup.MethodHandleFactory;
|
||||
import jdk.nashorn.internal.lookup.MethodHandleFunctionality;
|
||||
import jdk.nashorn.internal.objects.NativeJava;
|
||||
import jdk.nashorn.internal.objects.annotations.Attribute;
|
||||
import jdk.nashorn.internal.objects.annotations.Function;
|
||||
@ -65,6 +71,10 @@ import jdk.nashorn.internal.objects.annotations.Function;
|
||||
* </pre>
|
||||
*/
|
||||
public final class NativeJavaPackage extends ScriptObject {
|
||||
private static final MethodHandleFunctionality MH = MethodHandleFactory.getFunctionality();
|
||||
private static final MethodHandle CLASS_NOT_FOUND = findOwnMH("classNotFound", Void.TYPE, NativeJavaPackage.class);
|
||||
private static final MethodHandle TYPE_GUARD = Guards.getClassGuard(NativeJavaPackage.class);
|
||||
|
||||
/** Full name of package (includes path.) */
|
||||
private final String name;
|
||||
|
||||
@ -123,6 +133,30 @@ public final class NativeJavaPackage extends ScriptObject {
|
||||
return super.getDefaultValue(hint);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GuardedInvocation findNewMethod(CallSiteDescriptor desc) {
|
||||
return createClassNotFoundInvocation(desc);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GuardedInvocation findCallMethod(CallSiteDescriptor desc, LinkRequest request) {
|
||||
return createClassNotFoundInvocation(desc);
|
||||
}
|
||||
|
||||
private static GuardedInvocation createClassNotFoundInvocation(final CallSiteDescriptor desc) {
|
||||
// If NativeJavaPackage is invoked either as a constructor or as a function, throw a ClassNotFoundException as
|
||||
// we can assume the user attempted to instantiate a non-existent class.
|
||||
final MethodType type = desc.getMethodType();
|
||||
return new GuardedInvocation(
|
||||
MH.dropArguments(CLASS_NOT_FOUND, 1, type.parameterList().subList(1, type.parameterCount())),
|
||||
type.parameterType(0) == NativeJavaPackage.class ? null : TYPE_GUARD);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static void classNotFound(final NativeJavaPackage pkg) throws ClassNotFoundException {
|
||||
throw new ClassNotFoundException(pkg.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* "No such property" call placeholder.
|
||||
*
|
||||
@ -188,4 +222,7 @@ public final class NativeJavaPackage extends ScriptObject {
|
||||
return noSuchProperty(desc, request);
|
||||
}
|
||||
|
||||
private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
|
||||
return MH.findStatic(MethodHandles.lookup(), NativeJavaPackage.class, name, MH.type(rtype, types));
|
||||
}
|
||||
}
|
||||
|
@ -52,6 +52,9 @@ public abstract class Property {
|
||||
* we can use leave flag byte initialized with (the default) zero value.
|
||||
*/
|
||||
|
||||
/** Mask for property being both writable, enumerable and configurable */
|
||||
public static final int WRITABLE_ENUMERABLE_CONFIGURABLE = 0b0000_0000_0000;
|
||||
|
||||
/** ECMA 8.6.1 - Is this property not writable? */
|
||||
public static final int NOT_WRITABLE = 0b0000_0000_0001;
|
||||
|
||||
@ -351,6 +354,27 @@ public abstract class Property {
|
||||
return slot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of this property in {@code owner}. This allows to bypass creation of the
|
||||
* setter MethodHandle for spill and user accessor properties.
|
||||
*
|
||||
* @param self the this object
|
||||
* @param owner the owner object
|
||||
* @param value the new property value
|
||||
* @param strict is this a strict setter?
|
||||
*/
|
||||
protected abstract void setObjectValue(ScriptObject self, ScriptObject owner, Object value, boolean strict);
|
||||
|
||||
/**
|
||||
* Set the Object value of this property from {@code owner}. This allows to bypass creation of the
|
||||
* getter MethodHandle for spill and user accessor properties.
|
||||
*
|
||||
* @param self the this object
|
||||
* @param owner the owner object
|
||||
* @return the property value
|
||||
*/
|
||||
protected abstract Object getObjectValue(ScriptObject self, ScriptObject owner);
|
||||
|
||||
/**
|
||||
* Abstract method for retrieving the setter for the property. We do not know
|
||||
* anything about the internal representation when we request the setter, we only
|
||||
|
@ -30,6 +30,8 @@ import static jdk.nashorn.internal.lookup.Lookup.MH;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import jdk.nashorn.internal.codegen.Compiler;
|
||||
@ -49,9 +51,16 @@ import jdk.nashorn.internal.parser.TokenType;
|
||||
*/
|
||||
public final class RecompilableScriptFunctionData extends ScriptFunctionData {
|
||||
|
||||
/** FunctionNode with the code for this ScriptFunction */
|
||||
private FunctionNode functionNode;
|
||||
private final PropertyMap allocatorMap;
|
||||
|
||||
/** Allocator map from makeMap() */
|
||||
private final PropertyMap allocatorMap;
|
||||
|
||||
/** Code installer used for all further recompilation/specialization of this ScriptFunction */
|
||||
private final CodeInstaller<ScriptEnvironment> installer;
|
||||
|
||||
/** Name of class where allocator function resides */
|
||||
private final String allocatorClassName;
|
||||
|
||||
/** lazily generated allocator */
|
||||
@ -59,6 +68,23 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
|
||||
|
||||
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
|
||||
|
||||
/**
|
||||
* Used for specialization based on runtime arguments. Whenever we specialize on
|
||||
* callsite parameter types at runtime, we need to use a parameter type guard to
|
||||
* ensure that the specialized version of the script function continues to be
|
||||
* applicable for a particular callsite *
|
||||
*/
|
||||
private static final MethodHandle PARAM_TYPE_GUARD = findOwnMH("paramTypeGuard", boolean.class, Type[].class, Object[].class);
|
||||
|
||||
/**
|
||||
* It is usually a good gamble whever we detect a runtime callsite with a double
|
||||
* (or java.lang.Number instance) to specialize the parameter to an integer, if the
|
||||
* parameter in question can be represented as one. The double typically only exists
|
||||
* because the compiler doesn't know any better than "a number type" and conservatively
|
||||
* picks doubles when it can't prove that an integer addition wouldn't overflow
|
||||
*/
|
||||
private static final MethodHandle ENSURE_INT = findOwnMH("ensureInt", int.class, Object.class);
|
||||
|
||||
/**
|
||||
* Constructor - public as scripts use it
|
||||
*
|
||||
@ -141,14 +167,6 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
|
||||
return; // nothing to do, we have code, at least some.
|
||||
}
|
||||
|
||||
// check if function node is lazy, need to compile it.
|
||||
// note that currently function cloning is not working completely, which
|
||||
// means that the compiler will mutate the function node it has been given
|
||||
// once it has been compiled, it cannot be recompiled. This means that
|
||||
// lazy compilation works (not compiled yet) but e.g. specializations won't
|
||||
// until the copy-on-write changes for IR are in, making cloning meaningless.
|
||||
// therefore, currently method specialization is disabled. TODO
|
||||
|
||||
if (functionNode.isLazy()) {
|
||||
Compiler.LOG.info("Trampoline hit: need to do lazy compilation of '", functionNode.getName(), "'");
|
||||
final Compiler compiler = new Compiler(installer);
|
||||
@ -156,38 +174,55 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
|
||||
assert !functionNode.isLazy();
|
||||
compiler.install(functionNode);
|
||||
|
||||
// we don't need to update any flags - varArgs and needsCallee are instrincic
|
||||
// in the function world we need to get a destination node from the compile instead
|
||||
// and replace it with our function node. TODO
|
||||
/*
|
||||
* We don't need to update any flags - varArgs and needsCallee are instrincic
|
||||
* in the function world we need to get a destination node from the compile instead
|
||||
* and replace it with our function node. TODO
|
||||
*/
|
||||
}
|
||||
|
||||
// we can't get here unless we have bytecode, either from eager compilation or from
|
||||
// running a lazy compile on the lines above
|
||||
/*
|
||||
* We can't get to this program point unless we have bytecode, either from
|
||||
* eager compilation or from running a lazy compile on the lines above
|
||||
*/
|
||||
|
||||
assert functionNode.hasState(CompilationState.EMITTED) : functionNode.getName() + " " + functionNode.getState() + " " + Debug.id(functionNode);
|
||||
|
||||
// code exists - look it up and add it into the automatically sorted invoker list
|
||||
addCode(functionNode, null, null);
|
||||
addCode(functionNode);
|
||||
}
|
||||
|
||||
private MethodHandle addCode(final FunctionNode fn, final MethodHandle guard, final MethodHandle fallback) {
|
||||
final MethodHandle target =
|
||||
private MethodHandle addCode(final FunctionNode fn) {
|
||||
return addCode(fn, null, null, null);
|
||||
}
|
||||
|
||||
private MethodHandle addCode(final FunctionNode fn, final MethodType runtimeType, final MethodHandle guard, final MethodHandle fallback) {
|
||||
final MethodType targetType = new FunctionSignature(fn).getMethodType();
|
||||
MethodHandle target =
|
||||
MH.findStatic(
|
||||
LOOKUP,
|
||||
fn.getCompileUnit().getCode(),
|
||||
fn.getName(),
|
||||
new FunctionSignature(fn).
|
||||
getMethodType());
|
||||
MethodHandle mh = target;
|
||||
if (guard != null) {
|
||||
try {
|
||||
mh = MH.guardWithTest(MH.asCollector(guard, Object[].class, target.type().parameterCount()), MH.asType(target, fallback.type()), fallback);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
targetType);
|
||||
|
||||
/*
|
||||
* For any integer argument. a double that is representable as an integer is OK.
|
||||
* otherwise the guard would have failed. in that case introduce a filter that
|
||||
* casts the double to an integer, which we know will preserve all precision.
|
||||
*/
|
||||
for (int i = 0; i < targetType.parameterCount(); i++) {
|
||||
if (targetType.parameterType(i) == int.class) {
|
||||
//representable as int
|
||||
target = MH.filterArguments(target, i, ENSURE_INT);
|
||||
}
|
||||
}
|
||||
|
||||
final CompiledFunction cf = new CompiledFunction(mh);
|
||||
MethodHandle mh = target;
|
||||
if (guard != null) {
|
||||
mh = MH.guardWithTest(MH.asCollector(guard, Object[].class, target.type().parameterCount()), MH.asType(target, fallback.type()), fallback);
|
||||
}
|
||||
|
||||
final CompiledFunction cf = new CompiledFunction(runtimeType == null ? targetType : runtimeType, mh);
|
||||
code.add(cf);
|
||||
|
||||
return cf.getInvoker();
|
||||
@ -212,69 +247,162 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
|
||||
return Type.OBJECT;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static boolean paramTypeGuard(final Type[] compileTimeTypes, final Type[] runtimeTypes, Object... args) {
|
||||
//System.err.println("Param type guard " + Arrays.asList(args));
|
||||
private static boolean canCoerce(final Object arg, final Type type) {
|
||||
Type argType = runtimeType(arg);
|
||||
if (Type.widest(argType, type) == type || arg == ScriptRuntime.UNDEFINED) {
|
||||
return true;
|
||||
}
|
||||
System.err.println(arg + " does not fit in "+ argType + " " + type + " " + arg.getClass());
|
||||
new Throwable().printStackTrace();
|
||||
return false;
|
||||
}
|
||||
|
||||
private static final MethodHandle PARAM_TYPE_GUARD = findOwnMH("paramTypeGuard", boolean.class, Type[].class, Type[].class, Object[].class);
|
||||
@SuppressWarnings("unused")
|
||||
private static boolean paramTypeGuard(final Type[] paramTypes, final Object... args) {
|
||||
final int length = args.length;
|
||||
assert args.length >= paramTypes.length;
|
||||
|
||||
//i==start, skip the this, callee params etc
|
||||
int start = args.length - paramTypes.length;
|
||||
for (int i = start; i < args.length; i++) {
|
||||
final Object arg = args[i];
|
||||
if (!canCoerce(arg, paramTypes[i - start])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static int ensureInt(final Object arg) {
|
||||
if (arg instanceof Number) {
|
||||
return ((Number)arg).intValue();
|
||||
} else if (arg instanceof Undefined) {
|
||||
return 0;
|
||||
}
|
||||
throw new AssertionError(arg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the runtime callsite args, compute a method type that is equivalent to what
|
||||
* was passed - this is typically a lot more specific that what the compiler has been
|
||||
* able to deduce
|
||||
* @param callSiteType callsite type for the compiled callsite target
|
||||
* @param args runtime arguments to the compiled callsite target
|
||||
* @return adjusted method type, narrowed as to conform to runtime callsite type instead
|
||||
*/
|
||||
private static MethodType runtimeType(final MethodType callSiteType, final Object[] args) {
|
||||
if (args == null) {
|
||||
//for example bound, or otherwise runtime arguments to callsite unavailable, then
|
||||
//do not change the type
|
||||
return callSiteType;
|
||||
}
|
||||
final Class<?>[] paramTypes = new Class<?>[callSiteType.parameterCount()];
|
||||
final int start = args.length - callSiteType.parameterCount();
|
||||
for (int i = start; i < args.length; i++) {
|
||||
paramTypes[i - start] = runtimeType(args[i]).getTypeClass();
|
||||
}
|
||||
return MH.type(callSiteType.returnType(), paramTypes);
|
||||
}
|
||||
|
||||
private static ArrayList<Type> runtimeType(final MethodType mt) {
|
||||
final ArrayList<Type> type = new ArrayList<>();
|
||||
for (int i = 0; i < mt.parameterCount(); i++) {
|
||||
type.add(Type.typeFor(mt.parameterType(i)));
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
MethodHandle getBestInvoker(final MethodType callSiteType, final Object[] args) {
|
||||
final MethodHandle mh = super.getBestInvoker(callSiteType, args);
|
||||
final MethodType runtimeType = runtimeType(callSiteType, args);
|
||||
assert runtimeType.parameterCount() == callSiteType.parameterCount();
|
||||
|
||||
if (!functionNode.canSpecialize() || !code.isLessSpecificThan(callSiteType)) {
|
||||
final MethodHandle mh = super.getBestInvoker(runtimeType, args);
|
||||
|
||||
/*
|
||||
* Not all functions can be specialized, for example, if we deemed memory
|
||||
* footprint too large to store a parse snapshot, or if it is meaningless
|
||||
* to do so, such as e.g. for runScript
|
||||
*/
|
||||
if (!functionNode.canSpecialize()) {
|
||||
return mh;
|
||||
}
|
||||
|
||||
final FunctionNode snapshot = functionNode.getSnapshot();
|
||||
if (snapshot == null) {
|
||||
/*
|
||||
* Check if best invoker is equally specific or more specific than runtime
|
||||
* type. In that case, we don't need further specialization, but can use
|
||||
* whatever we have already. We know that it will match callSiteType, or it
|
||||
* would not have been returned from getBestInvoker
|
||||
*/
|
||||
if (!code.isLessSpecificThan(runtimeType)) {
|
||||
return mh;
|
||||
}
|
||||
|
||||
int i;
|
||||
final FunctionNode snapshot = functionNode.getSnapshot();
|
||||
assert snapshot != null;
|
||||
|
||||
//classes known at runtime
|
||||
final LinkedList<Type> runtimeArgs = new LinkedList<>();
|
||||
for (i = args.length - 1; i >= args.length - snapshot.getParameters().size(); i--) {
|
||||
runtimeArgs.addLast(runtimeType(args[i]));
|
||||
}
|
||||
|
||||
//classes known at compile time
|
||||
/*
|
||||
* Create a list of the arg types that the compiler knows about
|
||||
* typically, the runtime args are a lot more specific, and we should aggressively
|
||||
* try to use those whenever possible
|
||||
* We WILL try to make an aggressive guess as possible, and add guards if needed.
|
||||
* For example, if the compiler can deduce that we have a number type, but the runtime
|
||||
* passes and int, we might still want to keep it an int, and the gamble to
|
||||
* check that whatever is passed is int representable usually pays off
|
||||
* If the compiler only knows that a parameter is an "Object", it is still worth
|
||||
* it to try to specialize it by looking at the runtime arg.
|
||||
*/
|
||||
final LinkedList<Type> compileTimeArgs = new LinkedList<>();
|
||||
for (i = callSiteType.parameterCount() - 1; i >= 0 && compileTimeArgs.size() < snapshot.getParameters().size(); i--) {
|
||||
compileTimeArgs.addLast(Type.typeFor(callSiteType.parameterType(i)));
|
||||
compileTimeArgs.addFirst(Type.typeFor(callSiteType.parameterType(i)));
|
||||
}
|
||||
|
||||
//the classes known at compile time are a safe to generate as primitives without parameter guards
|
||||
//the classes known at runtime are safe to generate as primitives IFF there are parameter guards
|
||||
/*
|
||||
* The classes known at compile time are a safe to generate as primitives without parameter guards
|
||||
* But the classes known at runtime (if more specific than compile time types) are safe to generate as primitives
|
||||
* IFF there are parameter guards
|
||||
*/
|
||||
MethodHandle guard = null;
|
||||
final ArrayList<Type> runtimeParamTypes = runtimeType(runtimeType);
|
||||
while (runtimeParamTypes.size() > functionNode.getParameters().size()) {
|
||||
runtimeParamTypes.remove(0);
|
||||
}
|
||||
for (i = 0; i < compileTimeArgs.size(); i++) {
|
||||
final Type runtimeType = runtimeArgs.get(i);
|
||||
final Type compileType = compileTimeArgs.get(i);
|
||||
final Type rparam = Type.typeFor(runtimeType.parameterType(i));
|
||||
final Type cparam = compileTimeArgs.get(i);
|
||||
|
||||
if (compileType.isObject() && !runtimeType.isObject()) {
|
||||
if (cparam.isObject() && !rparam.isObject()) {
|
||||
//check that the runtime object is still coercible to the runtime type, because compiler can't prove it's always primitive
|
||||
if (guard == null) {
|
||||
guard = PARAM_TYPE_GUARD;
|
||||
guard = MH.insertArguments(guard, 0, compileTimeArgs.toArray(new Type[compileTimeArgs.size()]), runtimeArgs.toArray(new Type[runtimeArgs.size()]));
|
||||
guard = MH.insertArguments(PARAM_TYPE_GUARD, 0, (Object)runtimeParamTypes.toArray(new Type[runtimeParamTypes.size()]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//System.err.println("Specialized " + name + " " + runtimeArgs + " known=" + compileTimeArgs);
|
||||
Compiler.LOG.info("Callsite specialized ", name, " runtimeType=", runtimeType, " parameters=", snapshot.getParameters(), " args=", Arrays.asList(args));
|
||||
|
||||
assert snapshot != null;
|
||||
assert snapshot != functionNode;
|
||||
|
||||
final Compiler compiler = new Compiler(installer);
|
||||
final FunctionNode compiledSnapshot = compiler.compile(snapshot.setHints(null, new Compiler.Hints(compileTimeArgs.toArray(new Type[compileTimeArgs.size()]))));
|
||||
|
||||
final FunctionNode compiledSnapshot = compiler.compile(
|
||||
snapshot.setHints(
|
||||
null,
|
||||
new Compiler.Hints(runtimeParamTypes.toArray(new Type[runtimeParamTypes.size()]))));
|
||||
|
||||
/*
|
||||
* No matter how narrow your types were, they can never be narrower than Attr during recompile made them. I.e. you
|
||||
* can put an int into the function here, if you see it as a runtime type, but if the function uses a multiplication
|
||||
* on it, it will still need to be a double. At least until we have overflow checks. Similarly, if an int is
|
||||
* passed but it is used as a string, it makes no sense to make the parameter narrower than Object. At least until
|
||||
* the "different types for one symbol in difference places" work is done
|
||||
*/
|
||||
compiler.install(compiledSnapshot);
|
||||
|
||||
final MethodHandle nmh = addCode(compiledSnapshot, guard, mh);
|
||||
|
||||
return nmh;
|
||||
return addCode(compiledSnapshot, runtimeType, guard, mh);
|
||||
}
|
||||
|
||||
private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
|
||||
|
@ -54,7 +54,7 @@ public final class ScriptEnvironment {
|
||||
private final Namespace namespace;
|
||||
|
||||
/** Current Options object. */
|
||||
private Options options;
|
||||
private final Options options;
|
||||
|
||||
/** Always allow functions as statements */
|
||||
public final boolean _anon_functions;
|
||||
@ -155,6 +155,9 @@ public final class ScriptEnvironment {
|
||||
/** print symbols and their contents for the script */
|
||||
public final boolean _print_symbols;
|
||||
|
||||
/** range analysis for known types */
|
||||
public final boolean _range_analysis;
|
||||
|
||||
/** is this environment in scripting mode? */
|
||||
public final boolean _scripting;
|
||||
|
||||
@ -183,7 +186,7 @@ public final class ScriptEnvironment {
|
||||
* @param out output print writer
|
||||
* @param err error print writer
|
||||
*/
|
||||
ScriptEnvironment(final Options options, final PrintWriter out, final PrintWriter err) {
|
||||
public ScriptEnvironment(final Options options, final PrintWriter out, final PrintWriter err) {
|
||||
this.out = out;
|
||||
this.err = err;
|
||||
this.namespace = new Namespace();
|
||||
@ -219,6 +222,7 @@ public final class ScriptEnvironment {
|
||||
_print_parse = options.getBoolean("print.parse");
|
||||
_print_lower_parse = options.getBoolean("print.lower.parse");
|
||||
_print_symbols = options.getBoolean("print.symbols");
|
||||
_range_analysis = options.getBoolean("range.analysis");
|
||||
_scripting = options.getBoolean("scripting");
|
||||
_strict = options.getBoolean("strict");
|
||||
_version = options.getBoolean("version");
|
||||
@ -258,14 +262,19 @@ public final class ScriptEnvironment {
|
||||
}
|
||||
this._callsite_flags = callSiteFlags;
|
||||
|
||||
final Option<?> option = options.get("timezone");
|
||||
if (option != null) {
|
||||
this._timezone = (TimeZone)option.getValue();
|
||||
final Option<?> timezoneOption = options.get("timezone");
|
||||
if (timezoneOption != null) {
|
||||
this._timezone = (TimeZone)timezoneOption.getValue();
|
||||
} else {
|
||||
this._timezone = TimeZone.getDefault();
|
||||
}
|
||||
|
||||
this._locale = Locale.getDefault();
|
||||
final Option<?> localeOption = options.get("locale");
|
||||
if (localeOption != null) {
|
||||
this._locale = (Locale)localeOption.getValue();
|
||||
} else {
|
||||
this._locale = Locale.getDefault();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -25,14 +25,13 @@
|
||||
|
||||
package jdk.nashorn.internal.runtime;
|
||||
|
||||
import static jdk.nashorn.internal.lookup.Lookup.MH;
|
||||
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
|
||||
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
|
||||
import static jdk.nashorn.internal.lookup.Lookup.MH;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
|
||||
import jdk.nashorn.internal.runtime.linker.JavaAdapterFactory;
|
||||
|
||||
/**
|
||||
@ -92,12 +91,13 @@ public abstract class ScriptFunctionData {
|
||||
CompiledFunction bind(final CompiledFunction originalInv, final ScriptFunction fn, final Object self, final Object[] args) {
|
||||
final MethodHandle boundInvoker = bindInvokeHandle(originalInv.getInvoker(), fn, self, args);
|
||||
|
||||
//TODO the boundinvoker.type() could actually be more specific here
|
||||
if (isConstructor()) {
|
||||
ensureConstructor(originalInv);
|
||||
return new CompiledFunction(boundInvoker, bindConstructHandle(originalInv.getConstructor(), fn, args));
|
||||
return new CompiledFunction(boundInvoker.type(), boundInvoker, bindConstructHandle(originalInv.getConstructor(), fn, args));
|
||||
}
|
||||
|
||||
return new CompiledFunction(boundInvoker);
|
||||
return new CompiledFunction(boundInvoker.type(), boundInvoker);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -389,7 +389,9 @@ public abstract class ScriptFunctionData {
|
||||
boundInvoker = noArgBoundInvoker;
|
||||
}
|
||||
} else {
|
||||
final Object[] boundArgs = new Object[Math.min(originalInvoker.type().parameterCount(), args.length + (isTargetBound ? 0 : (needsCallee ? 2 : 1)))];
|
||||
// If target is already bound, insert additional bound arguments after "this" argument, at position 1.
|
||||
final int argInsertPos = isTargetBound ? 1 : 0;
|
||||
final Object[] boundArgs = new Object[Math.min(originalInvoker.type().parameterCount() - argInsertPos, args.length + (isTargetBound ? 0 : (needsCallee ? 2 : 1)))];
|
||||
int next = 0;
|
||||
if (!isTargetBound) {
|
||||
if (needsCallee) {
|
||||
@ -403,7 +405,7 @@ public abstract class ScriptFunctionData {
|
||||
// "this" will get dropped anyway by the target invoker. We previously asserted that already bound functions
|
||||
// don't take a callee parameter, so we can know that the signature is (this[, args...]) therefore args
|
||||
// start at position 1. If the function is not bound, we start inserting arguments at position 0.
|
||||
boundInvoker = MH.insertArguments(originalInvoker, isTargetBound ? 1 : 0, boundArgs);
|
||||
boundInvoker = MH.insertArguments(originalInvoker, argInsertPos, boundArgs);
|
||||
}
|
||||
|
||||
if (isTargetBound) {
|
||||
|
@ -25,7 +25,6 @@
|
||||
|
||||
package jdk.nashorn.internal.runtime;
|
||||
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCall;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
|
||||
import static jdk.nashorn.internal.lookup.Lookup.MH;
|
||||
@ -151,17 +150,6 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
/** Method handle for setting the user accessors of a ScriptObject */
|
||||
public static final Call SET_USER_ACCESSORS = virtualCall(ScriptObject.class, "setUserAccessors", void.class, String.class, ScriptFunction.class, ScriptFunction.class);
|
||||
|
||||
/** Method handle for getter for {@link UserAccessorProperty}, given a slot */
|
||||
static final Call USER_ACCESSOR_GETTER = staticCall(MethodHandles.lookup(), ScriptObject.class, "userAccessorGetter", Object.class, ScriptObject.class, int.class, Object.class);
|
||||
|
||||
/** Method handle for setter for {@link UserAccessorProperty}, given a slot */
|
||||
static final Call USER_ACCESSOR_SETTER = staticCall(MethodHandles.lookup(), ScriptObject.class, "userAccessorSetter", void.class, ScriptObject.class, int.class, String.class, Object.class, Object.class);
|
||||
|
||||
private static final MethodHandle INVOKE_UA_GETTER = Bootstrap.createDynamicInvoker("dyn:call", Object.class,
|
||||
Object.class, Object.class);
|
||||
private static final MethodHandle INVOKE_UA_SETTER = Bootstrap.createDynamicInvoker("dyn:call", void.class,
|
||||
Object.class, Object.class, Object.class);
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
@ -699,17 +687,9 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
* @return New property.
|
||||
*/
|
||||
public final Property addOwnProperty(final String key, final int propertyFlags, final Object value) {
|
||||
final MethodHandle setter = addSpill(key, propertyFlags);
|
||||
|
||||
try {
|
||||
setter.invokeExact((Object)this, value);
|
||||
} catch (final Error|RuntimeException e) {
|
||||
throw e;
|
||||
} catch (final Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return getMap().findProperty(key);
|
||||
final Property property = addSpillProperty(key, propertyFlags);
|
||||
property.setObjectValue(this, this, value, false);
|
||||
return property;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -744,15 +724,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
// Erase the property field value with undefined. If the property is defined
|
||||
// by user-defined accessors, we don't want to call the setter!!
|
||||
if (!(property instanceof UserAccessorProperty)) {
|
||||
try {
|
||||
// make the property value to be undefined
|
||||
//TODO specproperties
|
||||
property.getSetter(Object.class, getMap()).invokeExact((Object)this, (Object)UNDEFINED);
|
||||
} catch (final RuntimeException | Error e) {
|
||||
throw e;
|
||||
} catch (final Throwable t) {
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
property.setObjectValue(this, this, UNDEFINED, false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -948,18 +920,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
* @return the value of the property
|
||||
*/
|
||||
protected static Object getObjectValue(final FindProperty find) {
|
||||
final MethodHandle getter = find.getGetter(Object.class);
|
||||
if (getter != null) {
|
||||
try {
|
||||
return getter.invokeExact((Object)find.getGetterReceiver());
|
||||
} catch (final Error|RuntimeException e) {
|
||||
throw e;
|
||||
} catch (final Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
return UNDEFINED;
|
||||
return find.getObjectValue();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2087,11 +2048,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
property = addOwnProperty(property);
|
||||
} else {
|
||||
int i = getMap().getSpillLength();
|
||||
MethodHandle getter = MH.arrayElementGetter(Object[].class);
|
||||
MethodHandle setter = MH.arrayElementSetter(Object[].class);
|
||||
getter = MH.asType(MH.insertArguments(getter, 1, i), Lookup.GET_OBJECT_TYPE);
|
||||
setter = MH.asType(MH.insertArguments(setter, 1, i), Lookup.SET_OBJECT_TYPE);
|
||||
property = new AccessorProperty(key, propertyFlags | Property.IS_SPILL, i, getter, setter);
|
||||
property = new AccessorProperty(key, propertyFlags | Property.IS_SPILL, i);
|
||||
notifyPropertyAdded(this, property);
|
||||
property = addOwnProperty(property);
|
||||
i = property.getSlot();
|
||||
@ -2115,20 +2072,15 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
|
||||
/**
|
||||
* Add a spill entry for the given key.
|
||||
* @param key Property key.
|
||||
* @param propertyFlags Property flags.
|
||||
* @param key Property key.
|
||||
* @return Setter method handle.
|
||||
*/
|
||||
private MethodHandle addSpill(final String key, final int propertyFlags) {
|
||||
final Property spillProperty = addSpillProperty(key, propertyFlags);
|
||||
MethodHandle addSpill(final String key) {
|
||||
final Property spillProperty = addSpillProperty(key, 0);
|
||||
final Class<?> type = Object.class;
|
||||
return spillProperty.getSetter(type, getMap()); //TODO specfields
|
||||
}
|
||||
|
||||
MethodHandle addSpill(final String key) {
|
||||
return addSpill(key, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure arguments are paired correctly, with respect to more parameters than declared,
|
||||
* fewer parameters than declared and other things that JavaScript allows. This might involve
|
||||
@ -2659,14 +2611,8 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final MethodHandle setter = f.getSetter(Object.class, strict); //TODO specfields
|
||||
setter.invokeExact((Object)f.getSetterReceiver(), value);
|
||||
} catch (final Error|RuntimeException e) {
|
||||
throw e;
|
||||
} catch (final Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
f.setObjectValue(value, strict);
|
||||
|
||||
} else if (!isExtensible()) {
|
||||
if (strict) {
|
||||
throw typeError("object.non.extensible", key, ScriptRuntime.safeToString(this));
|
||||
@ -2677,13 +2623,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
}
|
||||
|
||||
private void spill(final String key, final Object value) {
|
||||
try {
|
||||
addSpill(key).invokeExact((Object)this, value);
|
||||
} catch (final Error|RuntimeException e) {
|
||||
throw e;
|
||||
} catch (final Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
addSpillProperty(key, 0).setObjectValue(this, this, value, false);
|
||||
}
|
||||
|
||||
|
||||
@ -3217,46 +3157,6 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
return (index < 0 || (index >= spill.length)) ? null : spill[index];
|
||||
}
|
||||
|
||||
// User defined getter and setter are always called by "dyn:call". Note that the user
|
||||
// getter/setter may be inherited. If so, proto is bound during lookup. In either
|
||||
// inherited or self case, slot is also bound during lookup. Actual ScriptFunction
|
||||
// to be called is retrieved everytime and applied.
|
||||
@SuppressWarnings("unused")
|
||||
private static Object userAccessorGetter(final ScriptObject proto, final int slot, final Object self) {
|
||||
final ScriptObject container = (proto != null) ? proto : (ScriptObject)self;
|
||||
final Object func = container.getSpill(slot);
|
||||
|
||||
if (func instanceof ScriptFunction) {
|
||||
try {
|
||||
return INVOKE_UA_GETTER.invokeExact(func, self);
|
||||
} catch(final Error|RuntimeException t) {
|
||||
throw t;
|
||||
} catch(final Throwable t) {
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
}
|
||||
|
||||
return UNDEFINED;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static void userAccessorSetter(final ScriptObject proto, final int slot, final String name, final Object self, final Object value) {
|
||||
final ScriptObject container = (proto != null) ? proto : (ScriptObject)self;
|
||||
final Object func = container.getSpill(slot);
|
||||
|
||||
if (func instanceof ScriptFunction) {
|
||||
try {
|
||||
INVOKE_UA_SETTER.invokeExact(func, self, value);
|
||||
} catch(final Error|RuntimeException t) {
|
||||
throw t;
|
||||
} catch(final Throwable t) {
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
} else if (name != null) {
|
||||
throw typeError("property.has.no.setter", name, ScriptRuntime.safeToString(self));
|
||||
}
|
||||
}
|
||||
|
||||
private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
|
||||
final Class<?> own = ScriptObject.class;
|
||||
final MethodType mt = MH.type(rtype, types);
|
||||
|
@ -36,6 +36,7 @@ import java.lang.invoke.MethodHandle;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Objects;
|
||||
import jdk.internal.dynalink.beans.StaticClass;
|
||||
@ -788,7 +789,7 @@ public final class ScriptRuntime {
|
||||
return false;
|
||||
}
|
||||
|
||||
throw typeError("in.with.non.object", rvalType.toString().toLowerCase());
|
||||
throw typeError("in.with.non.object", rvalType.toString().toLowerCase(Locale.ENGLISH));
|
||||
}
|
||||
|
||||
/**
|
||||
|
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