This commit is contained in:
Lana Steuck 2013-06-03 23:24:36 -07:00
commit 9bd2647140
206 changed files with 5200 additions and 5179 deletions

View File

@ -24,3 +24,4 @@ jcov2/*
.idea/*
test/lib/testng.jar
test/script/external/*
.project

View File

@ -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 &ndash; passing an implementation JavaScript object
or function to a constructor, or using <code>Java.extend</code> with <code>new</code> &ndash; 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> &ndash; aside from being able to take any number of type parameters
denoting a class to extend and interfaces to implement &ndash; 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 &ndash; 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 &ndash; 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 &ndash; 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 &ndash; 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. &ndash; 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>

View File

@ -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);

View File

@ -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>

View File

@ -212,7 +212,9 @@
target="${javac.target}"
debug="${javac.debug}"
encoding="${javac.encoding}"
includeantruntime="false"/>
includeantruntime="false">
<compilerarg line="-extdirs &quot;&quot;"/>
</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>

View File

@ -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}"/>

View File

@ -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}

View File

@ -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
*

View File

@ -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

View File

@ -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);
}
});

View File

@ -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>");

View File

@ -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

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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();
}
/**

View File

@ -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;

View File

@ -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);

View File

@ -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;
}

View File

@ -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());
}
}
}
}

View 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;
}
}

View File

@ -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());
}
/**

View File

@ -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;

View File

@ -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;
}

View 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);
}
}

View File

@ -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

View File

@ -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;

View File

@ -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)));
}

View File

@ -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)));
}

View File

@ -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);
}

View File

@ -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)).

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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)));
}

View File

@ -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)).

View File

@ -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));
}
/**

View File

@ -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);
}

View File

@ -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)).

View File

@ -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

View File

@ -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;

View File

@ -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();
}
/**

View File

@ -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);

View File

@ -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);

View File

@ -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<>();

View File

@ -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)));
}

View File

@ -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)).

View File

@ -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)));

View File

@ -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) {

View File

@ -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)));
}

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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)));
}

View File

@ -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);

View File

@ -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(

View File

@ -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)).

View File

@ -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();

View File

@ -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;

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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

View File

@ -603,6 +603,11 @@ public final class NativeArguments extends ScriptObject {
}
}
@Override
public Object getLength() {
return length;
}
private Object getArgumentsLength() {
return length;
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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) {

View File

@ -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) {

View File

@ -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) {

View File

@ -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) {

View File

@ -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) {

View File

@ -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) {

View File

@ -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);
}
/**

View File

@ -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);
}
/**

View File

@ -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) {

View File

@ -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) {

View File

@ -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) {

View File

@ -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) {

View File

@ -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);

View File

@ -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() {

View File

@ -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);

View File

@ -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;
}

View File

@ -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;

View File

@ -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));
}
}

View File

@ -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);
}
}

View File

@ -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));
}
}

View File

@ -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) {

View File

@ -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

View 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;
}
}

View File

@ -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);

View File

@ -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));
}
}

View File

@ -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

View File

@ -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) {

View File

@ -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();
}
}
/**

View File

@ -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) {

View File

@ -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);

View File

@ -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