提交 2abdcbe2 编写于 作者: L lana

Merge

......@@ -24,3 +24,4 @@ jcov2/*
.idea/*
test/lib/testng.jar
test/script/external/*
.project
......@@ -71,9 +71,20 @@ Classes</a></span></li>
Arrays</a></span></li>
<li><span><a href="#jsimplement">Implementing Java
Interfaces</a></span></li>
<li><span><a href="#jsextend">Extending Java classes
<li><span><a href="#jsextendabstract">Extending Abstract Java Classes
</a></span></li>
<li><span><a href="#jsextendconcrete">Extending Concrete Java Classes
</a></span></li>
<li><span><a href="#jsimplementmultiple">Implementing Multiple Java Interfaces
</a></span></li>
<li><span><a href="#classBoundImplementations">Class-Bound Implementations
</a></span></li>
<li><span><a href="#jsoverload">Overload Resolution</a></span></li>
<li><span><a href="#dataTypeMapping">Mapping of Data Types Between Java
and JavaScript</a></span></li>
</ul>
</li>
<li><span><a href="#engineimpl">Implementing Your Own Script
......@@ -466,10 +477,10 @@ language rather than JavaScript.</p>
</code>
</pre>
Note that the name of the type is always a string for a fully qualified name. You can use any of these types to create new instances, e.g.:
Note that the name of the type is always a string for a fully qualified name. You can use any of these expressions to create new instances, e.g.:
<pre><code>
var anArrayList = new Java.type("java.util.ArrayList")
var anArrayList = new (Java.type("java.util.ArrayList"))
</code></pre>
or
......@@ -496,6 +507,37 @@ However, once you retrieved the outer class, you can access the inner class as a
<p>
You can access both static and non-static inner classes. If you want to create an instance of a non-static inner class, remember to pass an instance of its outer class as the first argument to the constructor.
</p>
<p>
In addition to creating new instances, the type objects returned from <code>Java.type</code> calls can also be used to access the
static fields and methods of the classes:
<pre><code>
var File = Java.type("java.io.File")
File.createTempFile("nashorn", ".tmp")
</code></pre>
<p>
Methods with names of the form <code>isXxx()</code>, <code>getXxx()</code>, and <code>setXxx()</code> can also be used as properties, for both instances and statics.
</p>
<p>
A type object returned from <code>Java.type</code> is distinct from a <code>java.lang.Class</code> object. You can obtain one from the other using properties <code>class</code> and <code>static</code> on them.
<pre><code>
var ArrayList = Java.type("java.util.ArrayList")
var a = new ArrayList
// All of the following print true:
print("Type acts as target of instanceof: " + (a instanceof ArrayList))
print("Class doesn't act as target of instanceof: " + !(a instanceof a.getClass()))
print("Type is not same as instance's getClass(): " + (a.getClass() !== ArrayList))
print("Type's `class` property is same as instance getClass(): " + (a.getClass() === ArrayList.class))
print("Type is same as instance getClass()'s `static` property: " + (a.getClass().static === ArrayList))
</code></pre>
<p>
You can think of the type object as similar to the class names as used in Java source code: you use them as the
arguments to the <code>new</code> and <code>instanceof</code> operators and as the namespace for the static fields
and methods, but they are different than the runtime <code>Class</code> objects returned by <code>getClass()</code> calls.
Syntactically and semantically, this separation produces code that is most similar to Java code, where a distinction
between compile-time class expressions and runtime class objects also exists. (Also, Java can't have the equivalent of <code>static</code>
property on a <code>Class</code> object since compile-time class expressions are never reified as objects).
</p>
<hr>
<a name="jsimport" id="jsimport"></a>
<h3>Importing Java Packages, Classes</h3>
......@@ -558,10 +600,7 @@ with (SwingGui) {
<a name="jsarrays" id="jsarrays"></a>
<h3>Creating, Converting and Using Java Arrays</h3>
<p>
Array element access or length access is
the same as in Java. Also, a script array can be used when a Java
method expects a Java array (auto conversion). So in most cases we
don't have to create Java arrays explicitly.</p>
Array element access or length access is the same as in Java.</p>
<pre><code>
// <a href="source/javaarray.js">javaarray.js</a>
......@@ -577,27 +616,31 @@ print(a[0]);
</pre>
<p>
It is also possible to convert between JavaScript and Java arrays.
Given a JavaScript array and a Java type, <code>Java.toJavaArray</code> returns a Java array with the same initial contents, and with the specified component type.
Given a JavaScript array and a Java type, <code>Java.to</code> returns a Java array with the same initial contents, and with the specified array type.
</p>
<pre><code>
var anArray = [1, "13", false]
var javaIntArray = Java.toJavaArray(anArray, "int")
var javaIntArray = Java.to(anArray, "int[]")
print(javaIntArray[0]) // prints 1
print(javaIntArray[1]) // prints 13, as string "13" was converted to number 13 as per ECMAScript ToNumber conversion
print(javaIntArray[2]) // prints 0, as boolean false was converted to number 0 as per ECMAScript ToNumber conversion
</code></pre>
<p>
Given a Java array or Collection, <code>Java.toJavaScriptArray</code> returns a JavaScript array with a shallow copy of its contents. Note that in most cases, you can use Java arrays and lists natively in Nashorn; in cases where for some reason you need to have an actual JavaScript native array (e.g. to work with the array comprehensions functions), you will want to use this method.i
You can use either a string or a type object returned from <code>Java.type()</code> to specify the type of the array.
You can also omit the array type, in which case a <code>Object[]</code> will be created.
</p>
<p>
Given a Java array or Collection, <code>Java.from</code> returns a JavaScript array with a shallow copy of its contents. Note that in most cases, you can use Java arrays and lists natively in Nashorn; in cases where for some reason you need to have an actual JavaScript native array (e.g. to work with the array comprehensions functions), you will want to use this method.
</p>
<pre><code>
var File = Java.type("java.io.File");
var listCurDir = new File(".").listFiles();
var jsList = Java.toJavaScriptArray(listCurDir);
var jsList = Java.from(listCurDir);
print(jsList);
</code></pre>
<hr>
<a name="jsimplement" id="jsimplement"></a>
<h3>Implementing Java Interfaces</h3>
<h3>Implementing Java interfaces</h3>
<p>A Java interface can be implemented in JavaScript by using a
Java anonymous class-like syntax:</p>
<pre><code>
......@@ -631,8 +674,8 @@ th.join();
</code>
</pre>
<hr>
<a name="jsextend" id="jsextend"></a>
<h3>Extending Java classes</h3>
<a name="jsextendabstract" id="jsextendabstract"></a>
<h3>Extending Abstract Java Classes</h3>
<p>
If a Java class is abstract, you can instantiate an anonymous subclass of it using an argument list that is applicable to any of its public or protected constructors, but inserting a JavaScript object with functions properties that provide JavaScript implementations of the abstract methods. If method names are overloaded, the JavaScript function will provide implementation for all overloads. E.g.:
</p>
......@@ -671,6 +714,9 @@ The use of functions can be taken even further; if you are invoking a Java metho
Here, <code>Timer.schedule()</code> expects a <code>TimerTask</code> as its argument, so Nashorn creates an instance of a TimerTask subclass and uses the passed function to implement its only abstract method, run(). In this usage though, you can't use non-default constructors; the type must be either an interface, or must have a protected or public no-arg constructor.
<hr>
<a name="jsextendconcrete" id="jsextendconcrete"></a>
<h3>Extending Concrete Java Classes</h3>
<p>
To extend a concrete Java class, you have to use <code>Java.extend</code> function.
<code>Java.extend</code> returns a type object for a subclass of the specified Java class (or implementation of the specified interface) that acts as a script-to-Java adapter for it.
......@@ -695,26 +741,178 @@ var printAddInvokedArrayList = new ArrayListExtender() {
printSizeInvokedArrayList.size();
printAddInvokedArrayList.add(33, 33);
</code></pre>
<p>
The reason you must use <code>Java.extend()</code> with concrete classes is that with concrete classes, there can be a
syntactic ambiguity if you just invoke their constructor. Consider this example:
</p>
<pre><code>
var t = new java.lang.Thread({ run: function() { print("Hello!") } })
</code></pre>
<p>
If we allowed subclassing of concrete classes with constructor syntax, Nashorn couldn't tell if you're creating a new
<code>Thread</code> and passing it a <code>Runnable</code> at this point, or you are subclassing <code>Thread</code> and
passing it a new implementation for its own <code>run()</code> method.
</p>
<hr>
<a name="jsimplementmultiple" id="jsimplementmultiple"></a>
<h3>Implementing Multiple Interfaces</h3>
<p>
<code>Java.extend</code> can in fact take a list of multiple types. At most one of the types can be a class, and the rest must
be interfaces (the class doesn't have to be the first in the list). You will get back an object that extends the class and
implements all the interfaces. (Obviously, if you only specify interfaces and no class, the object will extend <code>java.lang.Object</code>).
<hr>
<a name="classBoundImplementations" id="classBoundImplementations"></a>
<h3>Class-Bound Implementations</h3>
<p>
The methods shown so far for extending Java classes and implementing interfaces &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>
......
......@@ -40,7 +40,7 @@ print(a[0]);
// convert a script array to Java array
var anArray = [1, "13", false];
var javaIntArray = Java.toJavaArray(anArray, "int");
var javaIntArray = Java.to(anArray, "int[]");
print(javaIntArray[0]);// prints 1
print(javaIntArray[1]); // prints 13, as string "13" was converted to number 13 as per ECMAScript ToNumber conversion
print(javaIntArray[2]);// prints 0, as boolean false was converted to number 0 as per ECMAScript ToNumber conversion
......@@ -48,5 +48,5 @@ print(javaIntArray[2]);// prints 0, as boolean false was converted to number 0 a
// convert a Java array to a JavaScript array
var File = Java.type("java.io.File");
var listCurDir = new File(".").listFiles();
var jsList = Java.toJavaScriptArray(listCurDir);
var jsList = Java.from(listCurDir);
print(jsList);
......@@ -24,258 +24,270 @@
<project name="nashorn-benchmarks" default="all" basedir="..">
<target name="octane-init" depends="jar">
<fileset id="octane-set"
dir="${octane-test-sys-prop.test.js.roots}"
excludes="${octane-test-sys-prop.test.js.exclude.list}">
<include name="*.js"/>
</fileset>
<pathconvert pathsep=" " property="octane-tests" refid="octane-set"/>
<property name="octane-tests" value="box2d code-load crypto deltablue earley-boyer gbemu navier-stokes pdfjs raytrace regexp richards splay"/>
</target>
<!-- ignore benchmarks where rhino crashes -->
<target name="octane-init-rhino" depends="jar">
<property name="octane-tests" value="box2d code-load crypto deltablue earley-boyer gbemu navier-stokes raytrace regexp richards splay"/>
</target>
<!-- box2d -->
<target name="octane-box2d" depends="jar">
<antcall target="run-octane">
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/box2d.js"/>
<param name="octane-tests" value="box2d"/>
</antcall>
</target>
<target name="octane-box2d-v8" depends="jar">
<antcall target="run-octane-v8">
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/box2d.js"/>
<param name="octane-tests" value="box2d"/>
</antcall>
</target>
<target name="octane-box2d-rhino" depends="jar">
<antcall target="run-octane-rhino">
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/box2d.js"/>
<param name="octane-tests" value="box2d"/>
</antcall>
</target>
<!-- code-load -->
<target name="octane-code-load" depends="jar">
<antcall target="run-octane">
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/code-load.js"/>
<param name="octane-tests" value="code-load"/>
</antcall>
</target>
<target name="octane-code-load-v8" depends="jar">
<antcall target="run-octane-v8">
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/code-load.js"/>
<param name="octane-tests" value="code-load"/>
</antcall>
</target>
<target name="octane-code-load-rhino" depends="jar">
<antcall target="run-octane-rhino">
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/code-load.js"/>
<param name="octane-tests" value="code-load"/>
</antcall>
</target>
<!-- crypto -->
<target name="octane-crypto" depends="jar">
<antcall target="run-octane">
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/crypto.js"/>
<param name="octane-tests" value="crypto"/>
</antcall>
</target>
<target name="octane-crypto-v8" depends="jar">
<antcall target="run-octane-v8">
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/crypto.js"/>
<param name="octane-tests" value="crypto"/>
</antcall>
</target>
<target name="octane-crypto-rhino" depends="jar">
<antcall target="run-octane-rhino">
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/crypto.js"/>
<param name="octane-tests" value="crypto"/>
</antcall>
</target>
<!-- deltablue -->
<target name="octane-deltablue" depends="jar">
<antcall target="run-octane">
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/deltablue.js"/>
<param name="octane-tests" value="deltablue"/>
</antcall>
</target>
<target name="octane-deltablue-v8" depends="jar">
<antcall target="run-octane-v8">
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/deltablue.js"/>
<param name="octane-tests" value="deltablue"/>
</antcall>
</target>
<target name="octane-deltablue-rhino" depends="jar">
<antcall target="run-octane-rhino">
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/deltablue.js"/>
<param name="octane-tests" value="deltablue"/>
</antcall>
</target>
<!-- earley-boyer -->
<target name="octane-earley-boyer" depends="jar">
<antcall target="run-octane">
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/earley-boyer.js"/>
<param name="octane-tests" value="earley-boyer"/>
</antcall>
</target>
<target name="octane-earley-boyer-v8" depends="jar">
<antcall target="run-octane-v8">
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/earley-boyer.js"/>
<param name="octane-tests" value="earley-boyer"/>
</antcall>
</target>
<target name="octane-earley-boyer-rhino" depends="jar">
<antcall target="run-octane-rhino">
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/earley-boyer.js"/>
<param name="octane-tests" value="earley-boyer"/>
</antcall>
</target>
<!-- gbemu -->
<target name="octane-gbemu" depends="jar">
<antcall target="run-octane">
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/gbemu.js"/>
<param name="octane-tests" value="gbemu"/>
</antcall>
</target>
<target name="octane-gbemu-v8" depends="jar">
<antcall target="run-octane-v8">
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/gbemu.js"/>
<param name="octane-tests" value="gbemu"/>
</antcall>
</target>
<target name="octane-gbemu-rhino" depends="jar">
<antcall target="run-octane-rhino">
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/gbemu.js"/>
<param name="octane-tests" value="gbemu"/>
</antcall>
</target>
<!-- mandreel -->
<target name="octane-mandreel" depends="jar">
<antcall target="run-octane">
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/mandreel.js"/>
<param name="octane-tests" value="mandreel"/>
</antcall>
</target>
<target name="octane-mandreel-v8" depends="jar">
<antcall target="run-octane-v8">
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/mandreel.js"/>
<param name="octane-tests" value="mandreel"/>
</antcall>
</target>
<target name="octane-mandreel-rhino" depends="jar">
<antcall target="run-octane-rhino">
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/mandreel.js"/>
<param name="octane-tests" value="mandreel"/>
</antcall>
</target>
<!-- navier-stokes -->
<target name="octane-navier-stokes" depends="jar">
<antcall target="run-octane">
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/navier-stokes.js"/>
<param name="octane-tests" value="navier-stokes"/>
</antcall>
</target>
<target name="octane-navier-stokes-v8" depends="jar">
<antcall target="run-octane-v8">
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/navier-stokes.js"/>
<param name="octane-tests" value="navier-stokes"/>
</antcall>
</target>
<target name="octane-navier-stokes-rhino" depends="jar">
<antcall target="run-octane-rhino">
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/navier-stokes.js"/>
<param name="octane-tests" value="navier-stokes"/>
</antcall>
</target>
<!-- pdfjs -->
<target name="octane-pdfjs" depends="jar">
<antcall target="run-octane">
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/pdfjs.js"/>
<param name="octane-tests" value="pdfjs"/>
</antcall>
</target>
<target name="octane-pdfjs-v8" depends="jar">
<antcall target="run-octane-v8">
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/pdfjs.js"/>
<param name="octane-tests" value="pdfjs"/>
</antcall>
</target>
<target name="octane-pdfjs-rhino" depends="jar">
<antcall target="run-octane-rhino">
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/pdfjs.js"/>
<param name="octane-tests" value="pdfjs"/>
</antcall>
</target>
<!-- raytrace -->
<target name="octane-raytrace" depends="jar">
<antcall target="run-octane">
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/raytrace.js"/>
<param name="octane-tests" value="raytrace"/>
</antcall>
</target>
<target name="octane-raytrace-v8" depends="jar">
<antcall target="run-octane-v8">
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/raytrace.js"/>
<param name="octane-tests" value="raytrace"/>
</antcall>
</target>
<target name="octane-raytrace-rhino" depends="jar">
<antcall target="run-octane-rhino">
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/raytrace.js"/>
<param name="octane-tests" value="raytrace"/>
</antcall>
</target>
<!-- regexp -->
<target name="octane-regexp" depends="jar">
<antcall target="run-octane">
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/regexp.js"/>
<param name="octane-tests" value="regexp"/>
</antcall>
</target>
<target name="octane-regexp-octane-v8" depends="jar">
<antcall target="run-octane-v8">
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/regexp.js"/>
<param name="octane-tests" value="regexp"/>
</antcall>
</target>
<target name="octane-regexp-rhino" depends="jar">
<antcall target="run-octane-rhino">
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/regexp.js"/>
<param name="octane-tests" value="regexp"/>
</antcall>
</target>
<!-- richards -->
<target name="octane-richards" depends="jar">
<antcall target="run-octane">
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/richards.js"/>
<param name="octane-tests" value="richards"/>
</antcall>
</target>
<target name="octane-richards-v8" depends="jar">
<antcall target="run-octane-v8">
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/richards.js"/>
<param name="octane-tests" value="richards"/>
</antcall>
</target>
<target name="octane-richards-rhino" depends="jar">
<antcall target="run-octane-rhino">
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/richards.js"/>
<param name="octane-tests" value="richards"/>
</antcall>
</target>
<!-- splay -->
<target name="octane-splay" depends="jar">
<antcall target="run-octane">
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/splay.js"/>
<param name="octane-tests" value="splay"/>
</antcall>
</target>
<target name="octane-splay-v8" depends="jar">
<antcall target="run-octane-v8">
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/splay.js"/>
<param name="octane-tests" value="splay"/>
</antcall>
</target>
<target name="octane-splay-rhino" depends="jar">
<antcall target="run-octane-rhino">
<param name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/splay.js"/>
<param name="octane-tests" value="splay"/>
</antcall>
</target>
......@@ -307,7 +319,7 @@
</target>
<!-- run octane benchmarks using Rhino as runtime -->
<target name="octane-rhino" depends="octane-init">
<target name="octane-rhino" depends="octane-init-rhino">
<antcall target="run-octane-rhino"/>
</target>
......
......@@ -212,7 +212,9 @@
target="${javac.target}"
debug="${javac.debug}"
encoding="${javac.encoding}"
includeantruntime="false"/>
includeantruntime="false">
<compilerarg line="-extdirs &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>
......
......@@ -139,6 +139,32 @@
<arg value="${cc.merged.xml}"/>
<arg value="-exclude"/>
<arg value="com\.oracle\.nashorn\.runtime\.ScriptRuntime*"/>
<arg value="-exclude"/>
<arg value="jdk\.nashorn\.internal\.javaadapters*"/>
<arg value="-exclude"/>
<arg value="jdk\.nashorn\.internal\.objects\.annotations*"/>
<arg value="-exclude"/>
<arg value="jdk\.nashorn\.internal\.scripts*"/>
<arg value="-exclude"/>
<arg value="jdk\.nashorn\.internal\.lookup\.MethodHandleFactory*"/>
<arg value="-exclude"/>
<arg value="jdk\.nashorn\.internal\.test\.framework*"/>
<arg value="-exclude"/>
<arg value="jdk\.nashorn\.test\.models*"/>
<arg value="-exclude"/>
<arg value="jdk\.nashorn\.internal\.ir\.debug*"/>
<arg value="-exclude"/>
<arg value="jdk\.nashorn\.internal\.runtime\.regexp\.joni\.bench*"/>
<arg value="-exclude"/>
<arg value="jdk\.nashorn\.internal\.runtime\.DebugLogger*"/>
<arg value="-exclude"/>
<arg value="jdk\.nashorn\.internal\.runtime\.Timing*"/>
<arg value="-exclude"/>
<arg value="jdk\.nashorn\.internal\.runtime\.Logging*"/>
<arg value="-exclude"/>
<arg value="jdk\.nashorn\.internal\.runtime\.Debug*"/>
<arg value="-exclude"/>
<arg value="jdk\.nashorn\.internal\.objects\.NativeDebug*"/>
<arg line="${cc.all.xmls}"/>
<classpath>
<pathelement location="${jcov.jar}"/>
......
......@@ -87,6 +87,7 @@ testng.verbose=2
testng.listeners=\
org.testng.reporters.SuiteHTMLReporter, \
org.testng.reporters.TestHTMLReporter, \
org.testng.reporters.jq.Main, \
org.testng.reporters.FailedReporter, \
org.testng.reporters.XMLReporter \
......@@ -214,9 +215,13 @@ test.src.dir=test/src
run.test.xmx=3G
run.test.xms=2G
run.test.user.language=tr
run.test.user.country=TR
# -XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintNMethods
# add '-Dtest.js.outofprocess' to run each test in a new sub-process
run.test.jvmargs.main=-server -Xmx${run.test.xmx} -XX:+TieredCompilation -ea -Dnashorn.debug=true -Dfile.encoding=UTF-8
run.test.jvmargs.main=-server -Xmx${run.test.xmx} -XX:+TieredCompilation -ea -Dfile.encoding=UTF-8 -Duser.language=${run.test.user.language} -Duser.country=${run.test.user.country}
#-XX:+HeapDumpOnOutOfMemoryError -XX:-UseCompressedKlassPointers -XX:+PrintHeapAtGC -XX:ClassMetaspaceSize=300M
run.test.jvmargs.octane.main=-Xms${run.test.xms} ${run.test.jvmargs.main}
......
......@@ -23,26 +23,12 @@
* questions.
*/
package netscape.javascript;
import java.applet.Applet;
package jdk.nashorn.api.scripting;
/**
* Stub for JSObject to get compilation going.
* netscape.javascript.JSObject-like interface for nashorn script objects.
*/
public abstract class JSObject {
/**
* Get the window for an {@link Applet}. Not supported
* by Nashorn
*
* @param a applet
* @return the window instance
*/
public static JSObject getWindow(final Applet a) {
throw new UnsupportedOperationException("getWindow");
}
/**
* Call a JavaScript method
*
......
......@@ -42,7 +42,6 @@ import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.ScriptRuntime;
import netscape.javascript.JSObject;
/**
* Mirror object that wraps a given ScriptObject instance. User can
......
......@@ -88,7 +88,7 @@ Object.defineProperty(this, "sprintf", {
}
}
array = Java.toJavaArray(array);
array = Java.to(array);
return Packages.jdk.nashorn.api.scripting.ScriptUtils.format(format, array);
}
});
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.codegen;
import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LexicalContextNode;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.Symbol;
import jdk.nashorn.internal.ir.WithNode;
/**
* A lexical context that also tracks if we have any dynamic scopes in the context. Such scopes can have new
* variables introduced into them at run time - a with block or a function directly containing an eval call.
* Furthermore, this class keeps track of current discard state, which the current method emitter being used is,
* the current compile unit, and local variable indexes
*/
final class CodeGeneratorLexicalContext extends LexicalContext {
private int dynamicScopeCount;
/** Map of shared scope call sites */
private final Map<SharedScopeCall, SharedScopeCall> scopeCalls = new HashMap<>();
/** Compile unit stack - every time we start a sub method (e.g. a split) we push one */
private final Deque<CompileUnit> compileUnits = new ArrayDeque<>();
/** Method emitter stack - every time we start a sub method (e.g. a split) we push one */
private final Deque<MethodEmitter> methodEmitters = new ArrayDeque<>();
/** The discard stack - whenever we enter a discard node we keep track of its return value status -
* i.e. should we keep it or throw it away */
private final Deque<Node> discard = new ArrayDeque<>();
/** A stack tracking the next free local variable slot in the blocks. There's one entry for every block
* currently on the lexical context stack. */
private int[] nextFreeSlots = new int[16];
/** size of next free slot vector */
private int nextFreeSlotsSize;
@Override
public <T extends LexicalContextNode> T push(final T node) {
if (isDynamicScopeBoundary(node)) {
++dynamicScopeCount;
}
return super.push(node);
}
@Override
public <T extends LexicalContextNode> T pop(final T node) {
final T popped = super.pop(node);
if (isDynamicScopeBoundary(popped)) {
--dynamicScopeCount;
}
if (node instanceof Block) {
--nextFreeSlotsSize;
}
return popped;
}
private boolean isDynamicScopeBoundary(final LexicalContextNode node) {
if (node instanceof Block) {
// Block's immediate parent is a with node. Note we aren't testing for a WithNode, as that'd capture
// processing of WithNode.expression too, but it should be unaffected.
return !isEmpty() && peek() instanceof WithNode;
} else if (node instanceof FunctionNode) {
// Function has a direct eval in it (so a top-level "var ..." in the eval code can introduce a new
// variable into the function's scope), and it isn't strict (as evals in strict functions get an
// isolated scope).
return isFunctionDynamicScope((FunctionNode)node);
}
return false;
}
boolean inDynamicScope() {
return dynamicScopeCount > 0;
}
static boolean isFunctionDynamicScope(FunctionNode fn) {
return fn.hasEval() && !fn.isStrict();
}
MethodEmitter pushMethodEmitter(final MethodEmitter newMethod) {
methodEmitters.push(newMethod);
return newMethod;
}
MethodEmitter popMethodEmitter(final MethodEmitter oldMethod) {
assert methodEmitters.peek() == oldMethod;
methodEmitters.pop();
return methodEmitters.isEmpty() ? null : methodEmitters.peek();
}
CompileUnit pushCompileUnit(final CompileUnit newUnit) {
compileUnits.push(newUnit);
return newUnit;
}
CompileUnit popCompileUnit(final CompileUnit oldUnit) {
assert compileUnits.peek() == oldUnit;
compileUnits.pop();
return compileUnits.isEmpty() ? null : compileUnits.peek();
}
boolean hasCompileUnits() {
return !compileUnits.isEmpty();
}
Collection<SharedScopeCall> getScopeCalls() {
return Collections.unmodifiableCollection(scopeCalls.values());
}
/**
* Get a shared static method representing a dynamic scope callsite.
*
* @param unit current compile unit
* @param symbol the symbol
* @param valueType the value type of the symbol
* @param returnType the return type
* @param paramTypes the parameter types
* @param flags the callsite flags
* @return an object representing a shared scope call
*/
SharedScopeCall getScopeCall(final CompileUnit unit, final Symbol symbol, final Type valueType, final Type returnType, final Type[] paramTypes, final int flags) {
final SharedScopeCall scopeCall = new SharedScopeCall(symbol, valueType, returnType, paramTypes, flags);
if (scopeCalls.containsKey(scopeCall)) {
return scopeCalls.get(scopeCall);
}
scopeCall.setClassAndName(unit, getCurrentFunction().uniqueName("scopeCall"));
scopeCalls.put(scopeCall, scopeCall);
return scopeCall;
}
/**
* Get a shared static method representing a dynamic scope get access.
*
* @param unit current compile unit
* @param type the type of the variable
* @param symbol the symbol
* @param flags the callsite flags
* @return an object representing a shared scope call
*/
SharedScopeCall getScopeGet(final CompileUnit unit, final Type type, final Symbol symbol, final int flags) {
final SharedScopeCall scopeCall = new SharedScopeCall(symbol, type, type, null, flags);
if (scopeCalls.containsKey(scopeCall)) {
return scopeCalls.get(scopeCall);
}
scopeCall.setClassAndName(unit, getCurrentFunction().uniqueName("scopeCall"));
scopeCalls.put(scopeCall, scopeCall);
return scopeCall;
}
void nextFreeSlot(final Block block) {
final boolean isFunctionBody = isFunctionBody();
final int nextFreeSlot;
if (isFunctionBody) {
// On entry to function, start with slot 0
nextFreeSlot = 0;
} else {
// Otherwise, continue from previous block's first free slot
nextFreeSlot = nextFreeSlots[nextFreeSlotsSize - 1];
}
if (nextFreeSlotsSize == nextFreeSlots.length) {
final int[] newNextFreeSlots = new int[nextFreeSlotsSize * 2];
System.arraycopy(nextFreeSlots, 0, newNextFreeSlots, 0, nextFreeSlotsSize);
nextFreeSlots = newNextFreeSlots;
}
nextFreeSlots[nextFreeSlotsSize++] = assignSlots(block, nextFreeSlot);
}
private static int assignSlots(final Block block, final int firstSlot) {
int nextSlot = firstSlot;
for (final Symbol symbol : block.getSymbols()) {
if (symbol.hasSlot()) {
symbol.setSlot(nextSlot);
nextSlot += symbol.slotCount();
}
}
return nextSlot;
}
void pushDiscard(final Node node) {
discard.push(node);
}
Node popDiscard() {
return discard.pop();
}
Node getCurrentDiscard() {
return discard.peek();
}
int quickSlot(final Symbol symbol) {
final int quickSlot = nextFreeSlots[nextFreeSlotsSize - 1];
nextFreeSlots[nextFreeSlotsSize - 1] = quickSlot + symbol.slotCount();
return quickSlot;
}
}
......@@ -11,20 +11,27 @@ import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SPLIT;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import jdk.nashorn.internal.codegen.types.Range;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.CallNode;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.ReturnNode;
import jdk.nashorn.internal.ir.Symbol;
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.TemporarySymbols;
import jdk.nashorn.internal.ir.debug.ASTWriter;
import jdk.nashorn.internal.ir.debug.PrintVisitor;
import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.ECMAErrors;
import jdk.nashorn.internal.runtime.ScriptEnvironment;
......@@ -66,7 +73,7 @@ enum CompilationPhase {
FunctionNode newFunctionNode = outermostFunctionNode;
newFunctionNode = (FunctionNode)newFunctionNode.accept(new NodeVisitor() {
newFunctionNode = (FunctionNode)newFunctionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
// self references are done with invokestatic and thus cannot
// have trampolines - never lazy
@Override
......@@ -99,10 +106,9 @@ enum CompilationPhase {
lazy.remove(node);
}
newFunctionNode = (FunctionNode)newFunctionNode.accept(new NodeOperatorVisitor() {
newFunctionNode = (FunctionNode)newFunctionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
@Override
public Node leaveFunctionNode(final FunctionNode functionNode) {
final LexicalContext lc = getLexicalContext();
if (lazy.contains(functionNode)) {
Compiler.LOG.fine(
"Marking ",
......@@ -174,7 +180,7 @@ enum CompilationPhase {
FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
final TemporarySymbols ts = compiler.getTemporarySymbols();
final FunctionNode newFunctionNode = (FunctionNode)enterAttr(fn, ts).accept(new Attr(ts));
if(compiler.getEnv()._print_mem_usage) {
if (compiler.getEnv()._print_mem_usage) {
Compiler.LOG.info("Attr temporary symbol count: " + ts.getTotalSymbolCount());
}
return newFunctionNode;
......@@ -186,12 +192,11 @@ enum CompilationPhase {
* @param functionNode node where to start iterating
*/
private FunctionNode enterAttr(final FunctionNode functionNode, final TemporarySymbols ts) {
return (FunctionNode)functionNode.accept(new NodeVisitor() {
return (FunctionNode)functionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
@Override
public Node leaveFunctionNode(final FunctionNode node) {
final LexicalContext lc = getLexicalContext();
if (node.isLazy()) {
FunctionNode newNode = node.setReturnType(getLexicalContext(), Type.OBJECT);
FunctionNode newNode = node.setReturnType(lc, Type.OBJECT);
return ts.ensureSymbol(lc, Type.OBJECT, newNode);
}
//node may have a reference here that needs to be nulled if it was referred to by
......@@ -207,6 +212,89 @@ enum CompilationPhase {
}
},
/*
* Range analysis
* Conservatively prove that certain variables can be narrower than
* the most generic number type
*/
RANGE_ANALYSIS_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR)) {
@Override
FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
if (!compiler.getEnv()._range_analysis) {
return fn;
}
FunctionNode newFunctionNode = (FunctionNode)fn.accept(new RangeAnalyzer());
final List<ReturnNode> returns = new ArrayList<>();
newFunctionNode = (FunctionNode)newFunctionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
private final Deque<ArrayList<ReturnNode>> returnStack = new ArrayDeque<>();
@Override
public boolean enterFunctionNode(final FunctionNode functionNode) {
returnStack.push(new ArrayList<ReturnNode>());
return true;
}
@Override
public Node leaveFunctionNode(final FunctionNode functionNode) {
Type returnType = Type.UNKNOWN;
for (final ReturnNode ret : returnStack.pop()) {
if (ret.getExpression() == null) {
returnType = Type.OBJECT;
break;
}
returnType = Type.widest(returnType, ret.getExpression().getType());
}
return functionNode.setReturnType(lc, returnType);
}
@Override
public Node leaveReturnNode(final ReturnNode returnNode) {
final ReturnNode result = (ReturnNode)leaveDefault(returnNode);
returns.add(result);
return result;
}
@Override
public Node leaveDefault(final Node node) {
final Symbol symbol = node.getSymbol();
if (symbol != null) {
final Range range = symbol.getRange();
final Type symbolType = symbol.getSymbolType();
if (!symbolType.isNumeric()) {
return node;
}
final Type rangeType = range.getType();
if (!Type.areEquivalent(symbolType, rangeType) && Type.widest(symbolType, rangeType) == symbolType) { //we can narrow range
RangeAnalyzer.LOG.info("[", lc.getCurrentFunction().getName(), "] ", symbol, " can be ", range.getType(), " ", symbol.getRange());
return node.setSymbol(lc, symbol.setTypeOverrideShared(range.getType(), compiler.getTemporarySymbols()));
}
}
return node;
}
});
Type returnType = Type.UNKNOWN;
for (final ReturnNode node : returns) {
if (node.getExpression() != null) {
returnType = Type.widest(returnType, node.getExpression().getType());
} else {
returnType = Type.OBJECT;
break;
}
}
return newFunctionNode.setReturnType(null, returnType);
}
@Override
public String toString() {
return "[Range Analysis]";
}
},
/*
* Splitter Split the AST into several compile units based on a size
* heuristic Splitter needs attributed AST for weight calculations (e.g. is
......@@ -218,7 +306,6 @@ enum CompilationPhase {
FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
final CompileUnit outermostCompileUnit = compiler.addCompileUnit(compiler.firstCompileUnitName());
// assert fn.isProgram() ;
final FunctionNode newFunctionNode = new Splitter(compiler, fn, outermostCompileUnit).split(fn);
assert newFunctionNode.getCompileUnit() == outermostCompileUnit : "fn.compileUnit (" + newFunctionNode.getCompileUnit() + ") != " + outermostCompileUnit;
......
......@@ -99,7 +99,7 @@ public final class Compiler {
private boolean strict;
private CodeInstaller<ScriptEnvironment> installer;
private final CodeInstaller<ScriptEnvironment> installer;
private final TemporarySymbols temporarySymbols = new TemporarySymbols();
......@@ -219,6 +219,7 @@ public final class Compiler {
CompilationPhase.CONSTANT_FOLDING_PHASE,
CompilationPhase.LOWERING_PHASE,
CompilationPhase.ATTRIBUTION_PHASE,
CompilationPhase.RANGE_ANALYSIS_PHASE,
CompilationPhase.SPLITTING_PHASE,
CompilationPhase.TYPE_FINALIZATION_PHASE,
CompilationPhase.BYTECODE_GENERATION_PHASE);
......@@ -384,6 +385,8 @@ public final class Compiler {
if (info) {
final StringBuilder sb = new StringBuilder();
sb.append("Compile job for '").
append(newFunctionNode.getSource()).
append(':').
append(newFunctionNode.getName()).
append("' finished");
......@@ -487,7 +490,7 @@ public final class Compiler {
}
if (sb != null) {
LOG.info(sb);
LOG.fine(sb);
}
return rootClass;
......
......@@ -262,7 +262,7 @@ public enum CompilerConstants {
* @return the internal descriptor for this type
*/
public static String typeDescriptor(final Class<?> clazz) {
return Type.getDescriptor(clazz);
return Type.typeFor(clazz).getDescriptor();
}
/**
......
......@@ -31,6 +31,7 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.AccessNode;
import jdk.nashorn.internal.ir.Assignment;
......@@ -84,13 +85,14 @@ import jdk.nashorn.internal.runtime.JSType;
* and frame optimizations
*/
final class FinalizeTypes extends NodeOperatorVisitor {
final class FinalizeTypes extends NodeOperatorVisitor<LexicalContext> {
private static final DebugLogger LOG = new DebugLogger("finalize");
private final TemporarySymbols temporarySymbols;
FinalizeTypes(final TemporarySymbols temporarySymbols) {
super(new LexicalContext());
this.temporarySymbols = temporarySymbols;
}
......@@ -233,7 +235,7 @@ final class FinalizeTypes extends NodeOperatorVisitor {
private boolean symbolIsInteger(Node node) {
final Symbol symbol = node.getSymbol();
assert symbol != null && symbol.getSymbolType().isInteger() : "int coercion expected: " + Debug.id(symbol) + " " + symbol + " " + getLexicalContext().getCurrentFunction().getSource();
assert symbol != null && symbol.getSymbolType().isInteger() : "int coercion expected: " + Debug.id(symbol) + " " + symbol + " " + lc.getCurrentFunction().getSource();
return true;
}
......@@ -382,12 +384,10 @@ final class FinalizeTypes extends NodeOperatorVisitor {
final Node test = forNode.getTest();
final Node modify = forNode.getModify();
final LexicalContext lc = getLexicalContext();
if (forNode.isForIn()) {
return forNode.setModify(lc, convert(forNode.getModify(), Type.OBJECT)); // NASHORN-400
}
assert test != null || forNode.hasGoto() : "forNode " + forNode + " needs goto and is missing it in " + getLexicalContext().getCurrentFunction();
assert test != null || forNode.hasGoto() : "forNode " + forNode + " needs goto and is missing it in " + lc.getCurrentFunction();
return forNode.
setInit(lc, init == null ? null : discard(init)).
......@@ -419,7 +419,7 @@ final class FinalizeTypes extends NodeOperatorVisitor {
@Override
public Node leaveFunctionNode(final FunctionNode functionNode) {
return functionNode.setState(getLexicalContext(), CompilationState.FINALIZED);
return functionNode.setState(lc, CompilationState.FINALIZED);
}
@Override
......@@ -450,7 +450,7 @@ final class FinalizeTypes extends NodeOperatorVisitor {
public Node leaveReturnNode(final ReturnNode returnNode) {
final Node expr = returnNode.getExpression();
if (expr != null) {
return returnNode.setExpression(convert(expr, getLexicalContext().getCurrentFunction().getReturnType()));
return returnNode.setExpression(convert(expr, lc.getCurrentFunction().getReturnType()));
}
return returnNode;
}
......@@ -482,8 +482,8 @@ final class FinalizeTypes extends NodeOperatorVisitor {
}
return switchNode.
setExpression(getLexicalContext(), convert(expression, Type.OBJECT)).
setCases(getLexicalContext(), newCases);
setExpression(lc, convert(expression, Type.OBJECT)).
setCases(lc, newCases);
}
@Override
......@@ -519,14 +519,14 @@ final class FinalizeTypes extends NodeOperatorVisitor {
public Node leaveWhileNode(final WhileNode whileNode) {
final Node test = whileNode.getTest();
if (test != null) {
return whileNode.setTest(getLexicalContext(), convert(test, Type.BOOLEAN));
return whileNode.setTest(lc, convert(test, Type.BOOLEAN));
}
return whileNode;
}
@Override
public Node leaveWithNode(final WithNode withNode) {
return withNode.setExpression(getLexicalContext(), convert(withNode.getExpression(), Type.OBJECT));
return withNode.setExpression(lc, convert(withNode.getExpression(), Type.OBJECT));
}
private static void updateSymbolsLog(final FunctionNode functionNode, final Symbol symbol, final boolean loseSlot) {
......@@ -550,7 +550,6 @@ final class FinalizeTypes extends NodeOperatorVisitor {
return; // nothing to do
}
final LexicalContext lc = getLexicalContext();
final FunctionNode functionNode = lc.getFunction(block);
final boolean allVarsInScope = functionNode.allVarsInScope();
final boolean isVarArg = functionNode.isVarArg();
......@@ -652,7 +651,7 @@ final class FinalizeTypes extends NodeOperatorVisitor {
private static void setCanBePrimitive(final Node node, final Type to) {
final HashSet<Node> exclude = new HashSet<>();
node.accept(new NodeVisitor() {
node.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
private void setCanBePrimitive(final Symbol symbol) {
LOG.info("*** can be primitive symbol ", symbol, " ", Debug.id(symbol));
symbol.setCanBePrimitive(to);
......@@ -762,7 +761,7 @@ final class FinalizeTypes extends NodeOperatorVisitor {
}
}
LOG.info("Type override for lhs in '", node, "' => ", to);
return ((TypeOverride<T>)node).setType(temporarySymbols, getLexicalContext(), to);
return ((TypeOverride<T>)node).setType(temporarySymbols, lc, to);
}
/**
......@@ -785,8 +784,8 @@ final class FinalizeTypes extends NodeOperatorVisitor {
private Node convert(final Node node, final Type to) {
assert !to.isUnknown() : "unknown type for " + node + " class=" + node.getClass();
assert node != null : "node is null";
assert node.getSymbol() != null : "node " + node + " " + node.getClass() + " has no symbol! " + getLexicalContext().getCurrentFunction();
assert node.tokenType() != TokenType.CONVERT : "assert convert in convert " + node + " in " + getLexicalContext().getCurrentFunction();
assert node.getSymbol() != null : "node " + node + " " + node.getClass() + " has no symbol! " + lc.getCurrentFunction();
assert node.tokenType() != TokenType.CONVERT : "assert convert in convert " + node + " in " + lc.getCurrentFunction();
final Type from = node.getType();
......@@ -800,7 +799,7 @@ final class FinalizeTypes extends NodeOperatorVisitor {
Node resultNode = node;
if (node instanceof LiteralNode && !to.isObject()) {
if (node instanceof LiteralNode && !(node instanceof ArrayLiteralNode) && !to.isObject()) {
final LiteralNode<?> newNode = new LiteralNodeConstantEvaluator((LiteralNode<?>)node, to).eval();
if (newNode != null) {
resultNode = newNode;
......@@ -817,7 +816,6 @@ final class FinalizeTypes extends NodeOperatorVisitor {
assert !node.isTerminal();
final LexicalContext lc = getLexicalContext();
//This is the only place in this file that can create new temporaries
//FinalizeTypes may not introduce ANY node that is not a conversion.
return temporarySymbols.ensureSymbol(lc, to, resultNode);
......@@ -854,7 +852,7 @@ final class FinalizeTypes extends NodeOperatorVisitor {
symbol = symbol.setTypeOverrideShared(to, temporarySymbols);
LOG.info("Type override for temporary in '", node, "' => ", to);
}
return node.setSymbol(getLexicalContext(), symbol);
return node.setSymbol(lc, symbol);
}
/**
......@@ -907,7 +905,7 @@ final class FinalizeTypes extends NodeOperatorVisitor {
if (literalNode != null) {
//inherit literal symbol for attr.
literalNode = (LiteralNode<?>)literalNode.setSymbol(getLexicalContext(), parent.getSymbol());
literalNode = (LiteralNode<?>)literalNode.setSymbol(lc, parent.getSymbol());
}
return literalNode;
......
......@@ -33,7 +33,9 @@ import jdk.nashorn.internal.ir.ExecuteNode;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.ir.IfNode;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.TernaryNode;
import jdk.nashorn.internal.ir.UnaryNode;
......@@ -45,11 +47,12 @@ import jdk.nashorn.internal.runtime.ScriptRuntime;
/**
* Simple constant folding pass, executed before IR is starting to be lowered.
*/
final class FoldConstants extends NodeVisitor {
final class FoldConstants extends NodeVisitor<LexicalContext> {
private static final DebugLogger LOG = new DebugLogger("fold");
FoldConstants() {
super(new LexicalContext());
}
@Override
......@@ -79,7 +82,7 @@ final class FoldConstants extends NodeVisitor {
@Override
public Node leaveFunctionNode(final FunctionNode functionNode) {
return functionNode.setState(getLexicalContext(), CompilationState.CONSTANT_FOLDED);
return functionNode.setState(lc, CompilationState.CONSTANT_FOLDED);
}
@Override
......@@ -141,6 +144,10 @@ final class FoldConstants extends NodeVisitor {
return null;
}
if (rhsNode instanceof ArrayLiteralNode) {
return null;
}
final LiteralNode<?> rhs = (LiteralNode<?>)rhsNode;
final boolean rhsInteger = rhs.getType().isInteger();
......@@ -212,6 +219,10 @@ final class FoldConstants extends NodeVisitor {
final LiteralNode<?> lhs = (LiteralNode<?>)parent.lhs();
final LiteralNode<?> rhs = (LiteralNode<?>)parent.rhs();
if (lhs instanceof ArrayLiteralNode || rhs instanceof ArrayLiteralNode) {
return null;
}
final Type widest = Type.widest(lhs.getType(), rhs.getType());
boolean isInteger = widest.isInteger();
......@@ -279,9 +290,9 @@ final class FoldConstants extends NodeVisitor {
isLong &= value != 0.0 && JSType.isRepresentableAsLong(value);
if (isInteger) {
return LiteralNode.newInstance(token, finish, JSType.toInt32(value));
return LiteralNode.newInstance(token, finish, (int)value);
} else if (isLong) {
return LiteralNode.newInstance(token, finish, JSType.toLong(value));
return LiteralNode.newInstance(token, finish, (long)value);
}
return LiteralNode.newInstance(token, finish, value);
......
......@@ -80,7 +80,7 @@ import jdk.nashorn.internal.runtime.Source;
* finalized.
*/
final class Lower extends NodeOperatorVisitor {
final class Lower extends NodeOperatorVisitor<BlockLexicalContext> {
private static final DebugLogger LOG = new DebugLogger("lower");
......@@ -105,7 +105,7 @@ final class Lower extends NodeOperatorVisitor {
terminated = true;
}
} else {
statement.accept(new NodeVisitor() {
statement.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
@Override
public boolean enterVarNode(final VarNode varNode) {
newStatements.add(varNode.setInit(null));
......@@ -121,7 +121,6 @@ final class Lower extends NodeOperatorVisitor {
@Override
public boolean enterBlock(final Block block) {
final LexicalContext lc = getLexicalContext();
final FunctionNode function = lc.getCurrentFunction();
if (lc.isFunctionBody() && function.isProgram() && !function.hasDeclaredFunctions()) {
new ExecuteNode(block.getLineNumber(), block.getToken(), block.getFinish(), LiteralNode.newInstance(block, ScriptRuntime.UNDEFINED)).accept(this);
......@@ -134,12 +133,10 @@ final class Lower extends NodeOperatorVisitor {
//now we have committed the entire statement list to the block, but we need to truncate
//whatever is after the last terminal. block append won't append past it
final BlockLexicalContext lc = (BlockLexicalContext)getLexicalContext();
Statement last = lc.getLastStatement();
if (lc.isFunctionBody()) {
final FunctionNode currentFunction = getLexicalContext().getCurrentFunction();
final FunctionNode currentFunction = lc.getCurrentFunction();
final boolean isProgram = currentFunction.isProgram();
final ReturnNode returnNode = new ReturnNode(
last == null ? block.getLineNumber() : last.getLineNumber(), //TODO?
......@@ -191,7 +188,7 @@ final class Lower extends NodeOperatorVisitor {
final Node expr = executeNode.getExpression();
ExecuteNode node = executeNode;
final FunctionNode currentFunction = getLexicalContext().getCurrentFunction();
final FunctionNode currentFunction = lc.getCurrentFunction();
if (currentFunction.isProgram()) {
if (!(expr instanceof Block) || expr instanceof FunctionNode) { // it's not a block, but can be a function
......@@ -216,7 +213,7 @@ final class Lower extends NodeOperatorVisitor {
final Node test = forNode.getTest();
if (!forNode.isForIn() && conservativeAlwaysTrue(test)) {
newForNode = forNode.setTest(getLexicalContext(), null);
newForNode = forNode.setTest(lc, null);
}
return addStatement(checkEscape(newForNode));
......@@ -230,7 +227,7 @@ final class Lower extends NodeOperatorVisitor {
@Override
public Node leaveFunctionNode(final FunctionNode functionNode) {
LOG.info("END FunctionNode: ", functionNode.getName());
return functionNode.setState(getLexicalContext(), CompilationState.LOWERED);
return functionNode.setState(lc, CompilationState.LOWERED);
}
@Override
......@@ -261,19 +258,25 @@ final class Lower extends NodeOperatorVisitor {
return throwNode;
}
private static Node ensureUniqueLabelsIn(final Node node) {
return node.accept(new NodeVisitor() {
@Override
public Node leaveDefault(final Node labelledNode) {
return labelledNode.ensureUniqueLabels(getLexicalContext());
}
private static Node ensureUniqueNamesIn(final LexicalContext lc, final Node node) {
return node.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
@Override
public Node leaveFunctionNode(final FunctionNode functionNode) {
final String name = functionNode.getName();
return functionNode.setName(lc, lc.getCurrentFunction().uniqueName(name));
}
@Override
public Node leaveDefault(final Node labelledNode) {
return labelledNode.ensureUniqueLabels(lc);
}
});
}
private static List<Statement> copyFinally(final Block finallyBody) {
private static List<Statement> copyFinally(final LexicalContext lc, final Block finallyBody) {
final List<Statement> newStatements = new ArrayList<>();
for (final Statement statement : finallyBody.getStatements()) {
newStatements.add((Statement)ensureUniqueLabelsIn(statement));
newStatements.add((Statement)ensureUniqueNamesIn(lc, statement));
if (statement.hasTerminalFlags()) {
return newStatements;
}
......@@ -286,12 +289,12 @@ final class Lower extends NodeOperatorVisitor {
final long token = tryNode.getToken();
final int finish = tryNode.getFinish();
final IdentNode exception = new IdentNode(token, finish, getLexicalContext().getCurrentFunction().uniqueName("catch_all"));
final IdentNode exception = new IdentNode(token, finish, lc.getCurrentFunction().uniqueName("catch_all"));
final Block catchBody = new Block(lineNumber, token, finish, new ThrowNode(lineNumber, token, finish, new IdentNode(exception))).
setIsTerminal(getLexicalContext(), true); //ends with throw, so terminal
final Block catchBody = new Block(lineNumber, token, finish, new ThrowNode(lineNumber, token, finish, new IdentNode(exception), ThrowNode.IS_SYNTHETIC_RETHROW)).
setIsTerminal(lc, true); //ends with throw, so terminal
final CatchNode catchAllNode = new CatchNode(lineNumber, token, finish, new IdentNode(exception), null, catchBody);
final CatchNode catchAllNode = new CatchNode(lineNumber, token, finish, new IdentNode(exception), null, catchBody, CatchNode.IS_SYNTHETIC_RETHROW);
final Block catchAllBlock = new Block(lineNumber, token, finish, catchAllNode);
//catchallblock -> catchallnode (catchnode) -> exception -> throw
......@@ -300,7 +303,7 @@ final class Lower extends NodeOperatorVisitor {
}
private IdentNode compilerConstant(final CompilerConstants cc) {
final FunctionNode functionNode = getLexicalContext().getCurrentFunction();
final FunctionNode functionNode = lc.getCurrentFunction();
return new IdentNode(functionNode.getToken(), functionNode.getFinish(), cc.symbolName());
}
......@@ -316,11 +319,10 @@ final class Lower extends NodeOperatorVisitor {
* @return new try node after splicing finally code (same if nop)
*/
private Node spliceFinally(final TryNode tryNode, final List<ThrowNode> rethrows, final Block finallyBody) {
final int finish = tryNode.getFinish();
assert tryNode.getFinallyBody() == null;
final int finish = tryNode.getFinish();
final TryNode newTryNode = (TryNode)tryNode.accept(new NodeVisitor() {
final TryNode newTryNode = (TryNode)tryNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
final List<Node> insideTry = new ArrayList<>();
@Override
......@@ -338,7 +340,7 @@ final class Lower extends NodeOperatorVisitor {
@Override
public Node leaveThrowNode(final ThrowNode throwNode) {
if (rethrows.contains(throwNode)) {
final List<Statement> newStatements = copyFinally(finallyBody);
final List<Statement> newStatements = copyFinally(lc, finallyBody);
if (!isTerminal(newStatements)) {
newStatements.add(throwNode);
}
......@@ -349,12 +351,12 @@ final class Lower extends NodeOperatorVisitor {
@Override
public Node leaveBreakNode(final BreakNode breakNode) {
return copy(breakNode, Lower.this.getLexicalContext().getBreakable(breakNode.getLabel()));
return copy(breakNode, Lower.this.lc.getBreakable(breakNode.getLabel()));
}
@Override
public Node leaveContinueNode(final ContinueNode continueNode) {
return copy(continueNode, Lower.this.getLexicalContext().getContinueTo(continueNode.getLabel()));
return copy(continueNode, Lower.this.lc.getContinueTo(continueNode.getLabel()));
}
@Override
......@@ -372,17 +374,17 @@ final class Lower extends NodeOperatorVisitor {
resultNode = null;
}
newStatements.addAll(copyFinally(finallyBody));
newStatements.addAll(copyFinally(lc, finallyBody));
if (!isTerminal(newStatements)) {
newStatements.add(expr == null ? returnNode : returnNode.setExpression(resultNode));
}
return new ExecuteNode(returnNode.getLineNumber(), returnNode.getToken(), returnNode.getFinish(), new Block(returnNode.getLineNumber(), returnNode.getToken(), getLexicalContext().getCurrentBlock().getFinish(), newStatements));
return new ExecuteNode(returnNode.getLineNumber(), returnNode.getToken(), returnNode.getFinish(), new Block(returnNode.getLineNumber(), returnNode.getToken(), lc.getCurrentBlock().getFinish(), newStatements));
}
private Node copy(final Statement endpoint, final Node targetNode) {
if (!insideTry.contains(targetNode)) {
final List<Statement> newStatements = copyFinally(finallyBody);
final List<Statement> newStatements = copyFinally(lc, finallyBody);
if (!isTerminal(newStatements)) {
newStatements.add(endpoint);
}
......@@ -436,7 +438,7 @@ final class Lower extends NodeOperatorVisitor {
final Block catchAll = catchAllBlock(tryNode);
final List<ThrowNode> rethrows = new ArrayList<>();
catchAll.accept(new NodeVisitor() {
catchAll.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
@Override
public boolean enterThrowNode(final ThrowNode throwNode) {
rethrows.add(throwNode);
......@@ -464,7 +466,7 @@ final class Lower extends NodeOperatorVisitor {
@Override
public Node leaveVarNode(final VarNode varNode) {
addStatement(varNode);
if (varNode.getFlag(VarNode.IS_LAST_FUNCTION_DECLARATION) && getLexicalContext().getCurrentFunction().isProgram()) {
if (varNode.getFlag(VarNode.IS_LAST_FUNCTION_DECLARATION) && lc.getCurrentFunction().isProgram()) {
new ExecuteNode(varNode.getLineNumber(), varNode.getToken(), varNode.getFinish(), new IdentNode(varNode.getName())).accept(this);
}
return varNode;
......@@ -478,7 +480,7 @@ final class Lower extends NodeOperatorVisitor {
if (conservativeAlwaysTrue(test)) {
//turn it into a for node without a test.
final ForNode forNode = (ForNode)new ForNode(whileNode.getLineNumber(), whileNode.getToken(), whileNode.getFinish(), null, null, body, null, ForNode.IS_FOR).accept(this);
getLexicalContext().replace(whileNode, forNode);
lc.replace(whileNode, forNode);
return forNode;
}
......@@ -513,7 +515,7 @@ final class Lower extends NodeOperatorVisitor {
* @return eval location
*/
private String evalLocation(final IdentNode node) {
final Source source = getLexicalContext().getCurrentFunction().getSource();
final Source source = lc.getCurrentFunction().getSource();
return new StringBuilder().
append(source.getName()).
append('#').
......@@ -545,10 +547,10 @@ final class Lower extends NodeOperatorVisitor {
// 'eval' call with at least one argument
if (args.size() >= 1 && EVAL.symbolName().equals(callee.getName())) {
final FunctionNode currentFunction = getLexicalContext().getCurrentFunction();
final FunctionNode currentFunction = lc.getCurrentFunction();
return callNode.setEvalArgs(
new CallNode.EvalArgs(
ensureUniqueLabelsIn(args.get(0)).accept(this),
ensureUniqueNamesIn(lc, args.get(0)).accept(this),
compilerConstant(THIS),
evalLocation(callee),
currentFunction.isStrict()));
......@@ -574,7 +576,7 @@ final class Lower extends NodeOperatorVisitor {
private static boolean controlFlowEscapes(final LexicalContext lex, final Block loopBody) {
final List<Node> escapes = new ArrayList<>();
loopBody.accept(new NodeVisitor() {
loopBody.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
@Override
public Node leaveBreakNode(final BreakNode node) {
escapes.add(node);
......@@ -595,7 +597,6 @@ final class Lower extends NodeOperatorVisitor {
}
private LoopNode checkEscape(final LoopNode loopNode) {
final LexicalContext lc = getLexicalContext();
final boolean escapes = controlFlowEscapes(lc, loopNode.getBody());
if (escapes) {
return loopNode.
......@@ -607,7 +608,7 @@ final class Lower extends NodeOperatorVisitor {
private Node addStatement(final Statement statement) {
((BlockLexicalContext)getLexicalContext()).appendStatement(statement);
lc.appendStatement(statement);
return statement;
}
......
......@@ -2081,7 +2081,9 @@ public class MethodEmitter implements Emitter {
* @param args debug information to print
*/
private void debug(final Object... args) {
debug(30, args);
if (DEBUG) {
debug(30, args);
}
}
/**
......@@ -2091,7 +2093,9 @@ public class MethodEmitter implements Emitter {
* @param args debug information to print
*/
private void debug_label(final Object... args) {
debug(26, args);
if (DEBUG) {
debug(22, args);
}
}
private void debug(final int padConstant, final Object... args) {
......@@ -2164,7 +2168,6 @@ public class MethodEmitter implements Emitter {
new Throwable().printStackTrace(LOG.getOutputStream());
}
}
}
}
......
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.codegen;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import jdk.nashorn.internal.codegen.types.Range;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.Assignment;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.ForNode;
import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
import jdk.nashorn.internal.ir.LoopNode;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.RuntimeNode;
import jdk.nashorn.internal.ir.Symbol;
import jdk.nashorn.internal.ir.UnaryNode;
import jdk.nashorn.internal.ir.VarNode;
import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.TokenType;
import jdk.nashorn.internal.runtime.DebugLogger;
/**
* Range analysis and narrowing of type where it can be proven
* that there is no spillover, e.g.
*
* function func(c) {
* var v = c & 0xfff;
* var w = c & 0xeee;
* var x = v * w;
* return x;
* }
*
* Proves that the multiplication never exceeds 24 bits and can thus be an int
*/
final class RangeAnalyzer extends NodeOperatorVisitor<LexicalContext> {
static final DebugLogger LOG = new DebugLogger("ranges");
private static final Range.Functionality RANGE = new Range.Functionality(LOG);
private final Map<LoopNode, Symbol> loopCounters = new HashMap<>();
RangeAnalyzer() {
super(new LexicalContext());
}
@Override
public boolean enterForNode(final ForNode forNode) {
//conservatively attempt to identify the loop counter. Null means that it wasn't
//properly identified and that no optimizations can be made with it - its range is
//simply unknown in that case, if it is assigned in the loop
final Symbol counter = findLoopCounter(forNode);
LOG.fine("Entering forNode " + forNode + " counter = " + counter);
if (counter != null && !assignedInLoop(forNode, counter)) {
loopCounters.put(forNode, counter);
}
return true;
}
//destination visited
private Symbol setRange(final Node dest, final Range range) {
if (range.isUnknown()) {
return null;
}
final Symbol symbol = dest.getSymbol();
assert symbol != null : dest + " " + dest.getClass() + " has no symbol";
assert symbol.getRange() != null : symbol + " has no range";
final Range symRange = RANGE.join(symbol.getRange(), range);
//anything assigned in the loop, not being the safe loop counter(s) invalidates its entire range
if (lc.inLoop() && !isLoopCounter(lc.getCurrentLoop(), symbol)) {
symbol.setRange(Range.createGenericRange());
return symbol;
}
if (!symRange.equals(symbol.getRange())) {
LOG.fine("Modify range for " + dest + " " + symbol + " from " + symbol.getRange() + " to " + symRange + " (in node = " + dest + ")" );
symbol.setRange(symRange);
}
return null;
}
@Override
public Node leaveADD(final BinaryNode node) {
setRange(node, RANGE.add(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
return node;
}
@Override
public Node leaveSUB(final BinaryNode node) {
setRange(node, RANGE.sub(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
return node;
}
@Override
public Node leaveMUL(final BinaryNode node) {
setRange(node, RANGE.mul(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
return node;
}
@Override
public Node leaveDIV(final BinaryNode node) {
setRange(node, RANGE.div(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
return node;
}
@Override
public Node leaveMOD(final BinaryNode node) {
setRange(node, RANGE.mod(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
return node;
}
@Override
public Node leaveBIT_AND(final BinaryNode node) {
setRange(node, RANGE.and(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
return node;
}
@Override
public Node leaveBIT_OR(final BinaryNode node) {
setRange(node, RANGE.or(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
return node;
}
@Override
public Node leaveBIT_XOR(final BinaryNode node) {
setRange(node, RANGE.xor(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
return node;
}
@Override
public Node leaveSAR(final BinaryNode node) {
setRange(node, RANGE.sar(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
return node;
}
@Override
public Node leaveSHL(final BinaryNode node) {
setRange(node, RANGE.shl(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
return node;
}
@Override
public Node leaveSHR(final BinaryNode node) {
setRange(node, RANGE.shr(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
return node;
}
private Node leaveCmp(final BinaryNode node) {
setRange(node, Range.createTypeRange(Type.BOOLEAN));
return node;
}
@Override
public Node leaveEQ(final BinaryNode node) {
return leaveCmp(node);
}
@Override
public Node leaveEQ_STRICT(final BinaryNode node) {
return leaveCmp(node);
}
@Override
public Node leaveNE(final BinaryNode node) {
return leaveCmp(node);
}
@Override
public Node leaveNE_STRICT(final BinaryNode node) {
return leaveCmp(node);
}
@Override
public Node leaveLT(final BinaryNode node) {
return leaveCmp(node);
}
@Override
public Node leaveLE(final BinaryNode node) {
return leaveCmp(node);
}
@Override
public Node leaveGT(final BinaryNode node) {
return leaveCmp(node);
}
@Override
public Node leaveGE(final BinaryNode node) {
return leaveCmp(node);
}
@Override
public Node leaveASSIGN(final BinaryNode node) {
Range range = node.rhs().getSymbol().getRange();
if (range.isUnknown()) {
range = Range.createGenericRange();
}
setRange(node.lhs(), range);
setRange(node, range);
return node;
}
private Node leaveSelfModifyingAssign(final BinaryNode node, final Range range) {
setRange(node.lhs(), range);
setRange(node, range);
return node;
}
private Node leaveSelfModifyingAssign(final UnaryNode node, final Range range) {
setRange(node.rhs(), range);
setRange(node, range);
return node;
}
@Override
public Node leaveASSIGN_ADD(final BinaryNode node) {
return leaveSelfModifyingAssign(node, RANGE.add(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
}
@Override
public Node leaveASSIGN_SUB(final BinaryNode node) {
return leaveSelfModifyingAssign(node, RANGE.sub(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
}
@Override
public Node leaveASSIGN_MUL(final BinaryNode node) {
return leaveSelfModifyingAssign(node, RANGE.mul(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
}
@Override
public Node leaveASSIGN_DIV(final BinaryNode node) {
return leaveSelfModifyingAssign(node, Range.createTypeRange(Type.NUMBER));
}
@Override
public Node leaveASSIGN_MOD(final BinaryNode node) {
return leaveSelfModifyingAssign(node, Range.createTypeRange(Type.NUMBER));
}
@Override
public Node leaveASSIGN_BIT_AND(final BinaryNode node) {
return leaveSelfModifyingAssign(node, RANGE.and(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
}
@Override
public Node leaveASSIGN_BIT_OR(final BinaryNode node) {
return leaveSelfModifyingAssign(node, RANGE.or(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
}
@Override
public Node leaveASSIGN_BIT_XOR(final BinaryNode node) {
return leaveSelfModifyingAssign(node, RANGE.xor(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
}
@Override
public Node leaveASSIGN_SAR(final BinaryNode node) {
return leaveSelfModifyingAssign(node, RANGE.sar(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
}
@Override
public Node leaveASSIGN_SHR(final BinaryNode node) {
return leaveSelfModifyingAssign(node, RANGE.shr(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
}
@Override
public Node leaveASSIGN_SHL(final BinaryNode node) {
return leaveSelfModifyingAssign(node, RANGE.shl(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange()));
}
@Override
public Node leaveDECINC(final UnaryNode node) {
switch (node.tokenType()) {
case DECPREFIX:
case DECPOSTFIX:
return leaveSelfModifyingAssign(node, RANGE.sub(node.rhs().getSymbol().getRange(), Range.createRange(1)));
case INCPREFIX:
case INCPOSTFIX:
return leaveSelfModifyingAssign(node, RANGE.add(node.rhs().getSymbol().getRange(), Range.createRange(1)));
default:
assert false;
return node;
}
}
@Override
public Node leaveADD(final UnaryNode node) {
Range range = node.rhs().getSymbol().getRange();
if (!range.getType().isNumeric()) {
range = Range.createTypeRange(Type.NUMBER);
}
setRange(node, range);
return node;
}
@Override
public Node leaveBIT_NOT(final UnaryNode node) {
setRange(node, Range.createTypeRange(Type.INT));
return node;
}
@Override
public Node leaveNOT(final UnaryNode node) {
setRange(node, Range.createTypeRange(Type.BOOLEAN));
return node;
}
@Override
public Node leaveSUB(final UnaryNode node) {
setRange(node, RANGE.neg(node.rhs().getSymbol().getRange()));
return node;
}
@Override
public Node leaveVarNode(final VarNode node) {
if (node.isAssignment()) {
Range range = node.getInit().getSymbol().getRange();
range = range.isUnknown() ? Range.createGenericRange() : range;
setRange(node.getName(), range);
setRange(node, range);
}
return node;
}
@SuppressWarnings("rawtypes")
@Override
public boolean enterLiteralNode(final LiteralNode node) {
// ignore array literals
return !(node instanceof ArrayLiteralNode);
}
@Override
public Node leaveLiteralNode(@SuppressWarnings("rawtypes") final LiteralNode node) {
if (node.getType().isInteger()) {
setRange(node, Range.createRange(node.getInt32()));
} else if (node.getType().isNumber()) {
setRange(node, Range.createRange(node.getNumber()));
} else if (node.getType().isLong()) {
setRange(node, Range.createRange(node.getLong()));
} else if (node.getType().isBoolean()) {
setRange(node, Range.createTypeRange(Type.BOOLEAN));
} else {
setRange(node, Range.createGenericRange());
}
return node;
}
@Override
public boolean enterRuntimeNode(final RuntimeNode node) {
// a runtime node that cannot be specialized is no point entering
return node.getRequest().canSpecialize();
}
/**
* Check whether a symbol is unsafely assigned in a loop - i.e. repeteadly assigned and
* not being identified as the loop counter. That means we don't really know anything
* about its range.
* @param loopNode loop node
* @param symbol symbol
* @return true if assigned in loop
*/
// TODO - this currently checks for nodes only - needs to be augmented for while nodes
// assignment analysis is also very conservative
private static boolean assignedInLoop(final LoopNode loopNode, final Symbol symbol) {
final HashSet<Node> skip = new HashSet<>();
final HashSet<Node> assignmentsInLoop = new HashSet<>();
loopNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
private boolean assigns(final Node node, final Symbol s) {
return node.isAssignment() && ((Assignment<?>)node).getAssignmentDest().getSymbol() == s;
}
@Override
public boolean enterForNode(final ForNode forNode) {
if (forNode.getInit() != null) {
skip.add(forNode.getInit());
}
if (forNode.getModify() != null) {
skip.add(forNode.getModify());
}
return true;
}
@Override
public Node leaveDefault(final Node node) {
//if this is an assignment to symbol
if (!skip.contains(node) && assigns(node, symbol)) {
assignmentsInLoop.add(node);
}
return node;
}
});
return !assignmentsInLoop.isEmpty();
}
/**
* Check for a loop counter. This is currently quite conservative, in that it only handles
* x <= counter and x < counter.
*
* @param node loop node to check
* @return
*/
private static Symbol findLoopCounter(final LoopNode node) {
final Node test = node.getTest();
if (test != null && test.isComparison()) {
final BinaryNode binaryNode = (BinaryNode)test;
final Node lhs = binaryNode.lhs();
final Node rhs = binaryNode.rhs();
//detect ident cmp int_literal
if (lhs instanceof IdentNode && rhs instanceof LiteralNode && ((LiteralNode<?>)rhs).getType().isInteger()) {
final Symbol symbol = lhs.getSymbol();
final int margin = ((LiteralNode<?>)rhs).getInt32();
final TokenType op = test.tokenType();
switch (op) {
case LT:
case LE:
symbol.setRange(RANGE.join(symbol.getRange(), Range.createRange(op == TokenType.LT ? margin - 1 : margin)));
return symbol;
case GT:
case GE:
//setRange(lhs, Range.createRange(op == TokenType.GT ? margin + 1 : margin));
//return symbol;
default:
break;
}
}
}
return null;
}
private boolean isLoopCounter(final LoopNode loopNode, final Symbol symbol) {
//this only works if loop nodes aren't replaced by other ones during this transform, but they are not
return loopCounters.get(loopNode) == symbol;
}
}
......@@ -116,9 +116,10 @@ class SharedScopeCall {
/**
* Generate the invoke instruction for this shared scope call.
* @param method the method emitter
* @return the method emitter
*/
public void generateInvoke(final MethodEmitter method) {
method.invokestatic(compileUnit.getUnitClassName(), methodName, getStaticSignature());
public MethodEmitter generateInvoke(final MethodEmitter method) {
return method.invokestatic(compileUnit.getUnitClassName(), methodName, getStaticSignature());
}
/**
......
......@@ -49,12 +49,12 @@ import jdk.nashorn.internal.runtime.options.Options;
/**
* Split the IR into smaller compile units.
*/
final class Splitter extends NodeVisitor {
final class Splitter extends NodeVisitor<LexicalContext> {
/** Current compiler. */
private final Compiler compiler;
/** IR to be broken down. */
private FunctionNode outermost;
private final FunctionNode outermost;
/** Compile unit for the main script. */
private final CompileUnit outermostCompileUnit;
......@@ -75,6 +75,7 @@ final class Splitter extends NodeVisitor {
* @param outermostCompileUnit compile unit for outermost function, if non-lazy this is the script's compile unit
*/
public Splitter(final Compiler compiler, final FunctionNode functionNode, final CompileUnit outermostCompileUnit) {
super(new LexicalContext());
this.compiler = compiler;
this.outermost = functionNode;
this.outermostCompileUnit = outermostCompileUnit;
......@@ -93,8 +94,6 @@ final class Splitter extends NodeVisitor {
LOG.finest("Initiating split of '", functionNode.getName(), "'");
final LexicalContext lc = getLexicalContext();
long weight = WeighNodes.weigh(functionNode);
final boolean top = fn.isProgram(); //compiler.getFunctionNode() == outermost;
......@@ -127,7 +126,7 @@ final class Splitter extends NodeVisitor {
final Block body = functionNode.getBody();
final List<FunctionNode> dc = directChildren(functionNode);
final Block newBody = (Block)body.accept(new NodeVisitor() {
final Block newBody = (Block)body.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
@Override
public boolean enterFunctionNode(final FunctionNode nestedFunction) {
return dc.contains(nestedFunction);
......@@ -136,7 +135,7 @@ final class Splitter extends NodeVisitor {
@Override
public Node leaveFunctionNode(final FunctionNode nestedFunction) {
FunctionNode split = new Splitter(compiler, nestedFunction, outermostCompileUnit).split(nestedFunction);
getLexicalContext().replace(nestedFunction, split);
lc.replace(nestedFunction, split);
return split;
}
});
......@@ -149,13 +148,13 @@ final class Splitter extends NodeVisitor {
private static List<FunctionNode> directChildren(final FunctionNode functionNode) {
final List<FunctionNode> dc = new ArrayList<>();
functionNode.accept(new NodeVisitor() {
functionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
@Override
public boolean enterFunctionNode(final FunctionNode child) {
if (child == functionNode) {
return true;
}
if (getLexicalContext().getParentFunction(child) == functionNode) {
if (lc.getParentFunction(child) == functionNode) {
dc.add(child);
}
return false;
......@@ -181,7 +180,7 @@ final class Splitter extends NodeVisitor {
* @return new weight for the resulting block.
*/
private Block splitBlock(final Block block, final FunctionNode function) {
getLexicalContext().setFlag(getLexicalContext().getCurrentFunction(), FunctionNode.IS_SPLIT);
lc.setFlag(lc.getCurrentFunction(), FunctionNode.IS_SPLIT);
final List<Statement> splits = new ArrayList<>();
List<Statement> statements = new ArrayList<>();
......@@ -210,7 +209,7 @@ final class Splitter extends NodeVisitor {
splits.add(createBlockSplitNode(block, function, statements, statementsWeight));
}
return block.setStatements(getLexicalContext(), splits);
return block.setStatements(lc, splits);
}
/**
......@@ -258,7 +257,7 @@ final class Splitter extends NodeVisitor {
// been split already, so weigh again before splitting.
long weight = WeighNodes.weigh(block, weightCache);
if (weight >= SPLIT_THRESHOLD) {
newBlock = splitBlock(block, getLexicalContext().getFunction(block));
newBlock = splitBlock(block, lc.getFunction(block));
weight = WeighNodes.weigh(newBlock, weightCache);
}
weightCache.put(newBlock, weight);
......@@ -274,9 +273,9 @@ final class Splitter extends NodeVisitor {
return literal;
}
final FunctionNode functionNode = getLexicalContext().getCurrentFunction();
final FunctionNode functionNode = lc.getCurrentFunction();
getLexicalContext().setFlag(functionNode, FunctionNode.IS_SPLIT);
lc.setFlag(functionNode, FunctionNode.IS_SPLIT);
if (literal instanceof ArrayLiteralNode) {
final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode) literal;
......
......@@ -27,6 +27,7 @@ package jdk.nashorn.internal.codegen;
import java.util.List;
import java.util.Map;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.AccessNode;
import jdk.nashorn.internal.ir.BinaryNode;
......@@ -41,6 +42,7 @@ import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.IfNode;
import jdk.nashorn.internal.ir.IndexNode;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit;
......@@ -63,7 +65,7 @@ import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
* Computes the "byte code" weight of an AST segment. This is used
* for Splitting too large class files
*/
final class WeighNodes extends NodeOperatorVisitor {
final class WeighNodes extends NodeOperatorVisitor<LexicalContext> {
/*
* Weight constants.
*/
......@@ -100,7 +102,7 @@ final class WeighNodes extends NodeOperatorVisitor {
* @param weightCache cache of already calculated block weights
*/
private WeighNodes(FunctionNode topFunction, final Map<Node, Long> weightCache) {
super();
super(new LexicalContext());
this.topFunction = topFunction;
this.weightCache = weightCache;
}
......
......@@ -106,22 +106,12 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
Type(final String name, final Class<?> clazz, final int weight, final int slots) {
this.name = name;
this.clazz = clazz;
this.descriptor = Type.getDescriptor(clazz);
this.descriptor = jdk.internal.org.objectweb.asm.Type.getDescriptor(clazz);
this.weight = weight;
assert weight >= MIN_WEIGHT && weight <= MAX_WEIGHT : "illegal type weight: " + weight;
this.slots = slots;
}
/**
* Return an internal descriptor for a type
*
* @param type the type
* @return descriptor string
*/
public static String getDescriptor(final Class<?> type) {
return jdk.internal.org.objectweb.asm.Type.getDescriptor(type);
}
/**
* Get the weight of this type - use this e.g. for sorting method descriptors
* @return the weight
......
......@@ -60,7 +60,7 @@ public final class AccessNode extends BaseNode {
* @param visitor IR navigating visitor.
*/
@Override
public Node accept(final NodeVisitor visitor) {
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterAccessNode(this)) {
return visitor.leaveAccessNode(
setBase(base.accept(visitor)).
......@@ -110,7 +110,6 @@ public final class AccessNode extends BaseNode {
return new AccessNode(this, base, property, isFunction(), hasCallSiteType());
}
private AccessNode setProperty(final IdentNode property) {
if (this.property == property) {
return this;
......
......@@ -59,6 +59,23 @@ public final class BinaryNode extends Node implements Assignment<Node> {
this.rhs = rhs;
}
@Override
public boolean isComparison() {
switch (tokenType()) {
case EQ:
case EQ_STRICT:
case NE:
case NE_STRICT:
case LE:
case LT:
case GE:
case GT:
return true;
default:
return false;
}
}
/**
* Return the widest possible type for this operation. This is used for compile time
* static type inference
......@@ -143,7 +160,7 @@ public final class BinaryNode extends Node implements Assignment<Node> {
* @param visitor IR navigating visitor.
*/
@Override
public Node accept(final NodeVisitor visitor) {
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterBinaryNode(this)) {
return visitor.leaveBinaryNode(setLHS(lhs.accept(visitor)).setRHS(rhs.accept(visitor)));
}
......
......@@ -131,7 +131,7 @@ public class Block extends BreakableNode implements Flags<Block> {
* @return new or same node
*/
@Override
public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterBlock(this)) {
return visitor.leaveBlock(setStatements(lc, Node.accept(visitor, Statement.class, statements)));
}
......
......@@ -59,7 +59,7 @@ public final class BreakNode extends Statement {
* @param visitor IR navigating visitor.
*/
@Override
public Node accept(final NodeVisitor visitor) {
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterBreakNode(this)) {
return visitor.leaveBreakNode(this);
}
......
......@@ -27,6 +27,7 @@ package jdk.nashorn.internal.ir;
import java.util.Collections;
import java.util.List;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.annotations.Ignore;
import jdk.nashorn.internal.ir.annotations.Immutable;
......@@ -194,7 +195,7 @@ public final class CallNode extends LexicalContextNode implements TypeOverride<C
* @return node or replacement
*/
@Override
public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterCallNode(this)) {
final CallNode newCallNode = (CallNode)visitor.leaveCallNode(
setFunction(function.accept(visitor)).
......
......@@ -78,7 +78,7 @@ public final class CaseNode extends Node {
* @param visitor IR navigating visitor.
*/
@Override
public Node accept(final NodeVisitor visitor) {
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterCaseNode(this)) {
final Node newTest = test == null ? null : test.accept(visitor);
final Block newBody = body == null ? null : (Block)body.accept(visitor);
......
......@@ -42,6 +42,11 @@ public final class CatchNode extends Statement {
/** Catch body. */
private final Block body;
private final int flags;
/** Is this block a synthethic rethrow created by finally inlining? */
public static final int IS_SYNTHETIC_RETHROW = 1;
/**
* Constructors
*
......@@ -51,19 +56,22 @@ public final class CatchNode extends Statement {
* @param exception variable name of exception
* @param exceptionCondition exception condition
* @param body catch body
* @param flags flags
*/
public CatchNode(final int lineNumber, final long token, final int finish, final IdentNode exception, final Node exceptionCondition, final Block body) {
public CatchNode(final int lineNumber, final long token, final int finish, final IdentNode exception, final Node exceptionCondition, final Block body, final int flags) {
super(lineNumber, token, finish);
this.exception = exception;
this.exceptionCondition = exceptionCondition;
this.body = body;
this.flags = flags;
}
private CatchNode(final CatchNode catchNode, final IdentNode exception, final Node exceptionCondition, final Block body) {
private CatchNode(final CatchNode catchNode, final IdentNode exception, final Node exceptionCondition, final Block body, final int flags) {
super(catchNode);
this.exception = exception;
this.exceptionCondition = exceptionCondition;
this.body = body;
this.flags = flags;
}
/**
......@@ -71,7 +79,7 @@ public final class CatchNode extends Statement {
* @param visitor IR navigating visitor.
*/
@Override
public Node accept(final NodeVisitor visitor) {
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterCatchNode(this)) {
return visitor.leaveCatchNode(
setException((IdentNode)exception.accept(visitor)).
......@@ -124,7 +132,7 @@ public final class CatchNode extends Statement {
if (this.exceptionCondition == exceptionCondition) {
return this;
}
return new CatchNode(this, exception, exceptionCondition, body);
return new CatchNode(this, exception, exceptionCondition, body, flags);
}
/**
......@@ -144,13 +152,25 @@ public final class CatchNode extends Statement {
if (this.exception == exception) {
return this;
}
return new CatchNode(this, exception, exceptionCondition, body);
return new CatchNode(this, exception, exceptionCondition, body, flags);
}
private CatchNode setBody(final Block body) {
if (this.body == body) {
return this;
}
return new CatchNode(this, exception, exceptionCondition, body);
return new CatchNode(this, exception, exceptionCondition, body, flags);
}
/**
* Is this catch block a non-JavaScript constructor, for example created as
* part of the rethrow mechanism of a finally block in Lower? Then we just
* pass the exception on and need not unwrap whatever is in the ECMAException
* object catch symbol
* @return true if a finally synthetic rethrow
*/
public boolean isSyntheticRethrow() {
return (flags & IS_SYNTHETIC_RETHROW) == IS_SYNTHETIC_RETHROW;
}
}
......@@ -55,7 +55,7 @@ public class ContinueNode extends Statement {
}
@Override
public Node accept(final NodeVisitor visitor) {
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterContinueNode(this)) {
return visitor.leaveContinueNode(this);
}
......
......@@ -56,7 +56,7 @@ public final class EmptyNode extends Statement {
@Override
public Node accept(final NodeVisitor visitor) {
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterEmptyNode(this)) {
return visitor.leaveEmptyNode(this);
}
......
......@@ -62,7 +62,7 @@ public final class ExecuteNode extends Statement {
}
@Override
public Node accept(final NodeVisitor visitor) {
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterExecuteNode(this)) {
return visitor.leaveExecuteNode(setExpression(expression.accept(visitor)));
}
......
......@@ -86,7 +86,7 @@ public final class ForNode extends LoopNode {
}
@Override
protected Node accept(final LexicalContext lc, final NodeVisitor visitor) {
protected Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterForNode(this)) {
return visitor.leaveForNode(
setInit(lc, init == null ? null : init.accept(visitor)).
......
......@@ -250,6 +250,7 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
final FunctionNode functionNode,
final long lastToken,
final int flags,
final String name,
final Type returnType,
final CompileUnit compileUnit,
final EnumSet<CompilationState> compilationState,
......@@ -260,6 +261,7 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
super(functionNode);
this.flags = flags;
this.name = name;
this.returnType = returnType;
this.compileUnit = compileUnit;
this.lastToken = lastToken;
......@@ -271,7 +273,6 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
// the fields below never change - they are final and assigned in constructor
this.source = functionNode.source;
this.name = functionNode.name;
this.ident = functionNode.ident;
this.namespace = functionNode.namespace;
this.declaredSymbols = functionNode.declaredSymbols;
......@@ -280,7 +281,7 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
}
@Override
public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterFunctionNode(this)) {
return visitor.leaveFunctionNode(setBody(lc, (Block)body.accept(visitor)));
}
......@@ -315,7 +316,7 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
if (this.snapshot == null) {
return this;
}
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body, parameters, null, hints));
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, name, returnType, compileUnit, compilationState, body, parameters, null, hints));
}
/**
......@@ -331,7 +332,7 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
if (isProgram() || parameters.isEmpty()) {
return this; //never specialize anything that won't be recompiled
}
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body, parameters, this, hints));
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, name, returnType, compileUnit, compilationState, body, parameters, this, hints));
}
/**
......@@ -339,7 +340,7 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
* @return true if specialization is possible
*/
public boolean canSpecialize() {
return getFlag(CAN_SPECIALIZE);
return snapshot != null && getFlag(CAN_SPECIALIZE);
}
/**
......@@ -389,7 +390,7 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
}
final EnumSet<CompilationState> newState = EnumSet.copyOf(this.compilationState);
newState.add(state);
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, newState, body, parameters, snapshot, hints));
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, name, returnType, compileUnit, newState, body, parameters, snapshot, hints));
}
/**
......@@ -410,7 +411,7 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
if (this.hints == hints) {
return this;
}
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
}
/**
......@@ -463,7 +464,7 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
if (this.flags == flags) {
return this;
}
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
}
@Override
......@@ -529,7 +530,7 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
}
/**
* Get the identifier for this function
* Get the identifier for this function, this is its symbol.
* @return the identifier as an IdentityNode
*/
public IdentNode getIdent() {
......@@ -572,7 +573,7 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
if(this.body == body) {
return this;
}
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
}
/**
......@@ -640,7 +641,7 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
if (this.lastToken == lastToken) {
return this;
}
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
}
/**
......@@ -651,6 +652,20 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
return name;
}
/**
* Set the internal name for this function
* @param lc lexical context
* @param name new name
* @return new function node if changed, otherwise the same
*/
public FunctionNode setName(final LexicalContext lc, final String name) {
if (this.name.equals(name)) {
return this;
}
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
}
/**
* Check if this function should have all its variables in its own scope. Scripts, split sub-functions, and
* functions having with and/or eval blocks are such.
......@@ -698,7 +713,7 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
if (this.parameters == parameters) {
return this;
}
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
}
/**
......@@ -762,6 +777,7 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
this,
lastToken,
flags,
name,
Type.widest(this.returnType, returnType.isObject() ?
Type.OBJECT :
returnType),
......@@ -801,7 +817,7 @@ public final class FunctionNode extends LexicalContextNode implements Flags<Func
if (this.compileUnit == compileUnit) {
return this;
}
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
}
/**
......
......@@ -29,7 +29,6 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.__DIR__;
import static jdk.nashorn.internal.codegen.CompilerConstants.__FILE__;
import static jdk.nashorn.internal.codegen.CompilerConstants.__LINE__;
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.DEBUG_FIELDS;
import jdk.nashorn.internal.codegen.ObjectClassGenerator;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.annotations.Immutable;
......@@ -119,7 +118,7 @@ public final class IdentNode extends Node implements PropertyKey, TypeOverride<I
* @param visitor IR navigating visitor.
*/
@Override
public Node accept(final NodeVisitor visitor) {
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterIdentNode(this)) {
return visitor.leaveIdentNode(this);
}
......
......@@ -72,7 +72,7 @@ public final class IfNode extends Statement {
}
@Override
public Node accept(final NodeVisitor visitor) {
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterIfNode(this)) {
return visitor.leaveIfNode(
setTest(test.accept(visitor)).
......
......@@ -56,19 +56,12 @@ public final class IndexNode extends BaseNode {
}
@Override
public Node accept(final NodeVisitor visitor) {
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterIndexNode(this)) {
final Node newBase = base.accept(visitor);
final Node newIndex = index.accept(visitor);
final IndexNode newNode;
if (newBase != base || newIndex != index) {
newNode = new IndexNode(this, newBase, newIndex, isFunction(), hasCallSiteType());
} else {
newNode = this;
}
return visitor.leaveIndexNode(newNode);
return visitor.leaveIndexNode(
setBase(base.accept(visitor)).
setIndex(index.accept(visitor)));
}
return this;
}
......@@ -106,6 +99,13 @@ public final class IndexNode extends BaseNode {
return index;
}
private IndexNode setBase(final Node base) {
if (this.base == base) {
return this;
}
return new IndexNode(this, base, index, isFunction(), hasCallSiteType());
}
/**
* Set the index expression for this node
* @param index new index expression
......
......@@ -67,11 +67,11 @@ public final class LabelNode extends LexicalContextNode {
}
@Override
public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterLabelNode(this)) {
return visitor.leaveLabelNode(
setLabel(visitor.getLexicalContext(), (IdentNode)label.accept(visitor)).
setBody(visitor.getLexicalContext(), (Block)body.accept(visitor)));
setLabel(lc, (IdentNode)label.accept(visitor)).
setBody(lc, (Block)body.accept(visitor)));
}
return this;
......
......@@ -439,6 +439,23 @@ public class LexicalContext {
return null;
}
/**
* Check whether the lexical context is currently inside a loop
* @return true if inside a loop
*/
public boolean inLoop() {
return getCurrentLoop() != null;
}
/**
* Returns the loop header of the current loop, or null if not inside a loop
* @return loop header
*/
public LoopNode getCurrentLoop() {
final Iterator<LoopNode> iter = new NodeIterator<>(LoopNode.class, getCurrentFunction());
return iter.hasNext() ? iter.next() : null;
}
/**
* Find the breakable node corresponding to this label.
* @param label label to search for, if null the closest breakable node will be returned unconditionally, e.g. a while loop with no label
......@@ -461,8 +478,7 @@ public class LexicalContext {
}
private LoopNode getContinueTo() {
final Iterator<LoopNode> iter = new NodeIterator<>(LoopNode.class, getCurrentFunction());
return iter.hasNext() ? iter.next() : null;
return getCurrentLoop();
}
/**
......
......@@ -60,10 +60,10 @@ public abstract class LexicalContextNode extends Statement {
*
* @return new node or same node depending on state change
*/
protected abstract Node accept(final LexicalContext lc, final NodeVisitor visitor);
protected abstract Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor);
@Override
public Node accept(final NodeVisitor visitor) {
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
final LexicalContext lc = visitor.getLexicalContext();
lc.push(this);
final LexicalContextNode newNode = (LexicalContextNode)accept(lc, visitor);
......
......@@ -28,6 +28,7 @@ package jdk.nashorn.internal.ir;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import jdk.nashorn.internal.codegen.CompileUnit;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.annotations.Immutable;
......@@ -208,7 +209,7 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
* @param visitor IR navigating visitor.
*/
@Override
public Node accept(final NodeVisitor visitor) {
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterLiteralNode(this)) {
return visitor.leaveLiteralNode(this);
}
......@@ -514,7 +515,7 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
}
@Override
public Node accept(final NodeVisitor visitor) {
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterLiteralNode(this)) {
if (value != null) {
final Node newValue = value.accept(visitor);
......@@ -840,7 +841,7 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
}
@Override
public Node accept(final NodeVisitor visitor) {
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterLiteralNode(this)) {
final List<Node> oldValue = Arrays.asList(value);
final List<Node> newValue = Node.accept(visitor, Node.class, oldValue);
......
......@@ -27,6 +27,7 @@ package jdk.nashorn.internal.ir;
import java.util.ArrayList;
import java.util.List;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.Token;
......@@ -152,6 +153,14 @@ public abstract class Node implements Cloneable {
return Type.OBJECT;
}
/**
* Returns true if this node represents a comparison operator
* @return true if comparison
*/
public boolean isComparison() {
return false;
}
/**
* For reference copies - ensure that labels in the copy node are unique
* using an appropriate copy constructor
......@@ -167,7 +176,7 @@ public abstract class Node implements Cloneable {
* @param visitor Node visitor.
* @return node the node or its replacement after visitation, null if no further visitations are required
*/
public abstract Node accept(NodeVisitor visitor);
public abstract Node accept(NodeVisitor<? extends LexicalContext> visitor);
@Override
public String toString() {
......@@ -329,7 +338,7 @@ public abstract class Node implements Cloneable {
}
//on change, we have to replace the entire list, that's we can't simple do ListIterator.set
static <T extends Node> List<T> accept(final NodeVisitor visitor, final Class<T> clazz, final List<T> list) {
static <T extends Node> List<T> accept(final NodeVisitor<? extends LexicalContext> visitor, final Class<T> clazz, final List<T> list) {
boolean changed = false;
final List<T> newList = new ArrayList<>();
......
......@@ -58,7 +58,7 @@ public final class ObjectNode extends Node {
}
@Override
public Node accept(final NodeVisitor visitor) {
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterObjectNode(this)) {
return visitor.leaveObjectNode(setElements(Node.accept(visitor, Node.class, elements)));
}
......
......@@ -81,7 +81,7 @@ public final class PropertyNode extends Node {
}
@Override
public Node accept(final NodeVisitor visitor) {
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterPropertyNode(this)) {
return visitor.leavePropertyNode(
setKey((PropertyKey)((Node)key).accept(visitor)).
......
......@@ -86,7 +86,7 @@ public class ReturnNode extends Statement {
}
@Override
public Node accept(final NodeVisitor visitor) {
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterReturnNode(this)) {
if (expression != null) {
return visitor.leaveReturnNode(setExpression(expression.accept(visitor)));
......
......@@ -29,6 +29,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
......@@ -407,7 +408,7 @@ public class RuntimeNode extends Node implements TypeOverride<RuntimeNode> {
}
@Override
public Node accept(final NodeVisitor visitor) {
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterRuntimeNode(this)) {
final List<Node> newArgs = new ArrayList<>();
for (final Node arg : args) {
......
......@@ -81,7 +81,7 @@ public class SplitNode extends LexicalContextNode {
}
@Override
public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterSplitNode(this)) {
return visitor.leaveSplitNode(setBody(lc, body.accept(visitor)));
}
......
......@@ -100,11 +100,11 @@ public final class SwitchNode extends BreakableNode {
}
@Override
public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterSwitchNode(this)) {
return visitor.leaveSwitchNode(
setExpression(visitor.getLexicalContext(), expression.accept(visitor)).
setCases(visitor.getLexicalContext(), Node.accept(visitor, CaseNode.class, cases), defaultCaseIndex));
setExpression(lc, expression.accept(visitor)).
setCases(lc, Node.accept(visitor, CaseNode.class, cases), defaultCaseIndex));
}
return this;
......
......@@ -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);
......
......@@ -106,7 +106,7 @@ public final class TryNode extends Statement {
* @param visitor IR navigating visitor.
*/
@Override
public Node accept(final NodeVisitor visitor) {
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterTryNode(this)) {
// Need to do finallybody first for termination analysis. TODO still necessary?
final Block newFinallyBody = finallyBody == null ? null : (Block)finallyBody.accept(visitor);
......
......@@ -29,7 +29,6 @@ import static jdk.nashorn.internal.parser.TokenType.BIT_NOT;
import static jdk.nashorn.internal.parser.TokenType.CONVERT;
import static jdk.nashorn.internal.parser.TokenType.DECPOSTFIX;
import static jdk.nashorn.internal.parser.TokenType.INCPOSTFIX;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
......@@ -121,7 +120,7 @@ public final class UnaryNode extends Node implements Assignment<Node> {
* @param visitor IR navigating visitor.
*/
@Override
public Node accept(final NodeVisitor visitor) {
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterUnaryNode(this)) {
return visitor.leaveUnaryNode(setRHS(rhs.accept(visitor)));
}
......
......@@ -121,7 +121,7 @@ public final class VarNode extends Statement implements Assignment<IdentNode> {
* @param visitor IR navigating visitor.
*/
@Override
public Node accept(final NodeVisitor visitor) {
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
if (visitor.enterVarNode(this)) {
final IdentNode newName = (IdentNode)name.accept(visitor);
final Node newInit = init == null ? null : init.accept(visitor);
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册