Mercurial > people > rkennke > jdk9-shenandoah-final > nashorn
changeset 303:ddbf41575a2b jdk8-b93
Merge
author | lana |
---|---|
date | Mon, 03 Jun 2013 23:24:36 -0700 |
parents | 1c7481ac7fe0 (current diff) 4463e94d9b0d (diff) |
children | e857ab684db0 d2bd881976b5 |
files | src/jdk/nashorn/internal/runtime/options/ValueOption.java src/jdk/nashorn/internal/runtime/regexp/DefaultRegExp.java src/jdk/nashorn/internal/runtime/regexp/joni/AsmCompiler.java src/jdk/nashorn/internal/runtime/regexp/joni/AsmCompilerSupport.java src/jdk/nashorn/internal/runtime/regexp/joni/CaptureTreeNode.java src/jdk/nashorn/internal/runtime/regexp/joni/NameEntry.java src/jdk/nashorn/internal/runtime/regexp/joni/NativeMachine.java src/jdk/nashorn/internal/runtime/regexp/joni/UnsetAddrList.java src/jdk/nashorn/internal/runtime/regexp/joni/ast/CTypeNode.java src/jdk/nashorn/internal/runtime/regexp/joni/ast/CallNode.java src/jdk/nashorn/internal/runtime/regexp/joni/bench/AbstractBench.java src/jdk/nashorn/internal/runtime/regexp/joni/bench/BenchGreedyBacktrack.java src/jdk/nashorn/internal/runtime/regexp/joni/bench/BenchRailsRegs.java src/jdk/nashorn/internal/runtime/regexp/joni/bench/BenchSeveralRegexps.java src/jdk/nashorn/internal/runtime/regexp/joni/constants/Reduce.java src/jdk/nashorn/internal/runtime/regexp/joni/encoding/AsciiTables.java src/jdk/nashorn/internal/runtime/regexp/joni/encoding/PosixBracket.java src/jdk/nashorn/internal/runtime/regexp/joni/encoding/Ptr.java src/netscape/javascript/JSObject.java |
diffstat | 209 files changed, 5483 insertions(+), 5462 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgignore Thu May 30 10:58:35 2013 -0700 +++ b/.hgignore Mon Jun 03 23:24:36 2013 -0700 @@ -24,3 +24,4 @@ .idea/* test/lib/testng.jar test/script/external/* +.project
--- a/docs/JavaScriptingProgrammersGuide.html Thu May 30 10:58:35 2013 -0700 +++ b/docs/JavaScriptingProgrammersGuide.html Mon Jun 03 23:24:36 2013 -0700 @@ -71,9 +71,20 @@ 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 @@ </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 @@ <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 @@ <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 @@ </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 @@ </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 @@ 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 @@ printSizeInvokedArrayList.size(); printAddInvokedArrayList.add(33, 33); </code></pre> +<p> +The reason you must use <code>Java.extend()</code> with concrete classes is that with concrete classes, there can be a +syntactic ambiguity if you just invoke their constructor. Consider this example: +</p> +<pre><code> +var t = new java.lang.Thread({ run: function() { print("Hello!") } }) +</code></pre> +<p> +If we allowed subclassing of concrete classes with constructor syntax, Nashorn couldn't tell if you're creating a new +<code>Thread</code> and passing it a <code>Runnable</code> at this point, or you are subclassing <code>Thread</code> and +passing it a new implementation for its own <code>run()</code> method. +</p> +<hr> +<a name="jsimplementmultiple" id="jsimplementmultiple"></a> +<h3>Implementing Multiple Interfaces</h3> +<p> +<code>Java.extend</code> can in fact take a list of multiple types. At most one of the types can be a class, and the rest must +be interfaces (the class doesn't have to be the first in the list). You will get back an object that extends the class and +implements all the interfaces. (Obviously, if you only specify interfaces and no class, the object will extend <code>java.lang.Object</code>). +<hr> +<a name="classBoundImplementations" id="classBoundImplementations"></a> +<h3>Class-Bound Implementations</h3> +<p> +The methods shown so far for extending Java classes and implementing interfaces – passing an implementation JavaScript object +or function to a constructor, or using <code>Java.extend</code> with <code>new</code> – all produce classes that take an +extra JavaScript object parameter in their constructors that specifies the implementation. The implementation is therefore always bound +to the actual instance being created with <code>new</code>, and not to the whole class. This has some advantages, for example in the +memory footprint of the runtime, as Nashorn can just create a single "universal adapter" for every combination of types being implemented. +In reality, the below code shows that different instantiations of, say, <code>Runnable</code> have the same class regardless of them having +different JavaScript implementation objects: +</p> +<pre><code> +var Runnable = java.lang.Runnable; +var r1 = new Runnable(function() { print("I'm runnable 1!") }) +var r2 = new Runnable(function() { print("I'm runnable 2!") }) +r1.run() +r2.run() +print("We share the same class: " + (r1.class === r2.class)) +</code></pre> +<p> +prints: +</p> +<pre><code> +I'm runnable 1! +I'm runnable 2! +We share the same class: true +</code></pre> +<p> +Sometimes, however, you'll want to extend a Java class or implement an interface with implementation bound to the class, not to +its instances. Such a need arises, for example, when you need to pass the class for instantiation to an external API; prime example +of this is the JavaFX framework where you need to pass an Application class to the FX API and let it instantiate it. +</p> +<p> +Fortunately, there's a solution for that: <code>Java.extend()</code> – aside from being able to take any number of type parameters +denoting a class to extend and interfaces to implement – can also take one last argument that has to be a JavaScript object +that serves as the implementation for the methods. In this case, <code>Java.extend()</code> will create a class that has the same +constructors as the original class had, as they don't need to take an an extra implementation object parameter. The example below +shows how you can create class-bound implementations, and shows that in this case, the implementation classes for different invocations +are indeed different: +</p> +<pre><code> +var RunnableImpl1 = Java.extend(java.lang.Runnable, function() { print("I'm runnable 1!") }) +var RunnableImpl2 = Java.extend(java.lang.Runnable, function() { print("I'm runnable 2!") }) +var r1 = new RunnableImpl1() +var r2 = new RunnableImpl2() +r1.run() +r2.run() +print("We share the same class: " + (r1.class === r2.class)) +</code></pre> +<p> +prints: +</p> +<pre><code> +I'm runnable 1! +I'm runnable 2! +We share the same class: false +</code></pre> +<p> +As you can see, the major difference here is that we moved the implementation object into the invocation of <code>Java.extend</code> +from the constructor invocations – indeed the constructor invocations now don't even need to take an extra parameter! Since +the implementations are bound to a class, the two classes obviously can't be the same, and we indeed see that the two runnables no +longer share the same class – every invocation of <code>Java.extend()</code> with a class-specific implementation object triggers +the creation of a new Java adapter class. +</p> +<p> +Finally, the adapter classes with class-bound implementations can <i>still</i> take an additional constructor parameter to further +override the behavior on a per-instance basis. Thus, you can even combine the two approaches: you can provide part of the implementation +in a class-based JavaScript implementation object passed to <code>Java.extend</code>, and part in another object passed to the constructor. +Whatever functions are provided by the constructor-passed object will override the functions in the class-bound object. +</p> +<pre><code> +var RunnableImpl = Java.extend(java.lang.Runnable, function() { print("I'm runnable 1!") }) +var r1 = new RunnableImpl() +var r2 = new RunnableImpl(function() { print("I'm runnable 2!") }) +r1.run() +r2.run() +print("We share the same class: " + (r1.class === r2.class)) +</code></pre> +<p> +prints: +</p> +<pre><code> +I'm runnable 1! +I'm runnable 2! +We share the same class: true +</code></pre> <hr> <a name="jsoverload" id="jsoverload"></a> <h3>Overload Resolution</h3> <p>Java methods can be overloaded by argument types. In Java, overload resolution occurs at compile time (performed by javac). -When calling Java methods from a script, the script -interpreter/compiler needs to select the appropriate method. With -the JavaScript engine, you do not need to do anything special - the -correct Java method overload variant is selected based on the -argument types. But, sometimes you may want (or have) to explicitly -select a particular overload variant.</p> +When calling Java methods from Nashorn, the appropriate method will be +selected based on the argument types at invocation time. You do not need +to do anything special – the correct Java method overload variant +is selected based automatically. You still have the option of explicitly +specifying a particular overload variant. Reasons for this include +either running into a genuine ambiguity with actual argument types, or +rarely reasons of performance – if you specify the actual overload +then the engine doesn't have to perform resolution during invocation. +Individual overloads of a Java methods are exposed as special properties +with the name of the method followed with its signature in parentheses. +You can invoke them like this:</p> <pre><code> // <a href="source/overload.js">overload.js</a> var out = java.lang.System.out; // select a particular print function -out["println(java.lang.Object)"]("hello"); +out["println(Object)"]("hello"); </code> </pre> +<p> +Note that you normally don't even have to use qualified class names in +the signatures as long as the unqualified name of the type is sufficient +for uniquely identifying the signature. In practice this means that only +in the extremely unlikely case that two overloads only differ in +parameter types that have identical unqualified names but come from +different packages would you need to use the fully qualified name of the +class. +</p> +<hr> +<a name="dataTypeMapping" id="dataTypeMapping"></a> +<h3>Mapping of Data Types Between Java and JavaScript</h3> +<p> +We have previously shown some of the data type mappings between Java and JavaScript. +We saw that arrays need to be explicitly converted. We have also shown that JavaScript functions +are automatically converted to SAM types when passed as parameters to Java methods. Most other +conversions work as you would expect. +</p> +<p> +Every JavaScript object is also a <code>java.util.Map</code> so APIs receiving maps will receive them directly. +</p> +<p> +When numbers are passed to a Java API, they will be converted to the expected target numeric type, either boxed or +primitive, but if the target type is less specific, say <code>Number</code> or <code>Object</code>, you can only +count on them being a <code>Number</code>, and have to test specifically for whether it's a boxed <code>Double</code>, +<code>Integer</code>, <code>Long</code>, etc. – it can be any of these due to internal optimizations. Also, you +can pass any JavaScript value to a Java API expecting either a boxed or primitive number; the JavaScript specification's +<code>ToNumber</code> conversion algorithm will be applied to the value. +</p> +<p> +In a similar vein, if a Java method expects a <code>String</code> or a <code>Boolean</code>, the values will be +converted using all conversions allowed by the JavaScript specification's <code>ToString</code> and <code>ToBoolean</code> +conversions. +</p> +<p> +Finally, a word of caution about strings. Due to internal performance optimizations of string operations, JavaScript strings are +not always necessarily of type <code>java.lang.String</code>, but they will always be of type <code>java.lang.CharSequence</code>. +If you pass them to a Java method that expects a <code>java.lang.String</code> parameter, then you will naturally receive a Java +String, but if the signature of your method is more generic, i.e. it receives a <code>java.lang.Object</code> parameter, you can +end up with an object of private engine implementation class that implements <code>CharSequence</code> but is not a Java String. +</p> <hr> <a name="engineimpl" id="engineimpl"></a> <h2>Implementing Your Own Script Engine</h2>
--- a/docs/source/javaarray.js Thu May 30 10:58:35 2013 -0700 +++ b/docs/source/javaarray.js Mon Jun 03 23:24:36 2013 -0700 @@ -40,7 +40,7 @@ // 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 @@ // 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);
--- a/make/build-benchmark.xml Thu May 30 10:58:35 2013 -0700 +++ b/make/build-benchmark.xml Mon Jun 03 23:24:36 2013 -0700 @@ -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>
--- a/make/build.xml Thu May 30 10:58:35 2013 -0700 +++ b/make/build.xml Mon Jun 03 23:24:36 2013 -0700 @@ -212,7 +212,9 @@ target="${javac.target}" debug="${javac.debug}" encoding="${javac.encoding}" - includeantruntime="false"/> + includeantruntime="false"> + <compilerarg line="-extdirs """/> + </javac> <!-- tests that check nashorn internals and internal API --> <jar jarfile="${nashorn.internal.tests.jar}"> @@ -305,6 +307,8 @@ <include name="**/codegen/*Test.class"/> <include name="**/parser/*Test.class"/> <include name="**/runtime/*Test.class"/> + <include name="**/runtime/regexp/*Test.class"/> + <include name="**/runtime/regexp/joni/*Test.class"/> <include name="**/framework/*Test.class"/> </fileset>
--- a/make/code_coverage.xml Thu May 30 10:58:35 2013 -0700 +++ b/make/code_coverage.xml Mon Jun 03 23:24:36 2013 -0700 @@ -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}"/>
--- a/make/project.properties Thu May 30 10:58:35 2013 -0700 +++ b/make/project.properties Mon Jun 03 23:24:36 2013 -0700 @@ -87,6 +87,7 @@ 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 @@ 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}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk/nashorn/api/scripting/JSObject.java Mon Jun 03 23:24:36 2013 -0700 @@ -0,0 +1,87 @@ +/* + * 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.api.scripting; + +/** + * netscape.javascript.JSObject-like interface for nashorn script objects. + */ +public abstract class JSObject { + /** + * Call a JavaScript method + * + * @param methodName name of method + * @param args arguments to method + * @return result of call + */ + public abstract Object call(String methodName, Object args[]); + + /** + * Evaluate a JavaScript expression + * + * @param s JavaScript expression to evaluate + * @return evaluation result + */ + public abstract Object eval(String s); + + /** + * Retrieves a named member of a JavaScript object. + * + * @param name of member + * @return member + */ + public abstract Object getMember(String name); + + /** + * Retrieves an indexed member of a JavaScript object. + * + * @param index index of member slot + * @return member + */ + public abstract Object getSlot(int index); + + /** + * Remove a named member from a JavaScript object + * + * @param name name of member + */ + public abstract void removeMember(String name); + + /** + * Set a named member in a JavaScript object + * + * @param name name of member + * @param value value of member + */ + public abstract void setMember(String name, Object value); + + /** + * Set an indexed member in a JavaScript object + * + * @param index index of member slot + * @param value value of member + */ + public abstract void setSlot(int index, Object value); +}
--- a/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java Mon Jun 03 23:24:36 2013 -0700 @@ -42,7 +42,6 @@ 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
--- a/src/jdk/nashorn/api/scripting/resources/engine.js Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/api/scripting/resources/engine.js Mon Jun 03 23:24:36 2013 -0700 @@ -88,7 +88,7 @@ } } - array = Java.toJavaArray(array); + array = Java.to(array); return Packages.jdk.nashorn.api.scripting.ScriptUtils.format(format, array); } });
--- a/src/jdk/nashorn/internal/codegen/Attr.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/codegen/Attr.java Mon Jun 03 23:24:36 2013 -0700 @@ -84,8 +84,8 @@ import jdk.nashorn.internal.ir.UnaryNode; import jdk.nashorn.internal.ir.VarNode; import jdk.nashorn.internal.ir.WithNode; +import jdk.nashorn.internal.ir.visitor.NodeVisitor; 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.Context; import jdk.nashorn.internal.runtime.Debug; @@ -111,7 +111,7 @@ * computed. */ -final class Attr extends NodeOperatorVisitor { +final class Attr extends NodeOperatorVisitor<LexicalContext> { /** * Local definitions in current block (to discriminate from function @@ -138,6 +138,7 @@ * Constructor. */ Attr(final TemporarySymbols temporarySymbols) { + super(new LexicalContext()); this.temporarySymbols = temporarySymbols; this.localDefs = new ArrayDeque<>(); this.localUses = new ArrayDeque<>(); @@ -202,7 +203,7 @@ private void acceptDeclarations(final FunctionNode functionNode, final Block body) { // This visitor will assign symbol to all declared variables, except function declarations (which are taken care // in a separate step above) and "var" declarations in for loop initializers. - body.accept(new NodeOperatorVisitor() { + body.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { @Override public boolean enterFunctionNode(final FunctionNode nestedFn) { return false; @@ -218,7 +219,7 @@ if (varNode.isFunctionDeclaration()) { newType(symbol, FunctionNode.FUNCTION_TYPE); } - return varNode.setName((IdentNode)ident.setSymbol(getLexicalContext(), symbol)); + return varNode.setName((IdentNode)ident.setSymbol(lc, symbol)); } return varNode; } @@ -227,8 +228,8 @@ private void enterFunctionBody() { - final FunctionNode functionNode = getLexicalContext().getCurrentFunction(); - final Block body = getLexicalContext().getCurrentBlock(); + final FunctionNode functionNode = lc.getCurrentFunction(); + final Block body = lc.getCurrentBlock(); initFunctionWideVariables(functionNode, body); @@ -256,7 +257,7 @@ //the symbols in the block should really be stateless block.clearSymbols(); - if (getLexicalContext().isFunctionBody()) { + if (lc.isFunctionBody()) { enterFunctionBody(); } pushLocalsBlock(); @@ -283,7 +284,7 @@ @Override public boolean enterCatchNode(final CatchNode catchNode) { final IdentNode exception = catchNode.getException(); - final Block block = getLexicalContext().getCurrentBlock(); + final Block block = lc.getCurrentBlock(); start(catchNode); @@ -298,10 +299,10 @@ @Override public Node leaveCatchNode(final CatchNode catchNode) { final IdentNode exception = catchNode.getException(); - final Block block = getLexicalContext().getCurrentBlock(); + final Block block = lc.getCurrentBlock(); final Symbol symbol = findSymbol(block, exception.getName()); assert symbol != null; - return end(catchNode.setException((IdentNode)exception.setSymbol(getLexicalContext(), symbol))); + return end(catchNode.setException((IdentNode)exception.setSymbol(lc, symbol))); } /** @@ -320,7 +321,7 @@ flags |= IS_SCOPE; } - final FunctionNode function = getLexicalContext().getFunction(block); + final FunctionNode function = lc.getFunction(block); if (symbol != null) { // Symbol was already defined. Check if it needs to be redefined. if ((flags & KINDMASK) == IS_PARAM) { @@ -353,12 +354,12 @@ if ((flags & Symbol.KINDMASK) == IS_VAR && ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET)) { symbolBlock = block; //internal vars are always defined in the block closest to them } else { - symbolBlock = getLexicalContext().getFunctionBody(function); + symbolBlock = lc.getFunctionBody(function); } // Create and add to appropriate block. symbol = new Symbol(name, flags); - symbolBlock.putSymbol(getLexicalContext(), symbol); + symbolBlock.putSymbol(lc, symbol); if ((flags & Symbol.KINDMASK) != IS_GLOBAL) { symbol.setNeedsSlot(true); @@ -381,7 +382,7 @@ //an outermost function in our lexical context that is not a program (runScript) //is possible - it is a function being compiled lazily if (functionNode.isDeclared()) { - final Iterator<Block> blocks = getLexicalContext().getBlocks(); + final Iterator<Block> blocks = lc.getBlocks(); if (blocks.hasNext()) { defineSymbol(blocks.next(), functionNode.getIdent().getName(), IS_VAR); } @@ -397,13 +398,11 @@ public Node leaveFunctionNode(final FunctionNode functionNode) { FunctionNode newFunctionNode = functionNode; - final LexicalContext lc = getLexicalContext(); - final Block body = newFunctionNode.getBody(); //look for this function in the parent block if (functionNode.isDeclared()) { - final Iterator<Block> blocks = getLexicalContext().getBlocks(); + final Iterator<Block> blocks = lc.getBlocks(); if (blocks.hasNext()) { newFunctionNode = (FunctionNode)newFunctionNode.setSymbol(lc, findSymbol(blocks.next(), functionNode.getIdent().getName())); } @@ -411,7 +410,7 @@ final boolean anonymous = functionNode.isAnonymous(); final String name = anonymous ? null : functionNode.getIdent().getName(); if (anonymous || body.getExistingSymbol(name) != null) { - newFunctionNode = (FunctionNode)ensureSymbol(lc, FunctionNode.FUNCTION_TYPE, newFunctionNode); + newFunctionNode = (FunctionNode)ensureSymbol(FunctionNode.FUNCTION_TYPE, newFunctionNode); } else { assert name != null; final Symbol self = body.getExistingSymbol(name); @@ -490,8 +489,6 @@ start(identNode); - final LexicalContext lc = getLexicalContext(); - if (identNode.isPropertyName()) { // assign a pseudo symbol to property name final Symbol pseudoSymbol = pseudoSymbol(name); @@ -549,7 +546,7 @@ */ private void maybeForceScope(final Symbol symbol) { if (!symbol.isScope() && symbolNeedsToBeScope(symbol)) { - Symbol.setSymbolIsScope(getLexicalContext(), symbol); + Symbol.setSymbolIsScope(lc, symbol); } } @@ -558,7 +555,7 @@ return false; } boolean previousWasBlock = false; - for(final Iterator<LexicalContextNode> it = getLexicalContext().getAllNodes(); it.hasNext();) { + for(final Iterator<LexicalContextNode> it = lc.getAllNodes(); it.hasNext();) { final LexicalContextNode node = it.next(); if(node instanceof FunctionNode) { // We reached the function boundary without seeing a definition for the symbol - it needs to be in @@ -594,10 +591,8 @@ } if (symbol.isScope()) { - final LexicalContext lc = getLexicalContext(); - Block scopeBlock = null; - for (final Iterator<LexicalContextNode> contextNodeIter = getLexicalContext().getAllNodes(); contextNodeIter.hasNext(); ) { + for (final Iterator<LexicalContextNode> contextNodeIter = lc.getAllNodes(); contextNodeIter.hasNext(); ) { final LexicalContextNode node = contextNodeIter.next(); if (node instanceof Block) { if (((Block)node).getExistingSymbol(name) != null) { @@ -610,7 +605,7 @@ } if (scopeBlock != null) { - assert getLexicalContext().contains(scopeBlock); + assert lc.contains(scopeBlock); lc.setFlag(scopeBlock, Block.NEEDS_SCOPE); } } @@ -622,8 +617,8 @@ * @see #needsParentScope() */ private void setUsesGlobalSymbol() { - for (final Iterator<FunctionNode> fns = getLexicalContext().getFunctions(); fns.hasNext();) { - getLexicalContext().setFlag(fns.next(), FunctionNode.USES_ANCESTOR_SCOPE); + for (final Iterator<FunctionNode> fns = lc.getFunctions(); fns.hasNext();) { + lc.setFlag(fns.next(), FunctionNode.USES_ANCESTOR_SCOPE); } } @@ -635,7 +630,7 @@ private Symbol findSymbol(final Block block, final String name) { // Search up block chain to locate symbol. - for (final Iterator<Block> blocks = getLexicalContext().getBlocks(block); blocks.hasNext();) { + for (final Iterator<Block> blocks = lc.getBlocks(block); blocks.hasNext();) { // Find name. final Symbol symbol = blocks.next().getExistingSymbol(name); // If found then we are good. @@ -656,11 +651,11 @@ public Node leaveLiteralNode(final LiteralNode literalNode) { assert !literalNode.isTokenType(TokenType.THIS) : "tokentype for " + literalNode + " is this"; //guard against old dead code case. literal nodes should never inherit tokens assert literalNode instanceof ArrayLiteralNode || !(literalNode.getValue() instanceof Node) : "literals with Node values not supported"; - final Symbol symbol = new Symbol(getLexicalContext().getCurrentFunction().uniqueName(LITERAL_PREFIX.symbolName()), IS_CONSTANT, literalNode.getType()); + final Symbol symbol = new Symbol(lc.getCurrentFunction().uniqueName(LITERAL_PREFIX.symbolName()), IS_CONSTANT, literalNode.getType()); if (literalNode instanceof ArrayLiteralNode) { ((ArrayLiteralNode)literalNode).analyze(); } - return end(literalNode.setSymbol(getLexicalContext(), symbol)); + return end(literalNode.setSymbol(lc, symbol)); } @Override @@ -676,7 +671,7 @@ @Override public Node leavePropertyNode(final PropertyNode propertyNode) { // assign a pseudo symbol to property name, see NASHORN-710 - return propertyNode.setSymbol(getLexicalContext(), new Symbol(propertyNode.getKeyName(), 0, Type.OBJECT)); + return propertyNode.setSymbol(lc, new Symbol(propertyNode.getKeyName(), 0, Type.OBJECT)); } @Override @@ -734,11 +729,11 @@ type = Type.OBJECT; } - switchNode.setTag(newInternal(getLexicalContext().getCurrentFunction().uniqueName(SWITCH_TAG_PREFIX.symbolName()), type)); + switchNode.setTag(newInternal(lc.getCurrentFunction().uniqueName(SWITCH_TAG_PREFIX.symbolName()), type)); end(switchNode); - return switchNode.setCases(getLexicalContext(), newCases); + return switchNode.setCases(lc, newCases); } @Override @@ -761,7 +756,7 @@ final IdentNode ident = varNode.getName(); final String name = ident.getName(); - final Symbol symbol = defineSymbol(getLexicalContext().getCurrentBlock(), name, IS_VAR); + final Symbol symbol = defineSymbol(lc.getCurrentBlock(), name, IS_VAR); assert symbol != null; // NASHORN-467 - use before definition of vars - conservative @@ -781,7 +776,6 @@ final IdentNode ident = newVarNode.getName(); final String name = ident.getName(); - final LexicalContext lc = getLexicalContext(); final Symbol symbol = findSymbol(lc.getCurrentBlock(), ident.getName()); if (init == null) { @@ -834,7 +828,7 @@ @Override public Node leaveDELETE(final UnaryNode unaryNode) { - final FunctionNode currentFunctionNode = getLexicalContext().getCurrentFunction(); + final FunctionNode currentFunctionNode = lc.getCurrentFunction(); final boolean strictMode = currentFunctionNode.isStrict(); final Node rhs = unaryNode.rhs(); final Node strictFlagNode = LiteralNode.newInstance(unaryNode, strictMode).accept(this); @@ -894,10 +888,10 @@ * @return true if the symbol denoted by the specified name in the current lexical context defined in the program level. */ private boolean isProgramLevelSymbol(final String name) { - for(final Iterator<Block> it = getLexicalContext().getBlocks(); it.hasNext();) { + for(final Iterator<Block> it = lc.getBlocks(); it.hasNext();) { final Block next = it.next(); if(next.getExistingSymbol(name) != null) { - return next == getLexicalContext().getFunctionBody(getLexicalContext().getOutermostFunction()); + return next == lc.getFunctionBody(lc.getOutermostFunction()); } } throw new AssertionError("Couldn't find symbol " + name + " in the context"); @@ -914,14 +908,14 @@ } private IdentNode compilerConstant(CompilerConstants cc) { - final FunctionNode functionNode = getLexicalContext().getCurrentFunction(); + final FunctionNode functionNode = lc.getCurrentFunction(); return (IdentNode) new IdentNode( functionNode.getToken(), functionNode.getFinish(), cc.symbolName()). setSymbol( - getLexicalContext(), + lc, functionNode.compilerConstant(cc)); } @@ -999,7 +993,7 @@ final Node lhs = binaryNode.lhs(); if (lhs instanceof IdentNode) { - final Block block = getLexicalContext().getCurrentBlock(); + final Block block = lc.getCurrentBlock(); final IdentNode ident = (IdentNode)lhs; final String name = ident.getName(); @@ -1043,7 +1037,7 @@ } private boolean isLocal(FunctionNode function, Symbol symbol) { - final FunctionNode definingFn = getLexicalContext().getDefiningFunction(symbol); + final FunctionNode definingFn = lc.getDefiningFunction(symbol); // Temp symbols are not assigned to a block, so their defining fn is null; those can be assumed local return definingFn == null || definingFn == function; } @@ -1329,7 +1323,7 @@ @Override public Node leaveForNode(final ForNode forNode) { if (forNode.isForIn()) { - forNode.setIterator(newInternal(getLexicalContext().getCurrentFunction().uniqueName(ITERATOR_PREFIX.symbolName()), Type.OBJECT)); //NASHORN-73 + forNode.setIterator(newInternal(lc.getCurrentFunction().uniqueName(ITERATOR_PREFIX.symbolName()), Type.OBJECT)); //NASHORN-73 /* * Iterators return objects, so we need to widen the scope of the * init variable if it, for example, has been assigned double type @@ -1407,7 +1401,7 @@ final Symbol paramSymbol = functionNode.getBody().getExistingSymbol(param.getName()); assert paramSymbol != null; assert paramSymbol.isParam(); - newParams.add((IdentNode)param.setSymbol(getLexicalContext(), paramSymbol)); + newParams.add((IdentNode)param.setSymbol(lc, paramSymbol)); assert paramSymbol != null; Type type = functionNode.getHints().getParameterType(pos); @@ -1439,10 +1433,10 @@ FunctionNode newFunctionNode = functionNode; if (nparams == 0 || (specialize * 2) < nparams) { - newFunctionNode = newFunctionNode.clearSnapshot(getLexicalContext()); + newFunctionNode = newFunctionNode.clearSnapshot(lc); } - return newFunctionNode.setParameters(getLexicalContext(), newParams); + return newFunctionNode.setParameters(lc, newParams); } /** @@ -1506,7 +1500,7 @@ } private Symbol exceptionSymbol() { - return newInternal(getLexicalContext().getCurrentFunction().uniqueName(EXCEPTION_PREFIX.symbolName()), Type.typeFor(ECMAException.class)); + return newInternal(lc.getCurrentFunction().uniqueName(EXCEPTION_PREFIX.symbolName()), Type.typeFor(ECMAException.class)); } /** @@ -1520,8 +1514,8 @@ * @param assignmentDest the destination node of the assignment, e.g. lhs for binary nodes */ private Node ensureAssignmentSlots(final Node assignmentDest) { - final LexicalContext attrLexicalContext = getLexicalContext(); - return assignmentDest.accept(new NodeVisitor() { + final LexicalContext attrLexicalContext = lc; + return assignmentDest.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { @Override public Node leaveIndexNode(final IndexNode indexNode) { assert indexNode.getSymbol().isTemp(); @@ -1565,7 +1559,7 @@ FunctionNode currentFunctionNode = functionNode; do { changed.clear(); - final FunctionNode newFunctionNode = (FunctionNode)currentFunctionNode.accept(new NodeVisitor() { + final FunctionNode newFunctionNode = (FunctionNode)currentFunctionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { private Node widen(final Node node, final Type to) { if (node instanceof LiteralNode) { @@ -1579,7 +1573,7 @@ symbol = temporarySymbols.getTypedTemporarySymbol(to); } newType(symbol, to); - final Node newNode = node.setSymbol(getLexicalContext(), symbol); + final Node newNode = node.setSymbol(lc, symbol); changed.add(newNode); return newNode; } @@ -1622,7 +1616,7 @@ return newBinaryNode; } }); - getLexicalContext().replace(currentFunctionNode, newFunctionNode); + lc.replace(currentFunctionNode, newFunctionNode); currentFunctionNode = newFunctionNode; } while (!changed.isEmpty()); return currentFunctionNode; @@ -1643,12 +1637,12 @@ } private Node ensureSymbol(final Type type, final Node node) { - LOG.info("New TEMPORARY added to ", getLexicalContext().getCurrentFunction().getName(), " type=", type); - return ensureSymbol(getLexicalContext(), type, node); + LOG.info("New TEMPORARY added to ", lc.getCurrentFunction().getName(), " type=", type); + return temporarySymbols.ensureSymbol(lc, type, node); } private Symbol newInternal(final String name, final Type type) { - final Symbol iter = defineSymbol(getLexicalContext().getCurrentBlock(), name, IS_VAR | IS_INTERNAL); + final Symbol iter = defineSymbol(lc.getCurrentBlock(), name, IS_VAR | IS_INTERNAL); iter.setType(type); // NASHORN-73 return iter; } @@ -1705,10 +1699,6 @@ localUses.peek().add(name); } - private Node ensureSymbol(final LexicalContext lc, final Type type, final Node node) { - return temporarySymbols.ensureSymbol(lc, type, node); - } - /** * Pessimistically promote all symbols in current function node to Object types * This is done when the function contains unevaluated black boxes such as @@ -1717,7 +1707,7 @@ * @param body body for the function node we are leaving */ private static void objectifySymbols(final Block body) { - body.accept(new NodeVisitor() { + body.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { private void toObject(final Block block) { for (final Symbol symbol : block.getSymbols()) { if (!symbol.isTemp()) { @@ -1761,7 +1751,7 @@ append("] "). append(printNode ? node.toString() : ""). append(" in '"). - append(getLexicalContext().getCurrentFunction().getName()). + append(lc.getCurrentFunction().getName()). append("'"); LOG.info(sb); LOG.indent(); @@ -1787,7 +1777,7 @@ append("] "). append(printNode ? node.toString() : ""). append(" in '"). - append(getLexicalContext().getCurrentFunction().getName()); + append(lc.getCurrentFunction().getName()); if (node.getSymbol() == null) { sb.append(" <NO SYMBOL>");
--- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java Mon Jun 03 23:24:36 2013 -0700 @@ -52,16 +52,13 @@ import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_STRICT; import java.io.PrintWriter; -import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; -import java.util.Deque; import java.util.EnumSet; -import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; -import java.util.Map; +import java.util.Locale; import java.util.TreeMap; import jdk.nashorn.internal.codegen.ClassEmitter.Flag; @@ -83,11 +80,11 @@ import jdk.nashorn.internal.ir.ExecuteNode; import jdk.nashorn.internal.ir.ForNode; import jdk.nashorn.internal.ir.FunctionNode; +import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.IdentNode; import jdk.nashorn.internal.ir.IfNode; import jdk.nashorn.internal.ir.IndexNode; -import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LexicalContextNode; import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; @@ -150,7 +147,7 @@ * The CodeGenerator visits nodes only once, tags them as resolved and emits * bytecode for them. */ -final class CodeGenerator extends NodeOperatorVisitor { +final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContext> { /** Name of the Global object, cannot be referred to as .class, @see CodeGenerator */ private static final String GLOBAL_OBJECT = Compiler.OBJECTS_PACKAGE + '/' + "Global"; @@ -168,23 +165,12 @@ /** How many regexp fields have been emitted */ private int regexFieldCount; - /** 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]; - private int nextFreeSlotsSize = 0; + /** Line number for last statement. If we encounter a new line number, line number bytecode information + * needs to be generated */ + private int lastLineNumber = -1; + + /** When should we stop caching regexp expressions in fields to limit bytecode size? */ + private static final int MAX_REGEX_FIELDS = 2 * 1024; /** Current method emitter */ private MethodEmitter method; @@ -192,20 +178,16 @@ /** Current compile unit */ private CompileUnit unit; - private int lastLineNumber = -1; - - /** When should we stop caching regexp expressions in fields to limit bytecode size? */ - private static final int MAX_REGEX_FIELDS = 2 * 1024; - private static final DebugLogger LOG = new DebugLogger("codegen", "nashorn.codegen.debug"); + /** * Constructor. * * @param compiler */ CodeGenerator(final Compiler compiler) { - super(new DynamicScopeTrackingLexicalContext()); + super(new CodeGeneratorLexicalContext()); this.compiler = compiler; this.callSiteFlags = compiler.getEnv()._callsite_flags; } @@ -217,37 +199,7 @@ * @return the correct flags for a call site in the current function */ int getCallSiteFlags() { - return getLexicalContext().getCurrentFunction().isStrict() ? callSiteFlags | CALLSITE_STRICT : callSiteFlags; - } - - private void pushMethodEmitter(final MethodEmitter newMethod) { - methodEmitters.push(newMethod); - this.method = newMethod; - } - - private void popMethodEmitter(final MethodEmitter oldMethod) { - assert methodEmitters.peek() == oldMethod; - methodEmitters.pop(); - if (!methodEmitters.isEmpty()) { - this.method = methodEmitters.peek(); - } else { - this.method = null; - } - } - - private void push(final CompileUnit newUnit) { - compileUnits.push(newUnit); - this.unit = newUnit; - } - - private void pop(final CompileUnit oldUnit) { - assert compileUnits.peek() == oldUnit; - compileUnits.pop(); - if (!compileUnits.isEmpty()) { - this.unit = compileUnits.peek(); - } else { - this.unit = null; - } + return lc.getCurrentFunction().isStrict() ? callSiteFlags | CALLSITE_STRICT : callSiteFlags; } /** @@ -265,7 +217,7 @@ } final String name = symbol.getName(); - final Source source = getLexicalContext().getCurrentFunction().getSource(); + final Source source = lc.getCurrentFunction().getSource(); if (CompilerConstants.__FILE__.name().equals(name)) { return method.load(source.getName()); @@ -291,88 +243,43 @@ } /** - * A lexical context that also tracks if we have any dynamic scopes in the context. Such scopes can have new - * variables introduced into them at run time - a with block or a function directly containing an eval call. - */ - private static class DynamicScopeTrackingLexicalContext extends LexicalContext { - int dynamicScopeCount = 0; - - @Override - public <T extends LexicalContextNode> T push(T node) { - if(isDynamicScopeBoundary(node)) { - ++dynamicScopeCount; - } - return super.push(node); - } - - @Override - public <T extends LexicalContextNode> T pop(T node) { - final T popped = super.pop(node); - if(isDynamicScopeBoundary(popped)) { - --dynamicScopeCount; - } - return popped; - } - - private boolean isDynamicScopeBoundary(LexicalContextNode node) { - if(node instanceof Block) { - // Block's immediate parent is a with node. Note we aren't testing for a WithNode, as that'd capture - // processing of WithNode.expression too, but it should be unaffected. - return !isEmpty() && peek() instanceof WithNode; - } else if(node instanceof FunctionNode) { - // Function has a direct eval in it (so a top-level "var ..." in the eval code can introduce a new - // variable into the function's scope), and it isn't strict (as evals in strict functions get an - // isolated scope). - return isFunctionDynamicScope((FunctionNode)node); - } - return false; - } - } - - boolean inDynamicScope() { - return ((DynamicScopeTrackingLexicalContext)getLexicalContext()).dynamicScopeCount > 0; - } - - static boolean isFunctionDynamicScope(FunctionNode fn) { - return fn.hasEval() && !fn.isStrict(); - } - - /** * Check if this symbol can be accessed directly with a putfield or getfield or dynamic load * * @param function function to check for fast scope * @return true if fast scope */ private boolean isFastScope(final Symbol symbol) { - if(!symbol.isScope()) { + if (!symbol.isScope()) { return false; } - final LexicalContext lc = getLexicalContext(); - if(!inDynamicScope()) { + + if (!lc.inDynamicScope()) { // If there's no with or eval in context, and the symbol is marked as scoped, it is fast scoped. Such a // symbol must either be global, or its defining block must need scope. assert symbol.isGlobal() || lc.getDefiningBlock(symbol).needsScope() : symbol.getName(); return true; } - if(symbol.isGlobal()) { + + if (symbol.isGlobal()) { // Shortcut: if there's a with or eval in context, globals can't be fast scoped return false; } + // Otherwise, check if there's a dynamic scope between use of the symbol and its definition final String name = symbol.getName(); boolean previousWasBlock = false; for (final Iterator<LexicalContextNode> it = lc.getAllNodes(); it.hasNext();) { final LexicalContextNode node = it.next(); - if(node instanceof Block) { + if (node instanceof Block) { // If this block defines the symbol, then we can fast scope the symbol. final Block block = (Block)node; - if(block.getExistingSymbol(name) == symbol) { + if (block.getExistingSymbol(name) == symbol) { assert block.needsScope(); return true; } previousWasBlock = true; } else { - if((node instanceof WithNode && previousWasBlock) || (node instanceof FunctionNode && isFunctionDynamicScope((FunctionNode)node))) { + if ((node instanceof WithNode && previousWasBlock) || (node instanceof FunctionNode && CodeGeneratorLexicalContext.isFunctionDynamicScope((FunctionNode)node))) { // If we hit a scope that can have symbols introduced into it at run time before finding the defining // block, the symbol can't be fast scoped. A WithNode only counts if we've immediately seen a block // before - its block. Otherwise, we are currently processing the WithNode's expression, and that's @@ -387,16 +294,14 @@ } private MethodEmitter loadSharedScopeVar(final Type valueType, final Symbol symbol, final int flags) { - method.load(isFastScope(symbol) ? getScopeProtoDepth(getLexicalContext().getCurrentBlock(), symbol) : -1); - final SharedScopeCall scopeCall = getScopeGet(valueType, symbol, flags | CALLSITE_FAST_SCOPE); - scopeCall.generateInvoke(method); - return method; + method.load(isFastScope(symbol) ? getScopeProtoDepth(lc.getCurrentBlock(), symbol) : -1); + final SharedScopeCall scopeCall = lc.getScopeGet(unit, valueType, symbol, flags | CALLSITE_FAST_SCOPE); + return scopeCall.generateInvoke(method); } private MethodEmitter loadFastScopeVar(final Type valueType, final Symbol symbol, final int flags, final boolean isMethod) { loadFastScopeProto(symbol, false); - method.dynamicGet(valueType, symbol.getName(), flags | CALLSITE_FAST_SCOPE, isMethod); - return method; + return method.dynamicGet(valueType, symbol.getName(), flags | CALLSITE_FAST_SCOPE, isMethod); } private MethodEmitter storeFastScopeVar(final Type valueType, final Symbol symbol, final int flags) { @@ -408,7 +313,7 @@ private int getScopeProtoDepth(final Block startingBlock, final Symbol symbol) { int depth = 0; final String name = symbol.getName(); - for(final Iterator<Block> blocks = getLexicalContext().getBlocks(startingBlock); blocks.hasNext();) { + for(final Iterator<Block> blocks = lc.getBlocks(startingBlock); blocks.hasNext();) { final Block currentBlock = blocks.next(); if (currentBlock.getExistingSymbol(name) == symbol) { return depth; @@ -421,7 +326,7 @@ } private void loadFastScopeProto(final Symbol symbol, final boolean swap) { - final int depth = getScopeProtoDepth(getLexicalContext().getCurrentBlock(), symbol); + final int depth = getScopeProtoDepth(lc.getCurrentBlock(), symbol); assert depth != -1; if (depth > 0) { if (swap) { @@ -464,7 +369,7 @@ */ final CodeGenerator codegen = this; - node.accept(new NodeVisitor() { + node.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { @Override public boolean enterIdentNode(final IdentNode identNode) { loadIdent(identNode); @@ -538,7 +443,7 @@ final boolean isInternal = symbol.isParam() || symbol.isInternal() || symbol.isThis() || !symbol.canBeUndefined(); if (symbol.hasSlot() && !isInternal) { - assert symbol.getSymbolType().isNumber() || symbol.getSymbolType().isObject() : "no potentially undefined narrower local vars than doubles are allowed: " + symbol + " in " + getLexicalContext().getCurrentFunction(); + assert symbol.getSymbolType().isNumber() || symbol.getSymbolType().isObject() : "no potentially undefined narrower local vars than doubles are allowed: " + symbol + " in " + lc.getCurrentFunction(); if (symbol.getSymbolType().isNumber()) { numbers.add(symbol); } else if (symbol.getSymbolType().isObject()) { @@ -595,7 +500,6 @@ if (block.needsScope() && !block.isTerminal()) { popBlockScope(block); } - --nextFreeSlotsSize; return block; } @@ -624,11 +528,11 @@ public boolean enterBreakNode(final BreakNode breakNode) { lineNumber(breakNode); - final BreakableNode breakFrom = getLexicalContext().getBreakable(breakNode.getLabel()); - for (int i = 0; i < getLexicalContext().getScopeNestingLevelTo(breakFrom); i++) { + final BreakableNode breakFrom = lc.getBreakable(breakNode.getLabel()); + for (int i = 0; i < lc.getScopeNestingLevelTo(breakFrom); i++) { closeWith(); } - method.splitAwareGoto(getLexicalContext(), breakFrom.getBreakLabel()); + method.splitAwareGoto(lc, breakFrom.getBreakLabel()); return false; } @@ -672,11 +576,12 @@ final List<Node> args = callNode.getArgs(); final Node function = callNode.getFunction(); - final Block currentBlock = getLexicalContext().getCurrentBlock(); - - function.accept(new NodeVisitor() { - - private void sharedScopeCall(final IdentNode identNode, final int flags) { + final Block currentBlock = lc.getCurrentBlock(); + final CodeGeneratorLexicalContext codegenLexicalContext = lc; + + function.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { + + private MethodEmitter sharedScopeCall(final IdentNode identNode, final int flags) { final Symbol symbol = identNode.getSymbol(); int scopeCallFlags = flags; method.loadCompilerConstant(SCOPE); @@ -688,8 +593,8 @@ } loadArgs(args); final Type[] paramTypes = method.getTypesFromStack(args.size()); - final SharedScopeCall scopeCall = getScopeCall(symbol, identNode.getType(), callNode.getType(), paramTypes, scopeCallFlags); - scopeCall.generateInvoke(method); + final SharedScopeCall scopeCall = codegenLexicalContext.getScopeCall(unit, symbol, identNode.getType(), callNode.getType(), paramTypes, scopeCallFlags); + return scopeCall.generateInvoke(method); } private void scopeCall(final IdentNode node, final int flags) { @@ -756,7 +661,7 @@ evalCall(node, flags); } else if (useCount <= SharedScopeCall.FAST_SCOPE_CALL_THRESHOLD || (!isFastScope(symbol) && useCount <= SharedScopeCall.SLOW_SCOPE_CALL_THRESHOLD) - || CodeGenerator.this.inDynamicScope()) { + || CodeGenerator.this.lc.inDynamicScope()) { scopeCall(node, flags); } else { sharedScopeCall(node, flags); @@ -845,11 +750,11 @@ public boolean enterContinueNode(final ContinueNode continueNode) { lineNumber(continueNode); - final LoopNode continueTo = getLexicalContext().getContinueTo(continueNode.getLabel()); - for (int i = 0; i < getLexicalContext().getScopeNestingLevelTo(continueTo); i++) { + final LoopNode continueTo = lc.getContinueTo(continueNode.getLabel()); + for (int i = 0; i < lc.getScopeNestingLevelTo(continueTo); i++) { closeWith(); } - method.splitAwareGoto(getLexicalContext(), continueTo.getContinueLabel()); + method.splitAwareGoto(lc, continueTo.getContinueLabel()); return false; } @@ -875,90 +780,89 @@ public boolean enterForNode(final ForNode forNode) { lineNumber(forNode); - final Node test = forNode.getTest(); - final Block body = forNode.getBody(); - final Node modify = forNode.getModify(); - - final Label breakLabel = forNode.getBreakLabel(); - final Label continueLabel = forNode.getContinueLabel(); - final Label loopLabel = new Label("loop"); - - Node init = forNode.getInit(); - if (forNode.isForIn()) { - final Symbol iter = forNode.getIterator(); - - // We have to evaluate the optional initializer expression - // of the iterator variable of the for-in statement. - if (init instanceof VarNode) { - init.accept(this); - init = ((VarNode)init).getName(); - } - - load(modify); - assert modify.getType().isObject(); - method.invoke(forNode.isForEach() ? ScriptRuntime.TO_VALUE_ITERATOR : ScriptRuntime.TO_PROPERTY_ITERATOR); - method.store(iter); - method._goto(continueLabel); - method.label(loopLabel); - - new Store<Node>(init) { - @Override - protected void storeNonDiscard() { - return; - } - @Override - protected void evaluate() { - method.load(iter); - method.invoke(interfaceCallNoLookup(Iterator.class, "next", Object.class)); - } - }.store(); - - body.accept(this); - - method.label(continueLabel); - method.load(iter); - method.invoke(interfaceCallNoLookup(Iterator.class, "hasNext", boolean.class)); - method.ifne(loopLabel); - method.label(breakLabel); + enterForIn(forNode); } else { - if (init != null) { - init.accept(this); - } - - final Label testLabel = new Label("test"); - - method._goto(testLabel); - method.label(loopLabel); - body.accept(this); - method.label(continueLabel); - - if (!body.isTerminal() && modify != null) { - load(modify); - } - - method.label(testLabel); - if (test != null) { - new BranchOptimizer(this, method).execute(test, loopLabel, true); - } else { - method._goto(loopLabel); - } - - method.label(breakLabel); + enterFor(forNode); } return false; } - private static int assignSlots(final Block block, final int firstSlot) { - int nextSlot = firstSlot; - for (final Symbol symbol : block.getSymbols()) { - if (symbol.hasSlot()) { - symbol.setSlot(nextSlot); - nextSlot += symbol.slotCount(); + private void enterFor(final ForNode forNode) { + final Node init = forNode.getInit(); + final Node test = forNode.getTest(); + final Block body = forNode.getBody(); + final Node modify = forNode.getModify(); + + if (init != null) { + init.accept(this); + } + + final Label loopLabel = new Label("loop"); + final Label testLabel = new Label("test"); + + method._goto(testLabel); + method.label(loopLabel); + body.accept(this); + method.label(forNode.getContinueLabel()); + + if (!body.isTerminal() && modify != null) { + load(modify); + } + + method.label(testLabel); + if (test != null) { + new BranchOptimizer(this, method).execute(test, loopLabel, true); + } else { + method._goto(loopLabel); + } + + method.label(forNode.getBreakLabel()); + } + + private void enterForIn(final ForNode forNode) { + final Block body = forNode.getBody(); + final Node modify = forNode.getModify(); + + final Symbol iter = forNode.getIterator(); + final Label loopLabel = new Label("loop"); + + Node init = forNode.getInit(); + + // We have to evaluate the optional initializer expression + // of the iterator variable of the for-in statement. + if (init instanceof VarNode) { + init.accept(this); + init = ((VarNode)init).getName(); + } + + load(modify); + assert modify.getType().isObject(); + method.invoke(forNode.isForEach() ? ScriptRuntime.TO_VALUE_ITERATOR : ScriptRuntime.TO_PROPERTY_ITERATOR); + method.store(iter); + method._goto(forNode.getContinueLabel()); + method.label(loopLabel); + + new Store<Node>(init) { + @Override + protected void storeNonDiscard() { + return; } - } - return nextSlot; + @Override + protected void evaluate() { + method.load(iter); + method.invoke(interfaceCallNoLookup(Iterator.class, "next", Object.class)); + } + }.store(); + + body.accept(this); + + method.label(forNode.getContinueLabel()); + method.load(iter); + method.invoke(interfaceCallNoLookup(Iterator.class, "hasNext", boolean.class)); + method.ifne(loopLabel); + method.label(forNode.getBreakLabel()); } /** @@ -967,24 +871,11 @@ * @param block block with local vars. */ private void initLocals(final Block block) { - final boolean isFunctionBody = getLexicalContext().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); - - final FunctionNode function = getLexicalContext().getCurrentFunction(); + lc.nextFreeSlot(block); + + final boolean isFunctionBody = lc.isFunctionBody(); + + final FunctionNode function = lc.getCurrentFunction(); if (isFunctionBody) { /* Fix the predefined slots so they have numbers >= 0, like varargs. */ if (function.needsParentScope()) { @@ -1023,7 +914,7 @@ } if (symbol.isVar()) { - if(varsInScope || symbol.isScope()) { + if (varsInScope || symbol.isScope()) { nameList.add(symbol.getName()); newSymbols.add(symbol); values.add(null); @@ -1062,7 +953,7 @@ @Override protected void loadScope(MethodEmitter m) { - if(function.needsParentScope()) { + if (function.needsParentScope()) { m.loadCompilerConstant(SCOPE); } else { m.loadNull(); @@ -1096,7 +987,7 @@ private void initArguments(final FunctionNode function) { method.loadCompilerConstant(VARARGS); - if(function.needsCallee()) { + if (function.needsCallee()) { method.loadCompilerConstant(CALLEE); } else { // If function is strict mode, "arguments.callee" is not populated, so we don't necessarily need the @@ -1126,10 +1017,10 @@ LOG.info("=== BEGIN ", functionNode.getName()); assert functionNode.getCompileUnit() != null : "no compile unit for " + functionNode.getName() + " " + Debug.id(functionNode); - push(functionNode.getCompileUnit()); - assert !compileUnits.isEmpty(); - - pushMethodEmitter(unit.getClassEmitter().method(functionNode)); + unit = lc.pushCompileUnit(functionNode.getCompileUnit()); + assert lc.hasCompileUnits(); + + method = lc.pushMethodEmitter(unit.getClassEmitter().method(functionNode)); // Mark end for variable tables. method.begin(); @@ -1140,11 +1031,11 @@ public Node leaveFunctionNode(final FunctionNode functionNode) { try { method.end(); // wrap up this method - pop(functionNode.getCompileUnit()); - popMethodEmitter(method); + unit = lc.popCompileUnit(functionNode.getCompileUnit()); + method = lc.popMethodEmitter(method); LOG.info("=== END ", functionNode.getName()); - final FunctionNode newFunctionNode = functionNode.setState(getLexicalContext(), CompilationState.EMITTED); + final FunctionNode newFunctionNode = functionNode.setState(lc, CompilationState.EMITTED); newFunctionObject(newFunctionNode, functionNode); return newFunctionNode; @@ -1238,16 +1129,16 @@ final MethodEmitter savedMethod = method; for (final ArrayUnit arrayUnit : units) { - push(arrayUnit.getCompileUnit()); + unit = lc.pushCompileUnit(arrayUnit.getCompileUnit()); final String className = unit.getUnitClassName(); - final String name = getLexicalContext().getCurrentFunction().uniqueName(SPLIT_PREFIX.symbolName()); + final String name = lc.getCurrentFunction().uniqueName(SPLIT_PREFIX.symbolName()); final String signature = methodDescriptor(type, Object.class, ScriptFunction.class, ScriptObject.class, type); final MethodEmitter me = unit.getClassEmitter().method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), name, signature); - pushMethodEmitter(me); - - method.setFunctionNode(getLexicalContext().getCurrentFunction()); + method = lc.pushMethodEmitter(me); + + method.setFunctionNode(lc.getCurrentFunction()); method.begin(); fixScopeSlot(); @@ -1260,7 +1151,7 @@ method._return(); method.end(); - popMethodEmitter(me); + method = lc.popMethodEmitter(me); assert method == savedMethod; method.loadCompilerConstant(THIS); @@ -1271,7 +1162,7 @@ method.swap(); method.invokestatic(className, name, signature); - pop(unit); + unit = lc.popCompileUnit(unit); } return method; @@ -1407,7 +1298,7 @@ return loadRegexToken(regexToken); } // emit field - final String regexName = getLexicalContext().getCurrentFunction().uniqueName(REGEX_PREFIX.symbolName()); + final String regexName = lc.getCurrentFunction().uniqueName(REGEX_PREFIX.symbolName()); final ClassEmitter classEmitter = unit.getClassEmitter(); classEmitter.field(EnumSet.of(PRIVATE, STATIC), regexName, Object.class); @@ -1545,7 +1436,7 @@ method.registerReturn(); - final Type returnType = getLexicalContext().getCurrentFunction().getReturnType(); + final Type returnType = lc.getCurrentFunction().getReturnType(); final Node expression = returnNode.getExpression(); if (expression != null) { @@ -1756,7 +1647,7 @@ final CompileUnit splitCompileUnit = splitNode.getCompileUnit(); - final FunctionNode fn = getLexicalContext().getCurrentFunction(); + final FunctionNode fn = lc.getCurrentFunction(); final String className = splitCompileUnit.getUnitClassName(); final String name = splitNode.getName(); @@ -1767,7 +1658,7 @@ new Class<?>[] {ScriptFunction.class, Object.class, ScriptObject.class}; final MethodEmitter caller = method; - push(splitCompileUnit); + unit = lc.pushCompileUnit(splitCompileUnit); final Call splitCall = staticCallNoLookup( className, @@ -1781,8 +1672,7 @@ rtype, ptypes); - pushMethodEmitter(splitEmitter); - + method = lc.pushMethodEmitter(splitEmitter); method.setFunctionNode(fn); if (fn.needsCallee()) { @@ -1809,7 +1699,7 @@ } private void fixScopeSlot() { - if (getLexicalContext().getCurrentFunction().compilerConstant(SCOPE).getSlot() != SCOPE.slot()) { + if (lc.getCurrentFunction().compilerConstant(SCOPE).getSlot() != SCOPE.slot()) { // TODO hack to move the scope to the expected slot (that's needed because split methods reuse the same slots as the root method) method.load(Type.typeFor(ScriptObject.class), SCOPE.slot()); method.storeCompilerConstant(SCOPE); @@ -1826,15 +1716,15 @@ // Wrap up this method. method.loadCompilerConstant(RETURN); - method._return(getLexicalContext().getCurrentFunction().getReturnType()); + method._return(lc.getCurrentFunction().getReturnType()); method.end(); - pop(splitNode.getCompileUnit()); - popMethodEmitter(method); + unit = lc.popCompileUnit(splitNode.getCompileUnit()); + method = lc.popMethodEmitter(method); } catch (final Throwable t) { Context.printStackTrace(t); - final VerifyError e = new VerifyError("Code generation bug in \"" + splitNode.getName() + "\": likely stack misaligned: " + t + " " + getLexicalContext().getCurrentFunction().getSource().getName()); + final VerifyError e = new VerifyError("Code generation bug in \"" + splitNode.getName() + "\": likely stack misaligned: " + t + " " + lc.getCurrentFunction().getSource().getName()); e.initCause(t); throw e; } @@ -1862,7 +1752,7 @@ //has to be zero caller.label(new Label("split_return")); method.loadCompilerConstant(RETURN); - caller._return(getLexicalContext().getCurrentFunction().getReturnType()); + caller._return(lc.getCurrentFunction().getReturnType()); caller.label(breakLabel); } else { assert !targets.isEmpty(); @@ -1879,14 +1769,14 @@ caller.label(labels[i - low]); if (i == 0) { caller.loadCompilerConstant(RETURN); - caller._return(getLexicalContext().getCurrentFunction().getReturnType()); + caller._return(lc.getCurrentFunction().getReturnType()); } else { // Clear split state. caller.loadCompilerConstant(SCOPE); caller.checkcast(Scope.class); caller.load(-1); caller.invoke(Scope.SET_SPLIT_STATE); - caller.splitAwareGoto(getLexicalContext(), targets.get(i - 1)); + caller.splitAwareGoto(lc, targets.get(i - 1)); } } caller.label(breakLabel); @@ -2028,9 +1918,16 @@ public boolean enterThrowNode(final ThrowNode throwNode) { lineNumber(throwNode); + if (throwNode.isSyntheticRethrow()) { + //do not wrap whatever this is in an ecma exception, just rethrow it + load(throwNode.getExpression()); + method.athrow(); + return false; + } + method._new(ECMAException.class).dup(); - final Source source = getLexicalContext().getCurrentFunction().getSource(); + final Source source = lc.getCurrentFunction().getSource(); final Node expression = throwNode.getExpression(); final int position = throwNode.position(); @@ -2081,7 +1978,7 @@ //TODO this is very ugly - try not to call enter/leave methods directly //better to use the implicit lexical context scoping given by the visitor's //accept method. - getLexicalContext().push(catchBlock); + lc.push(catchBlock); enterBlock(catchBlock); final CatchNode catchNode = (CatchNode)catchBlocks.get(i).getStatements().get(0); @@ -2094,15 +1991,19 @@ protected void storeNonDiscard() { return; } + @Override protected void evaluate() { + if (catchNode.isSyntheticRethrow()) { + method.load(symbol); + return; + } /* * If caught object is an instance of ECMAException, then * bind obj.thrown to the script catch var. Or else bind the * caught object itself to the script catch var. */ final Label notEcmaException = new Label("no_ecma_exception"); - method.load(symbol).dup()._instanceof(ECMAException.class).ifeq(notEcmaException); method.checkcast(ECMAException.class); //TODO is this necessary? method.getField(ECMAException.THROWN); @@ -2137,7 +2038,7 @@ } leaveBlock(catchBlock); - getLexicalContext().pop(catchBlock); + lc.pop(catchBlock); } method.label(skip); @@ -2234,7 +2135,7 @@ final boolean hasScope = method.hasScope(); final Label tryLabel; - if(hasScope) { + if (hasScope) { tryLabel = new Label("with_try"); method.label(tryLabel); method.loadCompilerConstant(SCOPE); @@ -2245,7 +2146,7 @@ load(expression); assert expression.getType().isObject() : "with expression needs to be object: " + expression; - if(hasScope) { + if (hasScope) { // Construct a WithObject if we have a scope method.invoke(ScriptRuntime.OPEN_WITH); method.storeCompilerConstant(SCOPE); @@ -2285,7 +2186,7 @@ @Override public boolean enterADD(final UnaryNode unaryNode) { load(unaryNode.rhs()); - assert unaryNode.rhs().getType().isNumber(); + assert unaryNode.rhs().getType().isNumber() : unaryNode.rhs().getType() + " "+ unaryNode.getSymbol(); method.store(unaryNode.getSymbol()); return false; @@ -2320,7 +2221,7 @@ } method.convert(Type.OBJECT); } else if (value instanceof Boolean) { - method.getField(staticField(Boolean.class, value.toString().toUpperCase(), Boolean.class)); + method.getField(staticField(Boolean.class, value.toString().toUpperCase(Locale.ENGLISH), Boolean.class)); } else { load(rhs); method.convert(unaryNode.getType()); @@ -2387,13 +2288,13 @@ public boolean enterDISCARD(final UnaryNode unaryNode) { final Node rhs = unaryNode.rhs(); - discard.push(rhs); + lc.pushDiscard(rhs); load(rhs); - if (discard.peek() == rhs) { + if (lc.getCurrentDiscard() == rhs) { assert !rhs.isAssignment(); method.pop(); - discard.pop(); + lc.popDiscard(); } return false; @@ -2445,7 +2346,7 @@ assert lhs.getType().equals(rhs.getType()) && lhs.getType().equals(type) : lhs.getType() + " != " + rhs.getType() + " != " + type + " " + new ASTWriter(lhs) + " " + new ASTWriter(rhs); load(lhs); load(rhs); - method.add(); + method.add(); //if the symbol is optimistic, it always needs to be written, not on the stack? method.store(symbol); return null; } @@ -2989,53 +2890,12 @@ * Generate all shared scope calls generated during codegen. */ protected void generateScopeCalls() { - for (final SharedScopeCall scopeAccess : scopeCalls.values()) { + for (final SharedScopeCall scopeAccess : lc.getScopeCalls()) { scopeAccess.generateScopeCall(); } } /** - * Get a shared static method representing a dynamic scope callsite. - * - * @param symbol the symbol - * @param valueType the value type of the symbol - * @param returnType the return type - * @param paramTypes the parameter types - * @param flags the callsite flags - * @return an object representing a shared scope call - */ - private SharedScopeCall getScopeCall(final Symbol symbol, final Type valueType, final Type returnType, - final Type[] paramTypes, final int flags) { - - final SharedScopeCall scopeCall = new SharedScopeCall(symbol, valueType, returnType, paramTypes, flags); - if (scopeCalls.containsKey(scopeCall)) { - return scopeCalls.get(scopeCall); - } - scopeCall.setClassAndName(unit, getLexicalContext().getCurrentFunction().uniqueName("scopeCall")); - scopeCalls.put(scopeCall, scopeCall); - return scopeCall; - } - - /** - * Get a shared static method representing a dynamic scope get access. - * - * @param type the type of the variable - * @param symbol the symbol - * @param flags the callsite flags - * @return an object representing a shared scope call - */ - private SharedScopeCall getScopeGet(final Type type, final Symbol symbol, final int flags) { - - final SharedScopeCall scopeCall = new SharedScopeCall(symbol, type, type, null, flags); - if (scopeCalls.containsKey(scopeCall)) { - return scopeCalls.get(scopeCall); - } - scopeCall.setClassAndName(unit, getLexicalContext().getCurrentFunction().uniqueName("scopeCall")); - scopeCalls.put(scopeCall, scopeCall); - return scopeCall; - } - - /** * Debug code used to print symbols * * @param block the block we are in @@ -3129,14 +2989,14 @@ private void prologue() { final Symbol targetSymbol = target.getSymbol(); - final Symbol scopeSymbol = getLexicalContext().getCurrentFunction().compilerConstant(SCOPE); + final Symbol scopeSymbol = lc.getCurrentFunction().compilerConstant(SCOPE); /** * This loads the parts of the target, e.g base and index. they are kept * on the stack throughout the store and used at the end to execute it */ - target.accept(new NodeVisitor() { + target.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { @Override public boolean enterIdentNode(final IdentNode node) { if (targetSymbol.isScope()) { @@ -3203,22 +3063,21 @@ * @return the quick symbol */ private Symbol quickSymbol(final Type type, final String prefix) { - final String name = getLexicalContext().getCurrentFunction().uniqueName(prefix); + final String name = lc.getCurrentFunction().uniqueName(prefix); final Symbol symbol = new Symbol(name, IS_TEMP | IS_INTERNAL); symbol.setType(type); - final int quickSlot = nextFreeSlots[nextFreeSlotsSize - 1]; - nextFreeSlots[nextFreeSlotsSize - 1] = quickSlot + symbol.slotCount(); - symbol.setSlot(quickSlot); + + symbol.setSlot(lc.quickSlot(symbol)); return symbol; } // store the result that "lives on" after the op, e.g. "i" in i++ postfix. protected void storeNonDiscard() { - if (discard.peek() == assignNode) { + if (lc.getCurrentDiscard() == assignNode) { assert assignNode.isAssignment(); - discard.pop(); + lc.popDiscard(); return; } @@ -3246,7 +3105,7 @@ */ method.convert(target.getType()); - target.accept(new NodeVisitor() { + target.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { @Override protected boolean enterDefault(Node node) { throw new AssertionError("Unexpected node " + node + " in store epilogue"); @@ -3308,7 +3167,6 @@ } private void newFunctionObject(final FunctionNode functionNode, final FunctionNode originalFunctionNode) { - final LexicalContext lc = getLexicalContext(); assert lc.peek() == functionNode; // We don't emit a ScriptFunction on stack for: // 1. the outermost compiled function (as there's no code being generated in its outer context that'd need it
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk/nashorn/internal/codegen/CodeGeneratorLexicalContext.java Mon Jun 03 23:24:36 2013 -0700 @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.nashorn.internal.codegen; + +import java.util.ArrayDeque; +import java.util.Collection; +import java.util.Collections; +import java.util.Deque; +import java.util.HashMap; +import java.util.Map; + +import jdk.nashorn.internal.codegen.types.Type; +import jdk.nashorn.internal.ir.Block; +import jdk.nashorn.internal.ir.FunctionNode; +import jdk.nashorn.internal.ir.LexicalContext; +import jdk.nashorn.internal.ir.LexicalContextNode; +import jdk.nashorn.internal.ir.Node; +import jdk.nashorn.internal.ir.Symbol; +import jdk.nashorn.internal.ir.WithNode; + +/** + * A lexical context that also tracks if we have any dynamic scopes in the context. Such scopes can have new + * variables introduced into them at run time - a with block or a function directly containing an eval call. + * Furthermore, this class keeps track of current discard state, which the current method emitter being used is, + * the current compile unit, and local variable indexes + */ +final class CodeGeneratorLexicalContext extends LexicalContext { + private int dynamicScopeCount; + + /** Map of shared scope call sites */ + private final Map<SharedScopeCall, SharedScopeCall> scopeCalls = new HashMap<>(); + + /** Compile unit stack - every time we start a sub method (e.g. a split) we push one */ + private final Deque<CompileUnit> compileUnits = new ArrayDeque<>(); + + /** Method emitter stack - every time we start a sub method (e.g. a split) we push one */ + private final Deque<MethodEmitter> methodEmitters = new ArrayDeque<>(); + + /** The discard stack - whenever we enter a discard node we keep track of its return value status - + * i.e. should we keep it or throw it away */ + private final Deque<Node> discard = new ArrayDeque<>(); + + /** A stack tracking the next free local variable slot in the blocks. There's one entry for every block + * currently on the lexical context stack. */ + private int[] nextFreeSlots = new int[16]; + + /** size of next free slot vector */ + private int nextFreeSlotsSize; + + @Override + public <T extends LexicalContextNode> T push(final T node) { + if (isDynamicScopeBoundary(node)) { + ++dynamicScopeCount; + } + return super.push(node); + } + + @Override + public <T extends LexicalContextNode> T pop(final T node) { + final T popped = super.pop(node); + if (isDynamicScopeBoundary(popped)) { + --dynamicScopeCount; + } + if (node instanceof Block) { + --nextFreeSlotsSize; + } + return popped; + } + + private boolean isDynamicScopeBoundary(final LexicalContextNode node) { + if (node instanceof Block) { + // Block's immediate parent is a with node. Note we aren't testing for a WithNode, as that'd capture + // processing of WithNode.expression too, but it should be unaffected. + return !isEmpty() && peek() instanceof WithNode; + } else if (node instanceof FunctionNode) { + // Function has a direct eval in it (so a top-level "var ..." in the eval code can introduce a new + // variable into the function's scope), and it isn't strict (as evals in strict functions get an + // isolated scope). + return isFunctionDynamicScope((FunctionNode)node); + } + return false; + } + + boolean inDynamicScope() { + return dynamicScopeCount > 0; + } + + static boolean isFunctionDynamicScope(FunctionNode fn) { + return fn.hasEval() && !fn.isStrict(); + } + + MethodEmitter pushMethodEmitter(final MethodEmitter newMethod) { + methodEmitters.push(newMethod); + return newMethod; + } + + MethodEmitter popMethodEmitter(final MethodEmitter oldMethod) { + assert methodEmitters.peek() == oldMethod; + methodEmitters.pop(); + return methodEmitters.isEmpty() ? null : methodEmitters.peek(); + } + + CompileUnit pushCompileUnit(final CompileUnit newUnit) { + compileUnits.push(newUnit); + return newUnit; + } + + CompileUnit popCompileUnit(final CompileUnit oldUnit) { + assert compileUnits.peek() == oldUnit; + compileUnits.pop(); + return compileUnits.isEmpty() ? null : compileUnits.peek(); + } + + boolean hasCompileUnits() { + return !compileUnits.isEmpty(); + } + + Collection<SharedScopeCall> getScopeCalls() { + return Collections.unmodifiableCollection(scopeCalls.values()); + } + + /** + * Get a shared static method representing a dynamic scope callsite. + * + * @param unit current compile unit + * @param symbol the symbol + * @param valueType the value type of the symbol + * @param returnType the return type + * @param paramTypes the parameter types + * @param flags the callsite flags + * @return an object representing a shared scope call + */ + SharedScopeCall getScopeCall(final CompileUnit unit, final Symbol symbol, final Type valueType, final Type returnType, final Type[] paramTypes, final int flags) { + final SharedScopeCall scopeCall = new SharedScopeCall(symbol, valueType, returnType, paramTypes, flags); + if (scopeCalls.containsKey(scopeCall)) { + return scopeCalls.get(scopeCall); + } + scopeCall.setClassAndName(unit, getCurrentFunction().uniqueName("scopeCall")); + scopeCalls.put(scopeCall, scopeCall); + return scopeCall; + } + + /** + * Get a shared static method representing a dynamic scope get access. + * + * @param unit current compile unit + * @param type the type of the variable + * @param symbol the symbol + * @param flags the callsite flags + * @return an object representing a shared scope call + */ + SharedScopeCall getScopeGet(final CompileUnit unit, final Type type, final Symbol symbol, final int flags) { + final SharedScopeCall scopeCall = new SharedScopeCall(symbol, type, type, null, flags); + if (scopeCalls.containsKey(scopeCall)) { + return scopeCalls.get(scopeCall); + } + scopeCall.setClassAndName(unit, getCurrentFunction().uniqueName("scopeCall")); + scopeCalls.put(scopeCall, scopeCall); + return scopeCall; + } + + + void nextFreeSlot(final Block block) { + final boolean isFunctionBody = isFunctionBody(); + + final int nextFreeSlot; + if (isFunctionBody) { + // On entry to function, start with slot 0 + nextFreeSlot = 0; + } else { + // Otherwise, continue from previous block's first free slot + nextFreeSlot = nextFreeSlots[nextFreeSlotsSize - 1]; + } + if (nextFreeSlotsSize == nextFreeSlots.length) { + final int[] newNextFreeSlots = new int[nextFreeSlotsSize * 2]; + System.arraycopy(nextFreeSlots, 0, newNextFreeSlots, 0, nextFreeSlotsSize); + nextFreeSlots = newNextFreeSlots; + } + nextFreeSlots[nextFreeSlotsSize++] = assignSlots(block, nextFreeSlot); + } + + private static int assignSlots(final Block block, final int firstSlot) { + int nextSlot = firstSlot; + for (final Symbol symbol : block.getSymbols()) { + if (symbol.hasSlot()) { + symbol.setSlot(nextSlot); + nextSlot += symbol.slotCount(); + } + } + return nextSlot; + } + + void pushDiscard(final Node node) { + discard.push(node); + } + + Node popDiscard() { + return discard.pop(); + } + + Node getCurrentDiscard() { + return discard.peek(); + } + + int quickSlot(final Symbol symbol) { + final int quickSlot = nextFreeSlots[nextFreeSlotsSize - 1]; + nextFreeSlots[nextFreeSlotsSize - 1] = quickSlot + symbol.slotCount(); + return quickSlot; + } + +} +
--- a/src/jdk/nashorn/internal/codegen/CompilationPhase.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/codegen/CompilationPhase.java Mon Jun 03 23:24:36 2013 -0700 @@ -11,20 +11,27 @@ 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.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.LexicalContext; 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 @@ 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 @@ 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 @@ 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 @@ * @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 @@ -208,6 +213,89 @@ }, /* + * 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 * a + b a ScriptRuntime.ADD with call overhead or a dadd with much less). @@ -218,7 +306,6 @@ 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;
--- a/src/jdk/nashorn/internal/codegen/Compiler.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/codegen/Compiler.java Mon Jun 03 23:24:36 2013 -0700 @@ -99,7 +99,7 @@ private boolean strict; - private CodeInstaller<ScriptEnvironment> installer; + private final CodeInstaller<ScriptEnvironment> installer; private final TemporarySymbols temporarySymbols = new TemporarySymbols(); @@ -219,6 +219,7 @@ 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 @@ 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 @@ } if (sb != null) { - LOG.info(sb); + LOG.fine(sb); } return rootClass;
--- a/src/jdk/nashorn/internal/codegen/CompilerConstants.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/codegen/CompilerConstants.java Mon Jun 03 23:24:36 2013 -0700 @@ -262,7 +262,7 @@ * @return the internal descriptor for this type */ public static String typeDescriptor(final Class<?> clazz) { - return Type.getDescriptor(clazz); + return Type.typeFor(clazz).getDescriptor(); } /**
--- a/src/jdk/nashorn/internal/codegen/FinalizeTypes.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/codegen/FinalizeTypes.java Mon Jun 03 23:24:36 2013 -0700 @@ -31,6 +31,7 @@ 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 @@ * 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 @@ 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 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 @@ @Override public Node leaveFunctionNode(final FunctionNode functionNode) { - return functionNode.setState(getLexicalContext(), CompilationState.FINALIZED); + return functionNode.setState(lc, CompilationState.FINALIZED); } @Override @@ -450,7 +450,7 @@ 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 @@ } 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 @@ 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 @@ 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 @@ 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 @@ } } 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ if (literalNode != null) { //inherit literal symbol for attr. - literalNode = (LiteralNode<?>)literalNode.setSymbol(getLexicalContext(), parent.getSymbol()); + literalNode = (LiteralNode<?>)literalNode.setSymbol(lc, parent.getSymbol()); } return literalNode;
--- a/src/jdk/nashorn/internal/codegen/FoldConstants.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/codegen/FoldConstants.java Mon Jun 03 23:24:36 2013 -0700 @@ -33,7 +33,9 @@ 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 @@ /** * 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 @@ @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 @@ return null; } + if (rhsNode instanceof ArrayLiteralNode) { + return null; + } + final LiteralNode<?> rhs = (LiteralNode<?>)rhsNode; final boolean rhsInteger = rhs.getType().isInteger(); @@ -212,6 +219,10 @@ 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 @@ 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);
--- a/src/jdk/nashorn/internal/codegen/Lower.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/codegen/Lower.java Mon Jun 03 23:24:36 2013 -0700 @@ -80,7 +80,7 @@ * finalized. */ -final class Lower extends NodeOperatorVisitor { +final class Lower extends NodeOperatorVisitor<BlockLexicalContext> { private static final DebugLogger LOG = new DebugLogger("lower"); @@ -105,7 +105,7 @@ 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 @@ @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 @@ //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 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 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 @@ @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 @@ 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 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 @@ } 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 @@ * @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(); - assert tryNode.getFinallyBody() == null; - - 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 @@ @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 @@ @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 @@ 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 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 @@ @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 @@ 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 @@ * @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 @@ // '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 @@ 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 @@ } 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 @@ private Node addStatement(final Statement statement) { - ((BlockLexicalContext)getLexicalContext()).appendStatement(statement); + lc.appendStatement(statement); return statement; }
--- a/src/jdk/nashorn/internal/codegen/MethodEmitter.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/codegen/MethodEmitter.java Mon Jun 03 23:24:36 2013 -0700 @@ -2081,7 +2081,9 @@ * @param args debug information to print */ private void debug(final Object... args) { - debug(30, args); + if (DEBUG) { + debug(30, args); + } } /** @@ -2091,7 +2093,9 @@ * @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 @@ new Throwable().printStackTrace(LOG.getOutputStream()); } } - } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk/nashorn/internal/codegen/RangeAnalyzer.java Mon Jun 03 23:24:36 2013 -0700 @@ -0,0 +1,476 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.nashorn.internal.codegen; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; + +import jdk.nashorn.internal.codegen.types.Range; +import jdk.nashorn.internal.codegen.types.Type; +import jdk.nashorn.internal.ir.Assignment; +import jdk.nashorn.internal.ir.BinaryNode; +import jdk.nashorn.internal.ir.ForNode; +import jdk.nashorn.internal.ir.IdentNode; +import jdk.nashorn.internal.ir.LexicalContext; +import jdk.nashorn.internal.ir.LiteralNode; +import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; +import jdk.nashorn.internal.ir.LoopNode; +import jdk.nashorn.internal.ir.Node; +import jdk.nashorn.internal.ir.RuntimeNode; +import jdk.nashorn.internal.ir.Symbol; +import jdk.nashorn.internal.ir.UnaryNode; +import jdk.nashorn.internal.ir.VarNode; +import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor; +import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.parser.TokenType; +import jdk.nashorn.internal.runtime.DebugLogger; + +/** + * Range analysis and narrowing of type where it can be proven + * that there is no spillover, e.g. + * + * function func(c) { + * var v = c & 0xfff; + * var w = c & 0xeee; + * var x = v * w; + * return x; + * } + * + * Proves that the multiplication never exceeds 24 bits and can thus be an int + */ +final class RangeAnalyzer extends NodeOperatorVisitor<LexicalContext> { + static final DebugLogger LOG = new DebugLogger("ranges"); + + private static final Range.Functionality RANGE = new Range.Functionality(LOG); + + private final Map<LoopNode, Symbol> loopCounters = new HashMap<>(); + + RangeAnalyzer() { + super(new LexicalContext()); + } + + @Override + public boolean enterForNode(final ForNode forNode) { + //conservatively attempt to identify the loop counter. Null means that it wasn't + //properly identified and that no optimizations can be made with it - its range is + //simply unknown in that case, if it is assigned in the loop + final Symbol counter = findLoopCounter(forNode); + LOG.fine("Entering forNode " + forNode + " counter = " + counter); + if (counter != null && !assignedInLoop(forNode, counter)) { + loopCounters.put(forNode, counter); + } + return true; + } + + //destination visited + private Symbol setRange(final Node dest, final Range range) { + if (range.isUnknown()) { + return null; + } + + final Symbol symbol = dest.getSymbol(); + assert symbol != null : dest + " " + dest.getClass() + " has no symbol"; + assert symbol.getRange() != null : symbol + " has no range"; + final Range symRange = RANGE.join(symbol.getRange(), range); + + //anything assigned in the loop, not being the safe loop counter(s) invalidates its entire range + if (lc.inLoop() && !isLoopCounter(lc.getCurrentLoop(), symbol)) { + symbol.setRange(Range.createGenericRange()); + return symbol; + } + + if (!symRange.equals(symbol.getRange())) { + LOG.fine("Modify range for " + dest + " " + symbol + " from " + symbol.getRange() + " to " + symRange + " (in node = " + dest + ")" ); + symbol.setRange(symRange); + } + + return null; + } + + @Override + public Node leaveADD(final BinaryNode node) { + setRange(node, RANGE.add(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange())); + return node; + } + + @Override + public Node leaveSUB(final BinaryNode node) { + setRange(node, RANGE.sub(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange())); + return node; + } + + @Override + public Node leaveMUL(final BinaryNode node) { + setRange(node, RANGE.mul(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange())); + return node; + } + + @Override + public Node leaveDIV(final BinaryNode node) { + setRange(node, RANGE.div(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange())); + return node; + } + + @Override + public Node leaveMOD(final BinaryNode node) { + setRange(node, RANGE.mod(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange())); + return node; + } + + @Override + public Node leaveBIT_AND(final BinaryNode node) { + setRange(node, RANGE.and(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange())); + return node; + } + + @Override + public Node leaveBIT_OR(final BinaryNode node) { + setRange(node, RANGE.or(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange())); + return node; + } + + @Override + public Node leaveBIT_XOR(final BinaryNode node) { + setRange(node, RANGE.xor(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange())); + return node; + } + + @Override + public Node leaveSAR(final BinaryNode node) { + setRange(node, RANGE.sar(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange())); + return node; + } + + @Override + public Node leaveSHL(final BinaryNode node) { + setRange(node, RANGE.shl(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange())); + return node; + } + + @Override + public Node leaveSHR(final BinaryNode node) { + setRange(node, RANGE.shr(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange())); + return node; + } + + private Node leaveCmp(final BinaryNode node) { + setRange(node, Range.createTypeRange(Type.BOOLEAN)); + return node; + } + + @Override + public Node leaveEQ(final BinaryNode node) { + return leaveCmp(node); + } + + @Override + public Node leaveEQ_STRICT(final BinaryNode node) { + return leaveCmp(node); + } + + @Override + public Node leaveNE(final BinaryNode node) { + return leaveCmp(node); + } + + @Override + public Node leaveNE_STRICT(final BinaryNode node) { + return leaveCmp(node); + } + + @Override + public Node leaveLT(final BinaryNode node) { + return leaveCmp(node); + } + + @Override + public Node leaveLE(final BinaryNode node) { + return leaveCmp(node); + } + + @Override + public Node leaveGT(final BinaryNode node) { + return leaveCmp(node); + } + + @Override + public Node leaveGE(final BinaryNode node) { + return leaveCmp(node); + } + + @Override + public Node leaveASSIGN(final BinaryNode node) { + Range range = node.rhs().getSymbol().getRange(); + if (range.isUnknown()) { + range = Range.createGenericRange(); + } + + setRange(node.lhs(), range); + setRange(node, range); + + return node; + } + + private Node leaveSelfModifyingAssign(final BinaryNode node, final Range range) { + setRange(node.lhs(), range); + setRange(node, range); + return node; + } + + private Node leaveSelfModifyingAssign(final UnaryNode node, final Range range) { + setRange(node.rhs(), range); + setRange(node, range); + return node; + } + + @Override + public Node leaveASSIGN_ADD(final BinaryNode node) { + return leaveSelfModifyingAssign(node, RANGE.add(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange())); + } + + @Override + public Node leaveASSIGN_SUB(final BinaryNode node) { + return leaveSelfModifyingAssign(node, RANGE.sub(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange())); + } + + @Override + public Node leaveASSIGN_MUL(final BinaryNode node) { + return leaveSelfModifyingAssign(node, RANGE.mul(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange())); + } + + @Override + public Node leaveASSIGN_DIV(final BinaryNode node) { + return leaveSelfModifyingAssign(node, Range.createTypeRange(Type.NUMBER)); + } + + @Override + public Node leaveASSIGN_MOD(final BinaryNode node) { + return leaveSelfModifyingAssign(node, Range.createTypeRange(Type.NUMBER)); + } + + @Override + public Node leaveASSIGN_BIT_AND(final BinaryNode node) { + return leaveSelfModifyingAssign(node, RANGE.and(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange())); + } + + @Override + public Node leaveASSIGN_BIT_OR(final BinaryNode node) { + return leaveSelfModifyingAssign(node, RANGE.or(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange())); + } + + @Override + public Node leaveASSIGN_BIT_XOR(final BinaryNode node) { + return leaveSelfModifyingAssign(node, RANGE.xor(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange())); + } + + @Override + public Node leaveASSIGN_SAR(final BinaryNode node) { + return leaveSelfModifyingAssign(node, RANGE.sar(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange())); + } + + @Override + public Node leaveASSIGN_SHR(final BinaryNode node) { + return leaveSelfModifyingAssign(node, RANGE.shr(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange())); + } + + @Override + public Node leaveASSIGN_SHL(final BinaryNode node) { + return leaveSelfModifyingAssign(node, RANGE.shl(node.lhs().getSymbol().getRange(), node.rhs().getSymbol().getRange())); + } + + @Override + public Node leaveDECINC(final UnaryNode node) { + switch (node.tokenType()) { + case DECPREFIX: + case DECPOSTFIX: + return leaveSelfModifyingAssign(node, RANGE.sub(node.rhs().getSymbol().getRange(), Range.createRange(1))); + case INCPREFIX: + case INCPOSTFIX: + return leaveSelfModifyingAssign(node, RANGE.add(node.rhs().getSymbol().getRange(), Range.createRange(1))); + default: + assert false; + return node; + } + } + + @Override + public Node leaveADD(final UnaryNode node) { + Range range = node.rhs().getSymbol().getRange(); + if (!range.getType().isNumeric()) { + range = Range.createTypeRange(Type.NUMBER); + } + setRange(node, range); + return node; + } + + @Override + public Node leaveBIT_NOT(final UnaryNode node) { + setRange(node, Range.createTypeRange(Type.INT)); + return node; + } + + @Override + public Node leaveNOT(final UnaryNode node) { + setRange(node, Range.createTypeRange(Type.BOOLEAN)); + return node; + } + + @Override + public Node leaveSUB(final UnaryNode node) { + setRange(node, RANGE.neg(node.rhs().getSymbol().getRange())); + return node; + } + + @Override + public Node leaveVarNode(final VarNode node) { + if (node.isAssignment()) { + Range range = node.getInit().getSymbol().getRange(); + range = range.isUnknown() ? Range.createGenericRange() : range; + + setRange(node.getName(), range); + setRange(node, range); + } + + return node; + } + + @SuppressWarnings("rawtypes") + @Override + public boolean enterLiteralNode(final LiteralNode node) { + // ignore array literals + return !(node instanceof ArrayLiteralNode); + } + + @Override + public Node leaveLiteralNode(@SuppressWarnings("rawtypes") final LiteralNode node) { + if (node.getType().isInteger()) { + setRange(node, Range.createRange(node.getInt32())); + } else if (node.getType().isNumber()) { + setRange(node, Range.createRange(node.getNumber())); + } else if (node.getType().isLong()) { + setRange(node, Range.createRange(node.getLong())); + } else if (node.getType().isBoolean()) { + setRange(node, Range.createTypeRange(Type.BOOLEAN)); + } else { + setRange(node, Range.createGenericRange()); + } + return node; + } + + @Override + public boolean enterRuntimeNode(final RuntimeNode node) { + // a runtime node that cannot be specialized is no point entering + return node.getRequest().canSpecialize(); + } + + /** + * Check whether a symbol is unsafely assigned in a loop - i.e. repeteadly assigned and + * not being identified as the loop counter. That means we don't really know anything + * about its range. + * @param loopNode loop node + * @param symbol symbol + * @return true if assigned in loop + */ + // TODO - this currently checks for nodes only - needs to be augmented for while nodes + // assignment analysis is also very conservative + private static boolean assignedInLoop(final LoopNode loopNode, final Symbol symbol) { + final HashSet<Node> skip = new HashSet<>(); + final HashSet<Node> assignmentsInLoop = new HashSet<>(); + + loopNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { + private boolean assigns(final Node node, final Symbol s) { + return node.isAssignment() && ((Assignment<?>)node).getAssignmentDest().getSymbol() == s; + } + + @Override + public boolean enterForNode(final ForNode forNode) { + if (forNode.getInit() != null) { + skip.add(forNode.getInit()); + } + if (forNode.getModify() != null) { + skip.add(forNode.getModify()); + } + return true; + } + + @Override + public Node leaveDefault(final Node node) { + //if this is an assignment to symbol + if (!skip.contains(node) && assigns(node, symbol)) { + assignmentsInLoop.add(node); + } + return node; + } + }); + + return !assignmentsInLoop.isEmpty(); + } + + /** + * Check for a loop counter. This is currently quite conservative, in that it only handles + * x <= counter and x < counter. + * + * @param node loop node to check + * @return + */ + private static Symbol findLoopCounter(final LoopNode node) { + final Node test = node.getTest(); + + if (test != null && test.isComparison()) { + final BinaryNode binaryNode = (BinaryNode)test; + final Node lhs = binaryNode.lhs(); + final Node rhs = binaryNode.rhs(); + + //detect ident cmp int_literal + if (lhs instanceof IdentNode && rhs instanceof LiteralNode && ((LiteralNode<?>)rhs).getType().isInteger()) { + final Symbol symbol = lhs.getSymbol(); + final int margin = ((LiteralNode<?>)rhs).getInt32(); + final TokenType op = test.tokenType(); + + switch (op) { + case LT: + case LE: + symbol.setRange(RANGE.join(symbol.getRange(), Range.createRange(op == TokenType.LT ? margin - 1 : margin))); + return symbol; + case GT: + case GE: + //setRange(lhs, Range.createRange(op == TokenType.GT ? margin + 1 : margin)); + //return symbol; + default: + break; + } + } + } + + return null; + } + + private boolean isLoopCounter(final LoopNode loopNode, final Symbol symbol) { + //this only works if loop nodes aren't replaced by other ones during this transform, but they are not + return loopCounters.get(loopNode) == symbol; + } +}
--- a/src/jdk/nashorn/internal/codegen/SharedScopeCall.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/codegen/SharedScopeCall.java Mon Jun 03 23:24:36 2013 -0700 @@ -116,9 +116,10 @@ /** * 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()); } /**
--- a/src/jdk/nashorn/internal/codegen/Splitter.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/codegen/Splitter.java Mon Jun 03 23:24:36 2013 -0700 @@ -49,12 +49,12 @@ /** * 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 @@ * @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 @@ 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 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 @@ @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 @@ 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 @@ * @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 @@ splits.add(createBlockSplitNode(block, function, statements, statementsWeight)); } - return block.setStatements(getLexicalContext(), splits); + return block.setStatements(lc, splits); } /** @@ -258,7 +257,7 @@ // 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 @@ 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;
--- a/src/jdk/nashorn/internal/codegen/WeighNodes.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/codegen/WeighNodes.java Mon Jun 03 23:24:36 2013 -0700 @@ -27,6 +27,7 @@ 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.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 @@ * 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 @@ * @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; }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk/nashorn/internal/codegen/types/Range.java Mon Jun 03 23:24:36 2013 -0700 @@ -0,0 +1,705 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.nashorn.internal.codegen.types; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import jdk.nashorn.internal.runtime.DebugLogger; +import jdk.nashorn.internal.runtime.JSType; + +/** + * Represents the value range of a symbol. + */ +public abstract class Range { + + private static final Range GENERIC_RANGE = new Range() { + @Override + public Type getType() { + return Type.OBJECT; + } + }; + + private static final Range NUMBER_RANGE = new Range() { + @Override + public Type getType() { + return Type.NUMBER; + } + }; + + private static final Range UNKNOWN_RANGE = new Range() { + @Override + public Type getType() { + return Type.UNKNOWN; + } + + @Override + public boolean isUnknown() { + return true; + } + }; + + private static class IntegerRange extends Range { + private final long min; + private final long max; + private final Type type; + + private IntegerRange(final long min, final long max) { + assert min <= max; + this.min = min; + this.max = max; + this.type = typeFromRange(min, max); + } + + private static Type typeFromRange(final long from, final long to) { + if (from >= Integer.MIN_VALUE && to <= Integer.MAX_VALUE) { + return Type.INT; + } + return Type.LONG; + } + + @Override + public Type getType() { + return type; + } + + public long getMin() { + return min; + } + + public long getMax() { + return max; + } + + @Override + public boolean isIntegerConst() { + return getMin() == getMax(); + } + + private long getBitMask() { + if (min == max) { + return min; + } + + if (min < 0) { + return ~0L; + } + + long mask = 1; + while (mask < max) { + mask = (mask << 1) | 1; + } + return mask; + } + + @Override + public boolean equals(final Object obj) { + if (obj instanceof IntegerRange) { + final IntegerRange other = (IntegerRange)obj; + return this.type == other.type && this.min == other.min && this.max == other.max; + } + return false; + } + + @Override + public int hashCode() { + return Long.hashCode(min) ^ Long.hashCode(max); + } + + @Override + public String toString() { + return super.toString() + "[" + min +", " + max + "]"; + } + } + + /** + * Get narrowest type for this range + * @return type + */ + public abstract Type getType(); + + /** + * Is this range unknown + * @return true if unknown + */ + public boolean isUnknown() { + return false; + } + + /** + * Check if an integer is enough to span this range + * @return true if integer is enough + */ + public boolean isIntegerType() { + return this instanceof IntegerRange; + } + + /** + * Check if an integer is enough to span this range + * @return true if integer is enough + */ + public boolean isIntegerConst() { + return false; + } + + /** + * Create an unknown range - this is most likely a singleton object + * and it represents "we have no known range information" + * @return the range + */ + public static Range createUnknownRange() { + return UNKNOWN_RANGE; + } + + /** + * Create a constant range: [value, value] + * @param value value + * @return the range + */ + public static Range createRange(final int value) { + return createIntegerRange(value, value); + } + + /** + * Create a constant range: [value, value] + * @param value value + * @return the range + */ + public static Range createRange(final long value) { + return createIntegerRange(value, value); + } + + /** + * Create a constant range: [value, value] + * @param value value + * @return the range + */ + public static Range createRange(final double value) { + if (isRepresentableAsLong(value)) { + return createIntegerRange((long) value, (long) value); + } + return createNumberRange(); + } + + /** + * Create a constant range: [value, value] + * @param value value + * @return the range + */ + public static Range createRange(final Object value) { + if (value instanceof Integer) { + return createRange((int)value); + } else if (value instanceof Long) { + return createRange((long)value); + } else if (value instanceof Double) { + return createRange((double)value); + } + + return createGenericRange(); + } + + /** + * Create a generic range - object symbol that carries no range + * information + * @return the range + */ + public static Range createGenericRange() { + return GENERIC_RANGE; + } + + /** + * Create a number range - number symbol that carries no range + * information + * @return the range + */ + public static Range createNumberRange() { + return NUMBER_RANGE; + } + + /** + * Create an integer range [min, max] + * @param min minimum value, inclusive + * @param max maximum value, inclusive + * @return the range + */ + public static IntegerRange createIntegerRange(final long min, final long max) { + return new IntegerRange(min, max); + } + + /** + * Create an integer range of maximum type width for the given type + * @param type the type + * @return the range + */ + public static IntegerRange createIntegerRange(final Type type) { + assert type.isNumeric() && !type.isNumber(); + final long min; + final long max; + if (type.isInteger()) { + min = Integer.MIN_VALUE; + max = Integer.MAX_VALUE; + } else if (type.isLong()) { + min = Long.MIN_VALUE; + max = Long.MAX_VALUE; + } else { + throw new AssertionError(); //type incompatible with integer range + } + return new IntegerRange(min, max); + } + + /** + * Create an range of maximum type width for the given type + * @param type the type + * @return the range + */ + public static Range createTypeRange(final Type type) { + if (type.isNumber()) { + return createNumberRange(); + } else if (type.isNumeric()) { + return createIntegerRange(type); + } else { + return createGenericRange(); + } + } + + // check that add doesn't overflow + private static boolean checkAdd(final long a, final long b) { + final long result = a + b; + return ((a ^ result) & (b ^ result)) >= 0; + } + + // check that sub doesn't overflow + private static boolean checkSub(final long a, final long b) { + final long result = a - b; + return ((a ^ result) & (b ^ result)) >= 0; + } + + private static boolean checkMul(final long a, final long b) { + // TODO correct overflow check + return a >= Integer.MIN_VALUE && a <= Integer.MAX_VALUE && b >= Integer.MIN_VALUE && b <= Integer.MAX_VALUE; + } + + /** + * The range functionality class responsible for merging ranges and drawing + * range conclusions from operations executed + */ + public static class Functionality { + /** logger */ + protected final DebugLogger log; + + /** + * Constructor + * @param log logger + */ + public Functionality(final DebugLogger log) { + this.log = log; + } + + /** + * Join two ranges + * @param a first range + * @param b second range + * @return the joined range + */ + public Range join(final Range a, final Range b) { + if (a.equals(b)) { + return a; + } + + Type joinedType = a.getType(); + if (a.getType() != b.getType()) { + if (a.isUnknown()) { + return b; + } + if (b.isUnknown()) { + return a; + } + + joinedType = Type.widest(a.getType(), b.getType()); + } + + if (joinedType.isInteger() || joinedType.isLong()) { + return createIntegerRange( + Math.min(((IntegerRange) a).getMin(), ((IntegerRange) b).getMin()), + Math.max(((IntegerRange) a).getMax(), ((IntegerRange) b).getMax())); + } + + return createTypeRange(joinedType); + } + + /** + * Add operation + * @param a range of first symbol to be added + * @param b range of second symbol to be added + * @return resulting range representing the value range after add + */ + public Range add(final Range a, final Range b) { + if (a.isIntegerType() && b.isIntegerType()) { + final IntegerRange lhs = (IntegerRange)a; + final IntegerRange rhs = (IntegerRange)b; + if (checkAdd(lhs.getMin(), rhs.getMin()) && checkAdd(lhs.getMax(), rhs.getMax())) { + return createIntegerRange(lhs.getMin() + rhs.getMin(), lhs.getMax() + rhs.getMax()); + } + } + + if (a.getType().isNumeric() && b.getType().isNumeric()) { + return createNumberRange(); + } + + return createGenericRange(); + } + + /** + * Sub operation + * @param a range of first symbol to be subtracted + * @param b range of second symbol to be subtracted + * @return resulting range representing the value range after subtraction + */ + public Range sub(final Range a, final Range b) { + if (a.isIntegerType() && b.isIntegerType()) { + final IntegerRange lhs = (IntegerRange)a; + final IntegerRange rhs = (IntegerRange)b; + if (checkSub(lhs.getMin(), rhs.getMax()) && checkSub(lhs.getMax(), rhs.getMin())) { + return createIntegerRange(lhs.getMin() - rhs.getMax(), lhs.getMax() - rhs.getMin()); + } + } + + if (a.getType().isNumeric() && b.getType().isNumeric()) { + return createNumberRange(); + } + + return createGenericRange(); + } + + /** + * Mul operation + * @param a range of first symbol to be multiplied + * @param b range of second symbol to be multiplied + * @return resulting range representing the value range after multiplication + */ + public Range mul(final Range a, final Range b) { + if (a.isIntegerType() && b.isIntegerType()) { + final IntegerRange lhs = (IntegerRange)a; + final IntegerRange rhs = (IntegerRange)b; + + //ensure that nothing ever overflows or underflows + if (checkMul(lhs.getMin(), rhs.getMin()) && + checkMul(lhs.getMax(), rhs.getMax()) && + checkMul(lhs.getMin(), rhs.getMax()) && + checkMul(lhs.getMax(), rhs.getMin())) { + + final List<Long> results = + Arrays.asList( + lhs.getMin() * rhs.getMin(), + lhs.getMin() * rhs.getMax(), + lhs.getMax() * rhs.getMin(), + lhs.getMax() * rhs.getMax()); + return createIntegerRange(Collections.min(results), Collections.max(results)); + } + } + + if (a.getType().isNumeric() && b.getType().isNumeric()) { + return createNumberRange(); + } + + return createGenericRange(); + } + + /** + * Neg operation + * @param a range of value symbol to be negated + * @return resulting range representing the value range after neg + */ + public Range neg(final Range a) { + if (a.isIntegerType()) { + final IntegerRange rhs = (IntegerRange)a; + if (rhs.getMin() != Long.MIN_VALUE && rhs.getMax() != Long.MIN_VALUE) { + return createIntegerRange(-rhs.getMax(), -rhs.getMin()); + } + } + + if (a.getType().isNumeric()) { + return createNumberRange(); + } + + return createGenericRange(); + } + + /** + * Bitwise and operation + * @param a range of first symbol to be and:ed + * @param b range of second symbol to be and:ed + * @return resulting range representing the value range after and + */ + public Range and(final Range a, final Range b) { + if (a.isIntegerType() && b.isIntegerType()) { + final int resultMask = (int) (((IntegerRange)a).getBitMask() & ((IntegerRange)b).getBitMask()); + if (resultMask >= 0) { + return createIntegerRange(0, resultMask); + } + } else if (a.isUnknown() && b.isIntegerType()) { + final long operandMask = ((IntegerRange)b).getBitMask(); + if (operandMask >= 0) { + return createIntegerRange(0, operandMask); + } + } else if (a.isIntegerType() && b.isUnknown()) { + final long operandMask = ((IntegerRange)a).getBitMask(); + if (operandMask >= 0) { + return createIntegerRange(0, operandMask); + } + } + + return createTypeRange(Type.INT); + } + + /** + * Bitwise or operation + * @param a range of first symbol to be or:ed + * @param b range of second symbol to be or:ed + * @return resulting range representing the value range after or + */ + public Range or(final Range a, final Range b) { + if (a.isIntegerType() && b.isIntegerType()) { + final int resultMask = (int)(((IntegerRange)a).getBitMask() | ((IntegerRange)b).getBitMask()); + if (resultMask >= 0) { + return createIntegerRange(0, resultMask); + } + } + + return createTypeRange(Type.INT); + } + + /** + * Bitwise xor operation + * @param a range of first symbol to be xor:ed + * @param b range of second symbol to be xor:ed + * @return resulting range representing the value range after and + */ + public Range xor(final Range a, final Range b) { + if (a.isIntegerConst() && b.isIntegerConst()) { + return createRange(((IntegerRange)a).getMin() ^ ((IntegerRange)b).getMin()); + } + + if (a.isIntegerType() && b.isIntegerType()) { + final int resultMask = (int)(((IntegerRange)a).getBitMask() | ((IntegerRange)b).getBitMask()); + if (resultMask >= 0) { + return createIntegerRange(0, createIntegerRange(0, resultMask).getBitMask()); + } + } + return createTypeRange(Type.INT); + } + + /** + * Bitwise shl operation + * @param a range of first symbol to be shl:ed + * @param b range of second symbol to be shl:ed + * @return resulting range representing the value range after shl + */ + public Range shl(final Range a, final Range b) { + if (b.isIntegerType() && b.isIntegerConst()) { + final IntegerRange left = (IntegerRange)(a.isIntegerType() ? a : createTypeRange(Type.INT)); + final int shift = (int)((IntegerRange) b).getMin() & 0x1f; + final int min = (int)left.getMin() << shift; + final int max = (int)left.getMax() << shift; + if (min >> shift == left.getMin() && max >> shift == left.getMax()) { + return createIntegerRange(min, max); + } + } + + return createTypeRange(Type.INT); + } + + /** + * Bitwise shr operation + * @param a range of first symbol to be shr:ed + * @param b range of second symbol to be shr:ed + * @return resulting range representing the value range after shr + */ + public Range shr(final Range a, final Range b) { + if (b.isIntegerType() && b.isIntegerConst()) { + final long shift = ((IntegerRange) b).getMin() & 0x1f; + final IntegerRange left = (IntegerRange)(a.isIntegerType() ? a : createTypeRange(Type.INT)); + if (left.getMin() >= 0) { + long min = left.getMin() >>> shift; + long max = left.getMax() >>> shift; + return createIntegerRange(min, max); + } else if (shift >= 1) { + return createIntegerRange(0, JSType.MAX_UINT >>> shift); + } + } + + return createTypeRange(Type.INT); + } + + /** + * Bitwise sar operation + * @param a range of first symbol to be sar:ed + * @param b range of second symbol to be sar:ed + * @return resulting range representing the value range after sar + */ + public Range sar(final Range a, final Range b) { + if (b.isIntegerType() && b.isIntegerConst()) { + final IntegerRange left = (IntegerRange)(a.isIntegerType() ? a : createTypeRange(Type.INT)); + final long shift = ((IntegerRange) b).getMin() & 0x1f; + final long min = left.getMin() >> shift; + final long max = left.getMax() >> shift; + return createIntegerRange(min, max); + } + + return createTypeRange(Type.INT); + } + + /** + * Modulo operation + * @param a range of first symbol to the mod operation + * @param b range of second symbol to be mod operation + * @return resulting range representing the value range after mod + */ + public Range mod(final Range a, final Range b) { + if (a.isIntegerType() && b.isIntegerType()) { + final IntegerRange rhs = (IntegerRange) b; + if (rhs.getMin() > 0 || rhs.getMax() < 0) { // divisor range must not include 0 + final long absmax = Math.max(Math.abs(rhs.getMin()), Math.abs(rhs.getMax())) - 1; + return createIntegerRange(rhs.getMin() > 0 ? 0 : -absmax, rhs.getMax() < 0 ? 0 : +absmax); + } + } + return createTypeRange(Type.NUMBER); + } + + /** + * Division operation + * @param a range of first symbol to the division + * @param b range of second symbol to be division + * @return resulting range representing the value range after division + */ + public Range div(final Range a, final Range b) { + // TODO + return createTypeRange(Type.NUMBER); + } + } + + /** + * Simple trace functionality that will log range creation + */ + public static class TraceFunctionality extends Functionality { + TraceFunctionality(final DebugLogger log) { + super(log); + } + + private Range trace(final Range result, final String operation, final Range... operands) { + log.fine("range::" + operation + Arrays.toString(operands) + " => " + result); + return result; + } + + @Override + public Range join(final Range a, final Range b) { + final Range result = super.join(a, b); + if (!a.equals(b)) { + trace(result, "join", a, b); + } + return result; + } + + @Override + public Range add(final Range a, final Range b) { + return trace(super.add(a, b), "add", a, b); + } + + @Override + public Range sub(final Range a, final Range b) { + return trace(super.sub(a, b), "sub", a, b); + } + + @Override + public Range mul(final Range a, final Range b) { + return trace(super.mul(a, b), "mul", a, b); + } + + @Override + public Range neg(final Range a) { + return trace(super.neg(a), "neg", a); + } + + @Override + public Range and(final Range a, final Range b) { + return trace(super.and(a, b), "and", a, b); + } + + @Override + public Range or(final Range a, final Range b) { + return trace(super.or(a, b), "or", a, b); + } + + @Override + public Range xor(final Range a, final Range b) { + return trace(super.xor(a, b), "xor", a, b); + } + + @Override + public Range shl(final Range a, final Range b) { + return trace(super.shl(a, b), "shl", a, b); + } + + @Override + public Range shr(final Range a, final Range b) { + return trace(super.shr(a, b), "shr", a, b); + } + + @Override + public Range sar(final Range a, final Range b) { + return trace(super.sar(a, b), "sar", a, b); + } + + @Override + public Range mod(final Range a, final Range b) { + return trace(super.mod(a, b), "mod", a, b); + } + + @Override + public Range div(final Range a, final Range b) { + return trace(super.div(a, b), "div", a, b); + } + } + + @Override + public String toString() { + return String.valueOf(getType()); + } + + @SuppressWarnings("unused") + private static boolean isRepresentableAsInt(final double number) { + return (int)number == number && !isNegativeZero(number); + } + + private static boolean isRepresentableAsLong(final double number) { + return (long)number == number && !isNegativeZero(number); + } + + private static boolean isNegativeZero(final double number) { + return Double.doubleToLongBits(number) == Double.doubleToLongBits(-0.0); + } +}
--- a/src/jdk/nashorn/internal/codegen/types/Type.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/codegen/types/Type.java Mon Jun 03 23:24:36 2013 -0700 @@ -106,23 +106,13 @@ 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 */
--- a/src/jdk/nashorn/internal/ir/AccessNode.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/ir/AccessNode.java Mon Jun 03 23:24:36 2013 -0700 @@ -60,7 +60,7 @@ * @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 @@ return new AccessNode(this, base, property, isFunction(), hasCallSiteType()); } - private AccessNode setProperty(final IdentNode property) { if (this.property == property) { return this;
--- a/src/jdk/nashorn/internal/ir/BinaryNode.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/ir/BinaryNode.java Mon Jun 03 23:24:36 2013 -0700 @@ -59,6 +59,23 @@ 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 @@ * @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))); }
--- a/src/jdk/nashorn/internal/ir/Block.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/ir/Block.java Mon Jun 03 23:24:36 2013 -0700 @@ -131,7 +131,7 @@ * @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))); }
--- a/src/jdk/nashorn/internal/ir/BreakNode.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/ir/BreakNode.java Mon Jun 03 23:24:36 2013 -0700 @@ -59,7 +59,7 @@ * @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); }
--- a/src/jdk/nashorn/internal/ir/CallNode.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/ir/CallNode.java Mon Jun 03 23:24:36 2013 -0700 @@ -27,6 +27,7 @@ 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 @@ * @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)).
--- a/src/jdk/nashorn/internal/ir/CaseNode.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/ir/CaseNode.java Mon Jun 03 23:24:36 2013 -0700 @@ -78,7 +78,7 @@ * @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);
--- a/src/jdk/nashorn/internal/ir/CatchNode.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/ir/CatchNode.java Mon Jun 03 23:24:36 2013 -0700 @@ -42,6 +42,11 @@ /** 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 @@ * @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 @@ * @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 @@ 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 @@ 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; + } + }
--- a/src/jdk/nashorn/internal/ir/ContinueNode.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/ir/ContinueNode.java Mon Jun 03 23:24:36 2013 -0700 @@ -55,7 +55,7 @@ } @Override - public Node accept(final NodeVisitor visitor) { + public Node accept(final NodeVisitor<? extends LexicalContext> visitor) { if (visitor.enterContinueNode(this)) { return visitor.leaveContinueNode(this); }
--- a/src/jdk/nashorn/internal/ir/EmptyNode.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/ir/EmptyNode.java Mon Jun 03 23:24:36 2013 -0700 @@ -56,7 +56,7 @@ @Override - public Node accept(final NodeVisitor visitor) { + public Node accept(final NodeVisitor<? extends LexicalContext> visitor) { if (visitor.enterEmptyNode(this)) { return visitor.leaveEmptyNode(this); }
--- a/src/jdk/nashorn/internal/ir/ExecuteNode.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/ir/ExecuteNode.java Mon Jun 03 23:24:36 2013 -0700 @@ -62,7 +62,7 @@ } @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))); }
--- a/src/jdk/nashorn/internal/ir/ForNode.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/ir/ForNode.java Mon Jun 03 23:24:36 2013 -0700 @@ -86,7 +86,7 @@ } @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)).
--- a/src/jdk/nashorn/internal/ir/FunctionNode.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/ir/FunctionNode.java Mon Jun 03 23:24:36 2013 -0700 @@ -250,6 +250,7 @@ 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 @@ super(functionNode); this.flags = flags; + this.name = name; this.returnType = returnType; this.compileUnit = compileUnit; this.lastToken = lastToken; @@ -271,7 +273,6 @@ // 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 @@ } @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 @@ 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 @@ 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 @@ * @return true if specialization is possible */ public boolean canSpecialize() { - return getFlag(CAN_SPECIALIZE); + return snapshot != null && getFlag(CAN_SPECIALIZE); } /** @@ -389,7 +390,7 @@ } 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 @@ 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 @@ 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 @@ } /** - * 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ this, lastToken, flags, + name, Type.widest(this.returnType, returnType.isObject() ? Type.OBJECT : returnType), @@ -801,7 +817,7 @@ 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)); } /**
--- a/src/jdk/nashorn/internal/ir/IdentNode.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/ir/IdentNode.java Mon Jun 03 23:24:36 2013 -0700 @@ -29,7 +29,6 @@ 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 @@ * @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); }
--- a/src/jdk/nashorn/internal/ir/IfNode.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/ir/IfNode.java Mon Jun 03 23:24:36 2013 -0700 @@ -72,7 +72,7 @@ } @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)).
--- a/src/jdk/nashorn/internal/ir/IndexNode.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/ir/IndexNode.java Mon Jun 03 23:24:36 2013 -0700 @@ -56,19 +56,12 @@ } @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 @@ 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
--- a/src/jdk/nashorn/internal/ir/LabelNode.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/ir/LabelNode.java Mon Jun 03 23:24:36 2013 -0700 @@ -67,11 +67,11 @@ } @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;
--- a/src/jdk/nashorn/internal/ir/LexicalContext.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/ir/LexicalContext.java Mon Jun 03 23:24:36 2013 -0700 @@ -440,6 +440,23 @@ } /** + * 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 * @return closest breakable node @@ -461,8 +478,7 @@ } private LoopNode getContinueTo() { - final Iterator<LoopNode> iter = new NodeIterator<>(LoopNode.class, getCurrentFunction()); - return iter.hasNext() ? iter.next() : null; + return getCurrentLoop(); } /**
--- a/src/jdk/nashorn/internal/ir/LexicalContextNode.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/ir/LexicalContextNode.java Mon Jun 03 23:24:36 2013 -0700 @@ -60,10 +60,10 @@ * * @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);
--- a/src/jdk/nashorn/internal/ir/LiteralNode.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/ir/LiteralNode.java Mon Jun 03 23:24:36 2013 -0700 @@ -28,6 +28,7 @@ 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 @@ * @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 @@ } @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 @@ } @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);
--- a/src/jdk/nashorn/internal/ir/Node.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/ir/Node.java Mon Jun 03 23:24:36 2013 -0700 @@ -27,6 +27,7 @@ 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; @@ -153,6 +154,14 @@ } /** + * 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 * @param lc lexical context @@ -167,7 +176,7 @@ * @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 @@ } //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<>();
--- a/src/jdk/nashorn/internal/ir/ObjectNode.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/ir/ObjectNode.java Mon Jun 03 23:24:36 2013 -0700 @@ -58,7 +58,7 @@ } @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))); }
--- a/src/jdk/nashorn/internal/ir/PropertyNode.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/ir/PropertyNode.java Mon Jun 03 23:24:36 2013 -0700 @@ -81,7 +81,7 @@ } @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)).
--- a/src/jdk/nashorn/internal/ir/ReturnNode.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/ir/ReturnNode.java Mon Jun 03 23:24:36 2013 -0700 @@ -86,7 +86,7 @@ } @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)));
--- a/src/jdk/nashorn/internal/ir/RuntimeNode.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/ir/RuntimeNode.java Mon Jun 03 23:24:36 2013 -0700 @@ -29,6 +29,7 @@ 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 @@ } @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) {
--- a/src/jdk/nashorn/internal/ir/SplitNode.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/ir/SplitNode.java Mon Jun 03 23:24:36 2013 -0700 @@ -81,7 +81,7 @@ } @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))); }
--- a/src/jdk/nashorn/internal/ir/SwitchNode.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/ir/SwitchNode.java Mon Jun 03 23:24:36 2013 -0700 @@ -100,11 +100,11 @@ } @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;
--- a/src/jdk/nashorn/internal/ir/Symbol.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/ir/Symbol.java Mon Jun 03 23:24:36 2013 -0700 @@ -29,6 +29,8 @@ import java.util.HashSet; import java.util.Set; import java.util.StringTokenizer; + +import jdk.nashorn.internal.codegen.types.Range; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.Debug; @@ -89,6 +91,9 @@ /** Number of times this symbol is used in code */ private int useCount; + /** Range for symbol */ + private Range range; + /** Debugging option - dump info and stack trace when symbols with given names are manipulated */ private static final Set<String> TRACE_SYMBOLS; private static final Set<String> TRACE_SYMBOLS_STACKTRACE; @@ -131,6 +136,7 @@ this.type = type; this.slot = slot; this.fieldIndex = -1; + this.range = Range.createUnknownRange(); trace("CREATE SYMBOL"); } @@ -157,12 +163,13 @@ private Symbol(final Symbol base, final String name, final int flags) { this.flags = flags; - this.name = name; + this.name = name; this.fieldIndex = base.fieldIndex; - this.slot = base.slot; - this.type = base.type; - this.useCount = base.useCount; + this.slot = base.slot; + this.type = base.type; + this.useCount = base.useCount; + this.range = base.range; } private static String align(final String string, final int max) { @@ -276,7 +283,7 @@ @Override public String toString() { - final StringBuilder sb = new StringBuilder(); + final StringBuilder sb = new StringBuilder(); sb.append(name). append(' '). @@ -410,6 +417,22 @@ } /** + * Get the range for this symbol + * @return range for symbol + */ + public Range getRange() { + return range; + } + + /** + * Set the range for this symbol + * @param range range + */ + public void setRange(final Range range) { + this.range = range; + } + + /** * Check if this symbol is a function parameter of known * narrowest type * @return true if parameter
--- a/src/jdk/nashorn/internal/ir/TernaryNode.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/ir/TernaryNode.java Mon Jun 03 23:24:36 2013 -0700 @@ -63,7 +63,7 @@ } @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);
--- a/src/jdk/nashorn/internal/ir/ThrowNode.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/ir/ThrowNode.java Mon Jun 03 23:24:36 2013 -0700 @@ -36,6 +36,11 @@ /** Exception expression. */ private final Node expression; + private final int flags; + + /** Is this block a synthethic rethrow created by finally inlining? */ + public static final int IS_SYNTHETIC_RETHROW = 1; + /** * Constructor * @@ -43,15 +48,18 @@ * @param token token * @param finish finish * @param expression expression to throw + * @param flags flags */ - public ThrowNode(final int lineNumber, final long token, final int finish, final Node expression) { + public ThrowNode(final int lineNumber, final long token, final int finish, final Node expression, final int flags) { super(lineNumber, token, finish); this.expression = expression; + this.flags = flags; } - private ThrowNode(final ThrowNode node, final Node expression) { + private ThrowNode(final ThrowNode node, final Node expression, final int flags) { super(node); this.expression = expression; + this.flags = flags; } @Override @@ -64,7 +72,7 @@ * @param visitor IR navigating visitor. */ @Override - public Node accept(final NodeVisitor visitor) { + public Node accept(final NodeVisitor<? extends LexicalContext> visitor) { if (visitor.enterThrowNode(this)) { return visitor.leaveThrowNode(setExpression(expression.accept(visitor))); } @@ -98,7 +106,17 @@ if (this.expression == expression) { return this; } - return new ThrowNode(this, expression); + return new ThrowNode(this, expression, flags); + } + + /** + * Is this a throw a synthetic rethrow in a synthetic catch-all block + * created when inlining finally statements? In that case we never + * wrap whatever is thrown into an ECMAException, just rethrow it. + * @return true if synthetic throw node + */ + public boolean isSyntheticRethrow() { + return (flags & IS_SYNTHETIC_RETHROW) == IS_SYNTHETIC_RETHROW; } }
--- a/src/jdk/nashorn/internal/ir/TryNode.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/ir/TryNode.java Mon Jun 03 23:24:36 2013 -0700 @@ -106,7 +106,7 @@ * @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);
--- a/src/jdk/nashorn/internal/ir/UnaryNode.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/ir/UnaryNode.java Mon Jun 03 23:24:36 2013 -0700 @@ -29,7 +29,6 @@ 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 @@ * @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))); }
--- a/src/jdk/nashorn/internal/ir/VarNode.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/ir/VarNode.java Mon Jun 03 23:24:36 2013 -0700 @@ -121,7 +121,7 @@ * @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);
--- a/src/jdk/nashorn/internal/ir/WhileNode.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/ir/WhileNode.java Mon Jun 03 23:24:36 2013 -0700 @@ -75,7 +75,7 @@ } @Override - protected Node accept(final LexicalContext lc, final NodeVisitor visitor) { + protected Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) { if (visitor.enterWhileNode(this)) { if (isDoWhile()) { return visitor.leaveWhileNode(
--- a/src/jdk/nashorn/internal/ir/WithNode.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/ir/WithNode.java Mon Jun 03 23:24:36 2013 -0700 @@ -64,7 +64,7 @@ * @param visitor IR navigating visitor. */ @Override - public Node accept(final LexicalContext lc, final NodeVisitor visitor) { + public Node accept(final LexicalContext lc, final NodeVisitor<? extends LexicalContext> visitor) { if (visitor.enterWithNode(this)) { return visitor.leaveWithNode( setExpression(lc, expression.accept(visitor)).
--- a/src/jdk/nashorn/internal/ir/debug/JSONWriter.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/ir/debug/JSONWriter.java Mon Jun 03 23:24:36 2013 -0700 @@ -45,6 +45,7 @@ import jdk.nashorn.internal.ir.IfNode; import jdk.nashorn.internal.ir.IndexNode; import jdk.nashorn.internal.ir.LabelNode; +import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.ObjectNode; @@ -74,7 +75,8 @@ /** * This IR writer produces a JSON string that represents AST as a JSON string. */ -public final class JSONWriter extends NodeVisitor { +public final class JSONWriter extends NodeVisitor<LexicalContext> { + /** * Returns AST as JSON compatible string. * @@ -867,7 +869,8 @@ // Internals below private JSONWriter(final boolean includeLocation) { - this.buf = new StringBuilder(); + super(new LexicalContext()); + this.buf = new StringBuilder(); this.includeLocation = includeLocation; } @@ -963,7 +966,7 @@ objectStart("loc"); // source name - final Source src = getLexicalContext().getCurrentFunction().getSource(); + final Source src = lc.getCurrentFunction().getSource(); property("source", src.getName()); comma();
--- a/src/jdk/nashorn/internal/ir/debug/PrintVisitor.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/ir/debug/PrintVisitor.java Mon Jun 03 23:24:36 2013 -0700 @@ -36,6 +36,7 @@ import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.IfNode; import jdk.nashorn.internal.ir.LabelNode; +import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.SplitNode; import jdk.nashorn.internal.ir.Statement; @@ -53,7 +54,7 @@ * * see the flags --print-parse and --print-lower-parse */ -public final class PrintVisitor extends NodeVisitor { +public final class PrintVisitor extends NodeVisitor<LexicalContext> { /** Tab width */ private static final int TABWIDTH = 4; @@ -84,6 +85,7 @@ * @param printLineNumbers should line number nodes be included in the output? */ public PrintVisitor(final boolean printLineNumbers) { + super(new LexicalContext()); this.EOLN = System.lineSeparator(); this.sb = new StringBuilder(); this.printLineNumbers = printLineNumbers;
--- a/src/jdk/nashorn/internal/ir/visitor/NodeOperatorVisitor.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/ir/visitor/NodeOperatorVisitor.java Mon Jun 03 23:24:36 2013 -0700 @@ -32,21 +32,15 @@ /** * Like NodeVisitor but navigating further into operators. + * @param <T> Lexical context class for this NodeOperatorVisitor */ -public class NodeOperatorVisitor extends NodeVisitor { - /** - * Constructor - */ - public NodeOperatorVisitor() { - super(); - } - +public class NodeOperatorVisitor<T extends LexicalContext> extends NodeVisitor<T> { /** * Constructor * * @param lc a custom lexical context */ - public NodeOperatorVisitor(final LexicalContext lc) { + public NodeOperatorVisitor(final T lc) { super(lc); }
--- a/src/jdk/nashorn/internal/ir/visitor/NodeVisitor.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/ir/visitor/NodeVisitor.java Mon Jun 03 23:24:36 2013 -0700 @@ -60,23 +60,18 @@ /** * Visitor used to navigate the IR. + * @param <T> lexical context class used by this visitor */ -public abstract class NodeVisitor { - private final LexicalContext lc; - - /** - * Constructor - */ - public NodeVisitor() { - this(new LexicalContext()); - } +public abstract class NodeVisitor<T extends LexicalContext> { + /** lexical context in use */ + protected final T lc; /** * Constructor * * @param lc a custom lexical context */ - public NodeVisitor(final LexicalContext lc) { + public NodeVisitor(final T lc) { this.lc = lc; } @@ -84,7 +79,7 @@ * Get the lexical context of this node visitor * @return lexical context */ - public LexicalContext getLexicalContext() { + public T getLexicalContext() { return lc; }
--- a/src/jdk/nashorn/internal/objects/ArrayBufferView.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/objects/ArrayBufferView.java Mon Jun 03 23:24:36 2013 -0700 @@ -59,11 +59,6 @@ } @Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE) - public static Object BYTES_PER_ELEMENT(final Object self) { - return ((ArrayBufferView)self).bytesPerElement(); - } - - @Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE) public static Object buffer(final Object self) { return ((ArrayDataImpl)((ArrayBufferView)self).getArray()).buffer; }
--- a/src/jdk/nashorn/internal/objects/DateParser.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/objects/DateParser.java Mon Jun 03 23:24:36 2013 -0700 @@ -32,6 +32,7 @@ import static java.lang.Character.UPPERCASE_LETTER; import java.util.HashMap; +import java.util.Locale; /** * JavaScript date parser. This class first tries to parse a date string @@ -486,7 +487,7 @@ while (pos < limit && isAsciiLetter(string.charAt(pos))) { pos++; } - final String key = string.substring(start, pos).toLowerCase(); + final String key = string.substring(start, pos).toLowerCase(Locale.ENGLISH); final Name name = names.get(key); // then advance to end of name while (pos < length && isAsciiLetter(string.charAt(pos))) { @@ -683,7 +684,7 @@ Name(final String name, final int type, final int value) { assert name != null; - assert name.equals(name.toLowerCase()); + assert name.equals(name.toLowerCase(Locale.ENGLISH)); this.name = name; // use first three characters as lookup key
--- a/src/jdk/nashorn/internal/objects/NativeArguments.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/objects/NativeArguments.java Mon Jun 03 23:24:36 2013 -0700 @@ -603,6 +603,11 @@ } } + @Override + public Object getLength() { + return length; + } + private Object getArgumentsLength() { return length; }
--- a/src/jdk/nashorn/internal/objects/NativeArray.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/objects/NativeArray.java Mon Jun 03 23:24:36 2013 -0700 @@ -754,25 +754,11 @@ final Object obj = Global.toObject(self); final ScriptObject sobj = (ScriptObject)obj; final long len = JSType.toUint32(sobj.getLength()); - final double startNum = JSType.toNumber(start); - final long relativeStartUint32 = JSType.toUint32(startNum); - final long relativeStart = JSType.toInteger(startNum); - - long k = relativeStart < 0 ? - Math.max(len + relativeStart, 0) : - Math.min( - Math.max(relativeStartUint32, relativeStart), - len); + final long relativeStart = JSType.toLong(start); + final long relativeEnd = (end == ScriptRuntime.UNDEFINED) ? len : JSType.toLong(end); - final double endNum = (end == ScriptRuntime.UNDEFINED)? Double.NaN : JSType.toNumber(end); - final long relativeEndUint32 = (end == ScriptRuntime.UNDEFINED)? len : JSType.toUint32(endNum); - final long relativeEnd = (end == ScriptRuntime.UNDEFINED)? len : JSType.toInteger(endNum); - - final long finale = relativeEnd < 0 ? - Math.max(len + relativeEnd, 0) : - Math.min( - Math.max(relativeEndUint32, relativeEnd), - len); + long k = relativeStart < 0 ? Math.max(len + relativeStart, 0) : Math.min(relativeStart, len); + final long finale = relativeEnd < 0 ? Math.max(len + relativeEnd, 0) : Math.min(relativeEnd, len); if (k >= finale) { return new NativeArray(0); @@ -909,21 +895,10 @@ final ScriptObject sobj = (ScriptObject)obj; final boolean strict = Global.isStrict(); final long len = JSType.toUint32(sobj.getLength()); - final double startNum = JSType.toNumber(start); - final long relativeStartUint32 = JSType.toUint32(startNum); - final long relativeStart = JSType.toInteger(startNum); + final long relativeStart = JSType.toLong(start); - //TODO: workaround overflow of relativeStart for start > Integer.MAX_VALUE - final long actualStart = relativeStart < 0 ? - Math.max(len + relativeStart, 0) : - Math.min( - Math.max(relativeStartUint32, relativeStart), - len); - - final long actualDeleteCount = - Math.min( - Math.max(JSType.toInteger(deleteCount), 0), - len - actualStart); + final long actualStart = relativeStart < 0 ? Math.max(len + relativeStart, 0) : Math.min(relativeStart, len); + final long actualDeleteCount = Math.min(Math.max(JSType.toLong(deleteCount), 0), len - actualStart); final NativeArray array = new NativeArray(actualDeleteCount);
--- a/src/jdk/nashorn/internal/objects/NativeDate.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/objects/NativeDate.java Mon Jun 03 23:24:36 2013 -0700 @@ -770,7 +770,7 @@ nd.setTime(NaN); return nd.getTime(); } - int yearInt = JSType.toInteger(yearNum); + int yearInt = (int)yearNum; if (0 <= yearInt && yearInt <= 99) { yearInt += 1900; }
--- a/src/jdk/nashorn/internal/objects/NativeFloat32Array.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/objects/NativeFloat32Array.java Mon Jun 03 23:24:36 2013 -0700 @@ -28,7 +28,9 @@ import jdk.nashorn.internal.objects.annotations.Attribute; import jdk.nashorn.internal.objects.annotations.Constructor; import jdk.nashorn.internal.objects.annotations.Function; +import jdk.nashorn.internal.objects.annotations.Property; import jdk.nashorn.internal.objects.annotations.ScriptClass; +import jdk.nashorn.internal.objects.annotations.Where; import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.arrays.ArrayData; @@ -38,7 +40,12 @@ */ @ScriptClass("Float32Array") public final class NativeFloat32Array extends ArrayBufferView { - private static final int BYTES_PER_ELEMENT = 4; + /** + * The size in bytes of each element in the array. + */ + @Property(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE, where = Where.CONSTRUCTOR) + public static final int BYTES_PER_ELEMENT = 4; + private static final Factory FACTORY = new Factory(BYTES_PER_ELEMENT) { @Override public ArrayBufferView construct(final NativeArrayBuffer buffer, final int byteOffset, final int length) {
--- a/src/jdk/nashorn/internal/objects/NativeFloat64Array.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/objects/NativeFloat64Array.java Mon Jun 03 23:24:36 2013 -0700 @@ -28,7 +28,9 @@ import jdk.nashorn.internal.objects.annotations.Attribute; import jdk.nashorn.internal.objects.annotations.Constructor; import jdk.nashorn.internal.objects.annotations.Function; +import jdk.nashorn.internal.objects.annotations.Property; import jdk.nashorn.internal.objects.annotations.ScriptClass; +import jdk.nashorn.internal.objects.annotations.Where; import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.arrays.ArrayData; @@ -38,7 +40,12 @@ */ @ScriptClass("Float64Array") public final class NativeFloat64Array extends ArrayBufferView { - private static final int BYTES_PER_ELEMENT = 8; + /** + * The size in bytes of each element in the array. + */ + @Property(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE, where = Where.CONSTRUCTOR) + public static final int BYTES_PER_ELEMENT = 8; + private static final Factory FACTORY = new Factory(BYTES_PER_ELEMENT) { @Override public ArrayBufferView construct(final NativeArrayBuffer buffer, final int byteOffset, final int length) {
--- a/src/jdk/nashorn/internal/objects/NativeInt16Array.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/objects/NativeInt16Array.java Mon Jun 03 23:24:36 2013 -0700 @@ -28,7 +28,9 @@ import jdk.nashorn.internal.objects.annotations.Attribute; import jdk.nashorn.internal.objects.annotations.Constructor; import jdk.nashorn.internal.objects.annotations.Function; +import jdk.nashorn.internal.objects.annotations.Property; import jdk.nashorn.internal.objects.annotations.ScriptClass; +import jdk.nashorn.internal.objects.annotations.Where; import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.arrays.ArrayData; @@ -37,7 +39,12 @@ */ @ScriptClass("Int16Array") public final class NativeInt16Array extends ArrayBufferView { - private static final int BYTES_PER_ELEMENT = 2; + /** + * The size in bytes of each element in the array. + */ + @Property(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE, where = Where.CONSTRUCTOR) + public static final int BYTES_PER_ELEMENT = 2; + private static final Factory FACTORY = new Factory(BYTES_PER_ELEMENT) { @Override public ArrayBufferView construct(final NativeArrayBuffer buffer, final int byteOffset, final int length) {
--- a/src/jdk/nashorn/internal/objects/NativeInt32Array.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/objects/NativeInt32Array.java Mon Jun 03 23:24:36 2013 -0700 @@ -28,7 +28,9 @@ import jdk.nashorn.internal.objects.annotations.Attribute; import jdk.nashorn.internal.objects.annotations.Constructor; import jdk.nashorn.internal.objects.annotations.Function; +import jdk.nashorn.internal.objects.annotations.Property; import jdk.nashorn.internal.objects.annotations.ScriptClass; +import jdk.nashorn.internal.objects.annotations.Where; import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.arrays.ArrayData; @@ -37,7 +39,12 @@ */ @ScriptClass("Int32Array") public final class NativeInt32Array extends ArrayBufferView { - private static final int BYTES_PER_ELEMENT = 4; + /** + * The size in bytes of each element in the array. + */ + @Property(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE, where = Where.CONSTRUCTOR) + public static final int BYTES_PER_ELEMENT = 4; + private static final Factory FACTORY = new Factory(BYTES_PER_ELEMENT) { @Override public ArrayBufferView construct(final NativeArrayBuffer buffer, final int byteOffset, final int length) {
--- a/src/jdk/nashorn/internal/objects/NativeInt8Array.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/objects/NativeInt8Array.java Mon Jun 03 23:24:36 2013 -0700 @@ -28,7 +28,9 @@ import jdk.nashorn.internal.objects.annotations.Attribute; import jdk.nashorn.internal.objects.annotations.Constructor; import jdk.nashorn.internal.objects.annotations.Function; +import jdk.nashorn.internal.objects.annotations.Property; import jdk.nashorn.internal.objects.annotations.ScriptClass; +import jdk.nashorn.internal.objects.annotations.Where; import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.arrays.ArrayData; @@ -37,7 +39,12 @@ */ @ScriptClass("Int8Array") public final class NativeInt8Array extends ArrayBufferView { - private static final int BYTES_PER_ELEMENT = 1; + /** + * The size in bytes of each element in the array. + */ + @Property(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE, where = Where.CONSTRUCTOR) + public static final int BYTES_PER_ELEMENT = 1; + private static final Factory FACTORY = new Factory(BYTES_PER_ELEMENT) { @Override public ArrayBufferView construct(final NativeArrayBuffer buffer, final int byteOffset, final int length) {
--- a/src/jdk/nashorn/internal/objects/NativeJava.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/objects/NativeJava.java Mon Jun 03 23:24:36 2013 -0700 @@ -30,6 +30,8 @@ import java.lang.reflect.Array; import java.util.Collection; +import java.util.Deque; +import java.util.List; import jdk.internal.dynalink.beans.StaticClass; import jdk.internal.dynalink.support.TypeUtilities; import jdk.nashorn.internal.objects.annotations.Attribute; @@ -37,6 +39,7 @@ import jdk.nashorn.internal.objects.annotations.ScriptClass; import jdk.nashorn.internal.objects.annotations.Where; import jdk.nashorn.internal.runtime.JSType; +import jdk.nashorn.internal.runtime.ListAdapter; import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.linker.JavaAdapterFactory; @@ -240,39 +243,56 @@ } /** - * Given a JavaScript array and a Java type, returns a Java array with the same initial contents, and with the - * specified component type. Example: + * Given a script object and a Java type, converts the script object into the desired Java type. Currently it + * performs shallow creation of Java arrays, as well as wrapping of objects in Lists and Dequeues. Example: * <pre> * var anArray = [1, "13", false] - * var javaIntArray = Java.toJavaArray(anArray, "int") + * var javaIntArray = Java.to(anArray, "int[]") * print(javaIntArray[0]) // prints 1 * print(javaIntArray[1]) // prints 13, as string "13" was converted to number 13 as per ECMAScript ToNumber conversion * print(javaIntArray[2]) // prints 0, as boolean false was converted to number 0 as per ECMAScript ToNumber conversion * </pre> * @param self not used - * @param objArray the JavaScript array. Can be null. - * @param objType either a {@link #type(Object, Object) type object} or a String describing the component type of - * the Java array to create. Can not be null. If undefined, Object is assumed (allowing the argument to be omitted). - * @return a Java array with the copy of JavaScript array's contents, converted to the appropriate Java component - * type. Returns null if objArray is null. + * @param obj the script object. Can be null. + * @param objType either a {@link #type(Object, Object) type object} or a String describing the type of the Java + * object to create. Can not be null. If undefined, a "default" conversion is presumed (allowing the argument to be + * omitted). + * @return a Java object whose value corresponds to the original script object's value. Specifically, for array + * target types, returns a Java array of the same type with contents converted to the array's component type. Does + * not recursively convert for multidimensional arrays. For {@link List} or {@link Deque}, returns a live wrapper + * around the object, see {@link ListAdapter} for details. Returns null if obj is null. * @throws ClassNotFoundException if the class described by objType is not found */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) - public static Object toJavaArray(final Object self, final Object objArray, final Object objType) throws ClassNotFoundException { - final StaticClass componentType = - objType instanceof StaticClass ? - (StaticClass)objType : - objType == UNDEFINED ? - StaticClass.forClass(Object.class) : - type(objType); - - if (objArray == null) { + public static Object to(final Object self, final Object obj, final Object objType) throws ClassNotFoundException { + if (obj == null) { return null; } - Global.checkObject(objArray); + Global.checkObject(obj); - return ((ScriptObject)objArray).getArray().asArrayOfType(componentType.getRepresentedClass()); + final Class<?> targetClass; + if(objType == UNDEFINED) { + targetClass = Object[].class; + } else { + final StaticClass targetType; + if(objType instanceof StaticClass) { + targetType = (StaticClass)objType; + } else { + targetType = type(objType); + } + targetClass = targetType.getRepresentedClass(); + } + + if(targetClass.isArray()) { + return ((ScriptObject)obj).getArray().asArrayOfType(targetClass.getComponentType()); + } + + if(targetClass == List.class || targetClass == Deque.class) { + return new ListAdapter((ScriptObject)obj); + } + + throw typeError("unsupported.java.to.type", targetClass.getName()); } /** @@ -283,7 +303,7 @@ * <pre> * var File = Java.type("java.io.File") * var listHomeDir = new File("~").listFiles() - * var jsListHome = Java.toJavaScriptArray(listHomeDir) + * var jsListHome = Java.from(listHomeDir) * var jpegModifiedDates = jsListHome * .filter(function(val) { return val.getName().endsWith(".jpg") }) * .map(function(val) { return val.lastModified() }) @@ -294,7 +314,7 @@ * null. */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) - public static Object toJavaScriptArray(final Object self, final Object objArray) { + public static Object from(final Object self, final Object objArray) { if (objArray == null) { return null; } else if (objArray instanceof Collection) {
--- a/src/jdk/nashorn/internal/objects/NativeMath.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/objects/NativeMath.java Mon Jun 03 23:24:36 2013 -0700 @@ -611,13 +611,11 @@ */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) public static Object round(final Object self, final Object x) { - if (GlobalFunctions.isNaN(self, x)) { - return Double.NaN; - } else if (!GlobalFunctions.isFinite(self, x)) { - return x; + final double d = JSType.toNumber(x); + if (Math.getExponent(d) >= 52) { + return d; } - - return Math.round(JSType.toNumber(x)); + return Math.copySign(Math.floor(d + 0.5), d); } /**
--- a/src/jdk/nashorn/internal/objects/NativeString.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/objects/NativeString.java Mon Jun 03 23:24:36 2013 -0700 @@ -38,6 +38,7 @@ import java.util.Arrays; import java.util.LinkedList; import java.util.List; +import java.util.Locale; import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.linker.GuardedInvocation; import jdk.internal.dynalink.linker.LinkRequest; @@ -630,17 +631,24 @@ final String str = checkObjectToString(self); final String searchStr = JSType.toString(search); + final int length = str.length(); - int from; + int end; if (pos == UNDEFINED) { - from = str.length(); + end = length; } else { final double numPos = JSType.toNumber(pos); - from = !Double.isNaN(numPos) ? (int)numPos : (int)Double.POSITIVE_INFINITY; + end = Double.isNaN(numPos) ? length : (int)numPos; + if (end < 0) { + end = 0; + } else if (end > length) { + end = length; + } } - return str.lastIndexOf(searchStr, from); + + return str.lastIndexOf(searchStr, end); } /** @@ -997,7 +1005,7 @@ */ @Function(attributes = Attribute.NOT_ENUMERABLE) public static Object toLowerCase(final Object self) { - return checkObjectToString(self).toLowerCase(); + return checkObjectToString(self).toLowerCase(Locale.ROOT); } /** @@ -1017,7 +1025,7 @@ */ @Function(attributes = Attribute.NOT_ENUMERABLE) public static Object toUpperCase(final Object self) { - return checkObjectToString(self).toUpperCase(); + return checkObjectToString(self).toUpperCase(Locale.ROOT); } /**
--- a/src/jdk/nashorn/internal/objects/NativeUint16Array.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/objects/NativeUint16Array.java Mon Jun 03 23:24:36 2013 -0700 @@ -28,7 +28,9 @@ import jdk.nashorn.internal.objects.annotations.Attribute; import jdk.nashorn.internal.objects.annotations.Constructor; import jdk.nashorn.internal.objects.annotations.Function; +import jdk.nashorn.internal.objects.annotations.Property; import jdk.nashorn.internal.objects.annotations.ScriptClass; +import jdk.nashorn.internal.objects.annotations.Where; import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.arrays.ArrayData; @@ -37,7 +39,12 @@ */ @ScriptClass("Uint16Array") public final class NativeUint16Array extends ArrayBufferView { - private static final int BYTES_PER_ELEMENT = 2; + /** + * The size in bytes of each element in the array. + */ + @Property(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE, where = Where.CONSTRUCTOR) + public static final int BYTES_PER_ELEMENT = 2; + private static final Factory FACTORY = new Factory(BYTES_PER_ELEMENT) { @Override public ArrayBufferView construct(final NativeArrayBuffer buffer, final int byteOffset, final int length) {
--- a/src/jdk/nashorn/internal/objects/NativeUint32Array.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/objects/NativeUint32Array.java Mon Jun 03 23:24:36 2013 -0700 @@ -28,7 +28,9 @@ import jdk.nashorn.internal.objects.annotations.Attribute; import jdk.nashorn.internal.objects.annotations.Constructor; import jdk.nashorn.internal.objects.annotations.Function; +import jdk.nashorn.internal.objects.annotations.Property; import jdk.nashorn.internal.objects.annotations.ScriptClass; +import jdk.nashorn.internal.objects.annotations.Where; import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.arrays.ArrayData; @@ -38,7 +40,12 @@ */ @ScriptClass("Uint32Array") public final class NativeUint32Array extends ArrayBufferView { - private static final int BYTES_PER_ELEMENT = 4; + /** + * The size in bytes of each element in the array. + */ + @Property(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE, where = Where.CONSTRUCTOR) + public static final int BYTES_PER_ELEMENT = 4; + private static final Factory FACTORY = new Factory(BYTES_PER_ELEMENT) { @Override public ArrayBufferView construct(final NativeArrayBuffer buffer, final int byteBegin, final int length) {
--- a/src/jdk/nashorn/internal/objects/NativeUint8Array.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/objects/NativeUint8Array.java Mon Jun 03 23:24:36 2013 -0700 @@ -28,7 +28,9 @@ import jdk.nashorn.internal.objects.annotations.Attribute; import jdk.nashorn.internal.objects.annotations.Constructor; import jdk.nashorn.internal.objects.annotations.Function; +import jdk.nashorn.internal.objects.annotations.Property; import jdk.nashorn.internal.objects.annotations.ScriptClass; +import jdk.nashorn.internal.objects.annotations.Where; import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.arrays.ArrayData; @@ -37,7 +39,12 @@ */ @ScriptClass("Uint8Array") public final class NativeUint8Array extends ArrayBufferView { - private static final int BYTES_PER_ELEMENT = 1; + /** + * The size in bytes of each element in the array. + */ + @Property(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE, where = Where.CONSTRUCTOR) + public static final int BYTES_PER_ELEMENT = 1; + private static final Factory FACTORY = new Factory(BYTES_PER_ELEMENT) { @Override public ArrayBufferView construct(final NativeArrayBuffer buffer, final int byteOffset, final int length) {
--- a/src/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java Mon Jun 03 23:24:36 2013 -0700 @@ -28,7 +28,9 @@ import jdk.nashorn.internal.objects.annotations.Attribute; import jdk.nashorn.internal.objects.annotations.Constructor; import jdk.nashorn.internal.objects.annotations.Function; +import jdk.nashorn.internal.objects.annotations.Property; import jdk.nashorn.internal.objects.annotations.ScriptClass; +import jdk.nashorn.internal.objects.annotations.Where; import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.arrays.ArrayData; @@ -38,7 +40,12 @@ */ @ScriptClass("Uint8ClampedArray") public final class NativeUint8ClampedArray extends ArrayBufferView { - private static final int BYTES_PER_ELEMENT = 1; + /** + * The size in bytes of each element in the array. + */ + @Property(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE, where = Where.CONSTRUCTOR) + public static final int BYTES_PER_ELEMENT = 1; + private static final Factory FACTORY = new Factory(BYTES_PER_ELEMENT) { @Override public ArrayBufferView construct(final NativeArrayBuffer buffer, final int byteOffset, final int length) {
--- a/src/jdk/nashorn/internal/parser/Parser.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/parser/Parser.java Mon Jun 03 23:24:36 2013 -0700 @@ -1537,7 +1537,7 @@ endOfLine(); - appendStatement(new ThrowNode(throwLine, throwToken, finish, expression)); + appendStatement(new ThrowNode(throwLine, throwToken, finish, expression, 0)); } /** @@ -1597,7 +1597,7 @@ try { // Get CATCH body. final Block catchBody = getBlock(true); - final CatchNode catchNode = new CatchNode(catchLine, catchToken, finish, exception, ifExpression, catchBody); + final CatchNode catchNode = new CatchNode(catchLine, catchToken, finish, exception, ifExpression, catchBody, 0); appendStatement(catchNode); } finally { catchBlock = restoreBlock(catchBlock);
--- a/src/jdk/nashorn/internal/parser/TokenType.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/parser/TokenType.java Mon Jun 03 23:24:36 2013 -0700 @@ -25,6 +25,7 @@ package jdk.nashorn.internal.parser; +import java.util.Locale; import static jdk.nashorn.internal.parser.TokenKind.BINARY; import static jdk.nashorn.internal.parser.TokenKind.BRACKET; import static jdk.nashorn.internal.parser.TokenKind.FUTURE; @@ -249,7 +250,7 @@ } public String getNameOrType() { - return name == null ? super.name().toLowerCase() : name; + return name == null ? super.name().toLowerCase(Locale.ENGLISH) : name; } public TokenType getNext() {
--- a/src/jdk/nashorn/internal/runtime/AccessorProperty.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/runtime/AccessorProperty.java Mon Jun 03 23:24:36 2013 -0700 @@ -75,7 +75,23 @@ private static final MethodType[] ACCESSOR_GETTER_TYPES = new MethodType[NOOF_TYPES]; private static final MethodType[] ACCESSOR_SETTER_TYPES = new MethodType[NOOF_TYPES]; - private static final MethodHandle SPILLGETTER = MH.asType(MH.getter(MethodHandles.lookup(), ScriptObject.class, "spill", Object[].class), Lookup.GET_OBJECT_TYPE); + private static final MethodHandle SPILL_ELEMENT_GETTER; + private static final MethodHandle SPILL_ELEMENT_SETTER; + + private static final int SPILL_CACHE_SIZE = 8; + private static final MethodHandle[] SPILL_ACCESSORS = new MethodHandle[SPILL_CACHE_SIZE * 2]; + + static { + for (int i = 0; i < NOOF_TYPES; i++) { + final Type type = ACCESSOR_TYPES.get(i); + ACCESSOR_GETTER_TYPES[i] = MH.type(type.getTypeClass(), Object.class); + ACCESSOR_SETTER_TYPES[i] = MH.type(void.class, Object.class, type.getTypeClass()); + } + + final MethodHandle spillGetter = MH.getter(MethodHandles.lookup(), ScriptObject.class, "spill", Object[].class); + SPILL_ELEMENT_GETTER = MH.filterArguments(MH.arrayElementGetter(Object[].class), 0, spillGetter); + SPILL_ELEMENT_SETTER = MH.filterArguments(MH.arrayElementSetter(Object[].class), 0, spillGetter); + } /** Seed getter for the primitive version of this field (in -Dnashorn.fields.dual=true mode) */ private MethodHandle primitiveGetter; @@ -96,14 +112,6 @@ */ private Class<?> currentType; - static { - for (int i = 0; i < NOOF_TYPES; i++) { - final Type type = ACCESSOR_TYPES.get(i); - ACCESSOR_GETTER_TYPES[i] = MH.type(type.getTypeClass(), Object.class); - ACCESSOR_SETTER_TYPES[i] = MH.type(void.class, Object.class, type.getTypeClass()); - } - } - /** * Delegate constructor. This is used when adding properties to the Global scope, which * is necessary for outermost levels in a script (the ScriptObject is represented by @@ -114,19 +122,31 @@ * @param delegate delegate script object to rebind receiver to */ public AccessorProperty(final AccessorProperty property, final ScriptObject delegate) { - this(property); - - this.getters = new MethodHandle[NOOF_TYPES]; + super(property); - this.primitiveGetter = bindTo(primitiveGetter, delegate); - this.primitiveSetter = bindTo(primitiveSetter, delegate); - this.objectGetter = bindTo(objectGetter, delegate); - this.objectSetter = bindTo(objectSetter, delegate); + this.primitiveGetter = bindTo(property.primitiveGetter, delegate); + this.primitiveSetter = bindTo(property.primitiveSetter, delegate); + this.objectGetter = bindTo(property.objectGetter, delegate); + this.objectSetter = bindTo(property.objectSetter, delegate); setCurrentType(property.getCurrentType()); } /** + * Constructor for spill properties. Array getters and setters will be created on demand. + * + * @param key the property key + * @param flags the property flags + * @param slot spill slot + */ + public AccessorProperty(final String key, final int flags, final int slot) { + super(key, flags, slot); + assert (flags & IS_SPILL) == IS_SPILL; + + setCurrentType(Object.class); + } + + /** * Constructor. Similar to the constructor with both primitive getters and setters, the difference * here being that only one getter and setter (setter is optional for non writable fields) is given * to the constructor, and the rest are created from those. Used e.g. by Nasgen classes @@ -268,7 +288,40 @@ } @Override + protected void setObjectValue(final ScriptObject self, final ScriptObject owner, final Object value, final boolean strict) { + if (isSpill()) { + self.spill[getSlot()] = value; + } else { + try { + getSetter(Object.class, self.getMap()).invokeExact((Object)self, value); + } catch (final Error|RuntimeException e) { + throw e; + } catch (final Throwable e) { + throw new RuntimeException(e); + } + } + } + + @Override + protected Object getObjectValue(final ScriptObject self, final ScriptObject owner) { + if (isSpill()) { + return self.spill[getSlot()]; + } + + try { + return getGetter(Object.class).invokeExact((Object)self); + } catch (final Error|RuntimeException e) { + throw e; + } catch (final Throwable e) { + throw new RuntimeException(e); + } + } + + @Override public MethodHandle getGetter(final Class<?> type) { + if (isSpill() && objectGetter == null) { + objectGetter = getSpillGetter(); + } final int i = getAccessorTypeIndex(type); if (getters[i] == null) { getters[i] = debug( @@ -284,7 +337,7 @@ "get"); } - return isSpill() ? MH.filterArguments(getters[i], 0, SPILLGETTER) : getters[i]; + return getters[i]; } private Property getWiderProperty(final Class<?> type) { @@ -313,6 +366,9 @@ } private MethodHandle generateSetter(final Class<?> forType, final Class<?> type) { + if (isSpill() && objectSetter == null) { + objectSetter = getSpillSetter(); + } MethodHandle mh = createSetter(forType, type, primitiveSetter, objectSetter); mh = MH.asType(mh, ACCESSOR_SETTER_TYPES[getAccessorTypeIndex(type)]); //has to be the case for invokeexact to work in ScriptObject mh = debug(mh, currentType, type, "set"); @@ -343,7 +399,7 @@ mh = generateSetter(forType, type); } - return isSpill() ? MH.filterArguments(mh, 0, SPILLGETTER) : mh; + return mh; } @Override @@ -363,6 +419,30 @@ setCurrentType(newType); } + private MethodHandle getSpillGetter() { + final int slot = getSlot(); + MethodHandle getter = slot < SPILL_CACHE_SIZE ? SPILL_ACCESSORS[slot * 2] : null; + if (getter == null) { + getter = MH.asType(MH.insertArguments(SPILL_ELEMENT_GETTER, 1, slot), Lookup.GET_OBJECT_TYPE); + if (slot < SPILL_CACHE_SIZE) { + SPILL_ACCESSORS[slot * 2] = getter; + } + } + return getter; + } + + private MethodHandle getSpillSetter() { + final int slot = getSlot(); + MethodHandle setter = slot < SPILL_CACHE_SIZE ? SPILL_ACCESSORS[slot * 2 + 1] : null; + if (setter == null) { + setter = MH.asType(MH.insertArguments(SPILL_ELEMENT_SETTER, 1, slot), Lookup.SET_OBJECT_TYPE); + if (slot < SPILL_CACHE_SIZE) { + SPILL_ACCESSORS[slot * 2 + 1] = setter; + } + } + return setter; + } + private static void finest(final String str) { if (DEBUG_FIELDS) { LOG.finest(str);
--- a/src/jdk/nashorn/internal/runtime/CompiledFunction.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/runtime/CompiledFunction.java Mon Jun 03 23:24:36 2013 -0700 @@ -35,21 +35,27 @@ */ final class CompiledFunction implements Comparable<CompiledFunction> { + /** The method type may be more specific than the invoker, if. e.g. + * the invoker is guarded, and a guard with a generic object only + * fallback, while the target is more specific, we still need the + * more specific type for sorting */ + private final MethodType type; private final MethodHandle invoker; private MethodHandle constructor; - CompiledFunction(final MethodHandle invoker) { - this(invoker, null); + CompiledFunction(final MethodType type, final MethodHandle invoker) { + this(type, invoker, null); } - CompiledFunction(final MethodHandle invoker, final MethodHandle constructor) { - this.invoker = invoker; - this.constructor = constructor; //isConstructor + CompiledFunction(final MethodType type, final MethodHandle invoker, final MethodHandle constructor) { + this.type = type; + this.invoker = invoker; + this.constructor = constructor; } @Override public String toString() { - return "<invoker=" + invoker + " ctor=" + constructor + ">"; + return "<callSiteType= " + type + " invoker=" + invoker + " ctor=" + constructor + ">"; } MethodHandle getInvoker() { @@ -69,7 +75,7 @@ } MethodType type() { - return invoker.type(); + return type; } @Override @@ -103,8 +109,8 @@ return weight() > o.weight(); } - boolean moreGenericThan(final MethodType type) { - return weight() > weight(type); + boolean moreGenericThan(final MethodType mt) { + return weight() > weight(mt); } /** @@ -112,15 +118,15 @@ * It is compatible if the types are narrower than the invocation type so that * a semantically equivalent linkage can be performed. * - * @param typesc + * @param mt type to check against * @return */ - boolean typeCompatible(final MethodType type) { - final Class<?>[] wantedParams = type.parameterArray(); + boolean typeCompatible(final MethodType mt) { + final Class<?>[] wantedParams = mt.parameterArray(); final Class<?>[] existingParams = type().parameterArray(); //if we are not examining a varargs type, the number of parameters must be the same - if (wantedParams.length != existingParams.length && !isVarArgsType(type)) { + if (wantedParams.length != existingParams.length && !isVarArgsType(mt)) { return false; }
--- a/src/jdk/nashorn/internal/runtime/DebugLogger.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/runtime/DebugLogger.java Mon Jun 03 23:24:36 2013 -0700 @@ -35,7 +35,6 @@ */ public final class DebugLogger { - @SuppressWarnings("NonConstantLogger") private final Logger logger; private final boolean isEnabled;
--- a/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java Mon Jun 03 23:24:36 2013 -0700 @@ -78,9 +78,9 @@ //only nasgen constructors: (boolean, self, args) are subject to binding a boolean newObj. isConstructor //is too conservative a check. However, isConstructor(mh) always implies isConstructor param assert isConstructor(); - code.add(new CompiledFunction(MH.insertArguments(mh, 0, false), composeConstructor(MH.insertArguments(mh, 0, true), needsCallee))); //make sure callee state can be determined when we reach constructor + code.add(new CompiledFunction(mh.type(), MH.insertArguments(mh, 0, false), composeConstructor(MH.insertArguments(mh, 0, true), needsCallee))); //make sure callee state can be determined when we reach constructor } else { - code.add(new CompiledFunction(mh)); + code.add(new CompiledFunction(mh.type(), mh)); } }
--- a/src/jdk/nashorn/internal/runtime/FindProperty.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/runtime/FindProperty.java Mon Jun 03 23:24:36 2013 -0700 @@ -153,5 +153,24 @@ return prototype.isScope(); } + /** + * Get the property value from self as object. + * + * @return the property value + */ + public Object getObjectValue() { + return property.getObjectValue(getGetterReceiver(), getOwner()); + } + + /** + * Set the property value in self. + * + * @param value the new value + * @param strict strict flag + */ + public void setObjectValue(final Object value, final boolean strict) { + property.setObjectValue(getSetterReceiver(), getOwner(), value, strict); + } + }
--- a/src/jdk/nashorn/internal/runtime/GlobalFunctions.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/runtime/GlobalFunctions.java Mon Jun 03 23:24:36 2013 -0700 @@ -30,6 +30,7 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; +import java.util.Locale; /** * Utilities used by Global class. @@ -373,10 +374,10 @@ } else if (ch < 256) { sb.append('%'); final byte b = (byte)ch; - sb.append(Integer.toHexString(b & 0xFF).toUpperCase()); + sb.append(Integer.toHexString(b & 0xFF).toUpperCase(Locale.ENGLISH)); } else { sb.append("%u"); - sb.append(Integer.toHexString(ch & 0xFFFF).toUpperCase()); + sb.append(Integer.toHexString(ch & 0xFFFF).toUpperCase(Locale.ENGLISH)); } }
--- a/src/jdk/nashorn/internal/runtime/JSONFunctions.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/runtime/JSONFunctions.java Mon Jun 03 23:24:36 2013 -0700 @@ -36,6 +36,8 @@ import jdk.nashorn.internal.parser.JSONParser; import jdk.nashorn.internal.parser.TokenType; import jdk.nashorn.internal.runtime.linker.Bootstrap; +import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.getArrayIndexNoThrow; +import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex; /** * Utilities used by "JSON" object implementation. @@ -94,7 +96,7 @@ if (reviver instanceof ScriptFunction) { assert global instanceof GlobalObject; final ScriptObject root = ((GlobalObject)global).newObject(); - root.set("", unfiltered, root.isStrictContext()); + root.addOwnProperty("", Property.WRITABLE_ENUMERABLE_CONFIGURABLE, unfiltered); return walk(root, "", (ScriptFunction)reviver); } return unfiltered; @@ -115,7 +117,7 @@ if (newElement == ScriptRuntime.UNDEFINED) { valueObj.delete(key, strict); } else { - valueObj.set(key, newElement, strict); + setPropertyValue(valueObj, key, newElement, strict); } } } @@ -175,7 +177,9 @@ final PropertyNode pNode = (PropertyNode) elem; final Node valueNode = pNode.getValue(); - object.set(pNode.getKeyName(), convertNode(global, valueNode), strict); + final String name = pNode.getKeyName(); + final Object value = convertNode(global, valueNode); + setPropertyValue(object, name, value, strict); } return object; @@ -188,6 +192,21 @@ } } + // add a new property if does not exist already, or else set old property + private static void setPropertyValue(final ScriptObject sobj, final String name, final Object value, final boolean strict) { + final int index = getArrayIndexNoThrow(name); + if (isValidArrayIndex(index)) { + // array index key + sobj.defineOwnProperty(index, value); + } else if (sobj.getMap().findProperty(name) != null) { + // pre-existing non-inherited property, call set + sobj.set(name, value, strict); + } else { + // add new property + sobj.addOwnProperty(name, Property.WRITABLE_ENUMERABLE_CONFIGURABLE, value); + } + } + // does the given IR node represent a numeric array? private static boolean isNumericArray(final Node[] values) { for (final Node node : values) {
--- a/src/jdk/nashorn/internal/runtime/JSType.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/runtime/JSType.java Mon Jun 03 23:24:36 2013 -0700 @@ -28,6 +28,7 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall; import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; +import java.util.Locale; import jdk.internal.dynalink.beans.StaticClass; import jdk.nashorn.internal.codegen.CompilerConstants.Call; import jdk.nashorn.internal.parser.Lexer; @@ -111,7 +112,7 @@ */ public final String typeName() { // For NULL, "object" has to be returned! - return ((this == NULL) ? OBJECT : this).name().toLowerCase(); + return ((this == NULL) ? OBJECT : this).name().toLowerCase(Locale.ENGLISH); } /** @@ -565,8 +566,11 @@ } /** - * JavaScript compliant Object to integer conversion - * See ECMA 9.4 ToInteger + * JavaScript compliant Object to integer conversion. See ECMA 9.4 ToInteger + * + * <p>Note that this returns {@link java.lang.Integer#MAX_VALUE} or {@link java.lang.Integer#MIN_VALUE} + * for double values that exceed the int range, including positive and negative Infinity. It is the + * caller's responsibility to handle such values correctly.</p> * * @param obj an object * @return an integer @@ -576,8 +580,11 @@ } /** - * JavaScript compliant Object to long conversion - * See ECMA 9.4 ToInteger + * JavaScript compliant Object to long conversion. See ECMA 9.4 ToInteger + * + * <p>Note that this returns {@link java.lang.Long#MAX_VALUE} or {@link java.lang.Long#MIN_VALUE} + * for double values that exceed the long range, including positive and negative Infinity. It is the + * caller's responsibility to handle such values correctly.</p> * * @param obj an object * @return a long
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk/nashorn/internal/runtime/ListAdapter.java Mon Jun 03 23:24:36 2013 -0700 @@ -0,0 +1,337 @@ +package jdk.nashorn.internal.runtime; + +import java.util.AbstractList; +import java.util.Deque; +import java.util.Iterator; +import java.util.ListIterator; +import java.util.NoSuchElementException; +import java.util.RandomAccess; +import jdk.nashorn.internal.runtime.linker.InvokeByName; + +/** + * An adapter that can wrap any ECMAScript Array-like object (that adheres to the array rules for the property + * {@code length} and having conforming {@code push}, {@code pop}, {@code shift}, {@code unshift}, and {@code splice} + * methods) and expose it as both a Java list and double-ended queue. While script arrays aren't necessarily efficient + * as dequeues, it's still slightly more efficient to be able to translate dequeue operations into pushes, pops, shifts, + * and unshifts, than to blindly translate all list's add/remove operations into splices. Also, it is conceivable that a + * custom script object that implements an Array-like API can have a background data representation that is optimized + * for dequeue-like access. Note that with ECMAScript arrays, {@code push} and {@pop} operate at the end of the array, + * while in Java {@code Deque} they operate on the front of the queue and as such the Java dequeue {@link #push(Object)} + * and {@link #pop()} operations will translate to {@code unshift} and {@code shift} script operations respectively, + * while {@link #addLast(Object)} and {@link #removeLast()} will translate to {@code push} and {@code pop}. + */ +public class ListAdapter extends AbstractList<Object> implements RandomAccess, Deque<Object> { + // These add to the back and front of the list + private static final InvokeByName PUSH = new InvokeByName("push", ScriptObject.class, void.class, Object.class); + private static final InvokeByName UNSHIFT = new InvokeByName("unshift", ScriptObject.class, void.class, Object.class); + + // These remove from the back and front of the list + private static final InvokeByName POP = new InvokeByName("pop", ScriptObject.class, Object.class); + private static final InvokeByName SHIFT = new InvokeByName("shift", ScriptObject.class, Object.class); + + // These insert and remove in the middle of the list + private static final InvokeByName SPLICE_ADD = new InvokeByName("splice", ScriptObject.class, void.class, int.class, int.class, Object.class); + private static final InvokeByName SPLICE_REMOVE = new InvokeByName("splice", ScriptObject.class, void.class, int.class, int.class); + + private final ScriptObject obj; + + /** + * Creates a new list wrapper for the specified script object. + * @param obj script the object to wrap + */ + public ListAdapter(ScriptObject obj) { + this.obj = obj; + } + + @Override + public int size() { + return JSType.toInt32(obj.getLength()); + } + + @Override + public Object get(int index) { + checkRange(index); + return obj.get(index); + } + + @Override + public Object set(int index, Object element) { + checkRange(index); + final Object prevValue = get(index); + obj.set(index, element, false); + return prevValue; + } + + private void checkRange(int index) { + if(index < 0 || index >= size()) { + throw invalidIndex(index); + } + } + + @Override + public void push(Object e) { + addFirst(e); + } + + @Override + public boolean add(Object e) { + addLast(e); + return true; + } + + @Override + public void addFirst(Object e) { + try { + final Object fn = UNSHIFT.getGetter().invokeExact(obj); + checkFunction(fn, UNSHIFT); + UNSHIFT.getInvoker().invokeExact(fn, obj, e); + } catch(RuntimeException | Error ex) { + throw ex; + } catch(Throwable t) { + throw new RuntimeException(t); + } + } + + @Override + public void addLast(Object e) { + try { + final Object fn = PUSH.getGetter().invokeExact(obj); + checkFunction(fn, PUSH); + PUSH.getInvoker().invokeExact(fn, obj, e); + } catch(RuntimeException | Error ex) { + throw ex; + } catch(Throwable t) { + throw new RuntimeException(t); + } + } + + @Override + public void add(int index, Object e) { + try { + if(index < 0) { + throw invalidIndex(index); + } else if(index == 0) { + addFirst(e); + } else { + final int size = size(); + if(index < size) { + final Object fn = SPLICE_ADD.getGetter().invokeExact(obj); + checkFunction(fn, SPLICE_ADD); + SPLICE_ADD.getInvoker().invokeExact(fn, obj, index, 0, e); + } else if(index == size) { + addLast(e); + } else { + throw invalidIndex(index); + } + } + } catch(RuntimeException | Error ex) { + throw ex; + } catch(Throwable t) { + throw new RuntimeException(t); + } + } + private static void checkFunction(Object fn, InvokeByName invoke) { + if(!(fn instanceof ScriptFunction)) { + throw new UnsupportedOperationException("The script object doesn't have a function named " + invoke.getName()); + } + } + + private static IndexOutOfBoundsException invalidIndex(int index) { + return new IndexOutOfBoundsException(String.valueOf(index)); + } + + @Override + public boolean offer(Object e) { + return offerLast(e); + } + + @Override + public boolean offerFirst(Object e) { + addFirst(e); + return true; + } + + @Override + public boolean offerLast(Object e) { + addLast(e); + return true; + } + + @Override + public Object pop() { + return removeFirst(); + } + + @Override + public Object remove() { + return removeFirst(); + } + + @Override + public Object removeFirst() { + checkNonEmpty(); + return invokeShift(); + } + + @Override + public Object removeLast() { + checkNonEmpty(); + return invokePop(); + } + + private void checkNonEmpty() { + if(isEmpty()) { + throw new NoSuchElementException(); + } + } + + @Override + public Object remove(int index) { + if(index < 0) { + throw invalidIndex(index); + } else if (index == 0) { + return invokeShift(); + } else { + final int maxIndex = size() - 1; + if(index < maxIndex) { + final Object prevValue = get(index); + invokeSpliceRemove(index, 1); + return prevValue; + } else if(index == maxIndex) { + return invokePop(); + } else { + throw invalidIndex(index); + } + } + } + + private Object invokeShift() { + try { + final Object fn = SHIFT.getGetter().invokeExact(obj); + checkFunction(fn, SHIFT); + return SHIFT.getInvoker().invokeExact(fn, obj); + } catch(RuntimeException | Error ex) { + throw ex; + } catch(Throwable t) { + throw new RuntimeException(t); + } + } + + private Object invokePop() { + try { + final Object fn = POP.getGetter().invokeExact(obj); + checkFunction(fn, POP); + return POP.getInvoker().invokeExact(fn, obj); + } catch(RuntimeException | Error ex) { + throw ex; + } catch(Throwable t) { + throw new RuntimeException(t); + } + } + + @Override + protected void removeRange(int fromIndex, int toIndex) { + invokeSpliceRemove(fromIndex, toIndex - fromIndex); + } + + private void invokeSpliceRemove(int fromIndex, int count) { + try { + final Object fn = SPLICE_REMOVE.getGetter().invokeExact(obj); + checkFunction(fn, SPLICE_REMOVE); + SPLICE_REMOVE.getInvoker().invokeExact(fn, obj, fromIndex, count); + } catch(RuntimeException | Error ex) { + throw ex; + } catch(Throwable t) { + throw new RuntimeException(t); + } + } + + @Override + public Object poll() { + return pollFirst(); + } + + @Override + public Object pollFirst() { + return isEmpty() ? null : invokeShift(); + } + + @Override + public Object pollLast() { + return isEmpty() ? null : invokePop(); + } + + @Override + public Object peek() { + return peekFirst(); + } + + @Override + public Object peekFirst() { + return isEmpty() ? null : get(0); + } + + @Override + public Object peekLast() { + return isEmpty() ? null : get(size() - 1); + } + + @Override + public Object element() { + return getFirst(); + } + + @Override + public Object getFirst() { + checkNonEmpty(); + return get(0); + } + + @Override + public Object getLast() { + checkNonEmpty(); + return get(size() - 1); + } + + @Override + public Iterator<Object> descendingIterator() { + final ListIterator<Object> it = listIterator(size()); + return new Iterator<Object>() { + @Override + public boolean hasNext() { + return it.hasPrevious(); + } + + @Override + public Object next() { + return it.previous(); + } + + @Override + public void remove() { + it.remove(); + } + }; + } + + @Override + public boolean removeFirstOccurrence(Object o) { + return removeOccurrence(o, iterator()); + } + + @Override + public boolean removeLastOccurrence(Object o) { + return removeOccurrence(o, descendingIterator()); + } + + private static boolean removeOccurrence(Object o, Iterator<Object> it) { + while(it.hasNext()) { + final Object e = it.next(); + if(o == null ? e == null : o.equals(e)) { + it.remove(); + return true; + } + } + return false; + } +}
--- a/src/jdk/nashorn/internal/runtime/Logging.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/runtime/Logging.java Mon Jun 03 23:24:36 2013 -0700 @@ -26,6 +26,7 @@ package jdk.nashorn.internal.runtime; import java.util.HashMap; +import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.logging.ConsoleHandler; @@ -117,7 +118,7 @@ if ("".equals(value)) { level = Level.INFO; } else { - level = Level.parse(value.toUpperCase()); + level = Level.parse(value.toUpperCase(Locale.ENGLISH)); } final String name = Logging.lastPart(key);
--- a/src/jdk/nashorn/internal/runtime/NativeJavaPackage.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/runtime/NativeJavaPackage.java Mon Jun 03 23:24:36 2013 -0700 @@ -25,10 +25,16 @@ package jdk.nashorn.internal.runtime; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.beans.StaticClass; import jdk.internal.dynalink.linker.GuardedInvocation; import jdk.internal.dynalink.linker.LinkRequest; +import jdk.internal.dynalink.support.Guards; +import jdk.nashorn.internal.lookup.MethodHandleFactory; +import jdk.nashorn.internal.lookup.MethodHandleFunctionality; import jdk.nashorn.internal.objects.NativeJava; import jdk.nashorn.internal.objects.annotations.Attribute; import jdk.nashorn.internal.objects.annotations.Function; @@ -65,6 +71,10 @@ * </pre> */ public final class NativeJavaPackage extends ScriptObject { + private static final MethodHandleFunctionality MH = MethodHandleFactory.getFunctionality(); + private static final MethodHandle CLASS_NOT_FOUND = findOwnMH("classNotFound", Void.TYPE, NativeJavaPackage.class); + private static final MethodHandle TYPE_GUARD = Guards.getClassGuard(NativeJavaPackage.class); + /** Full name of package (includes path.) */ private final String name; @@ -123,6 +133,30 @@ return super.getDefaultValue(hint); } + @Override + protected GuardedInvocation findNewMethod(CallSiteDescriptor desc) { + return createClassNotFoundInvocation(desc); + } + + @Override + protected GuardedInvocation findCallMethod(CallSiteDescriptor desc, LinkRequest request) { + return createClassNotFoundInvocation(desc); + } + + private static GuardedInvocation createClassNotFoundInvocation(final CallSiteDescriptor desc) { + // If NativeJavaPackage is invoked either as a constructor or as a function, throw a ClassNotFoundException as + // we can assume the user attempted to instantiate a non-existent class. + final MethodType type = desc.getMethodType(); + return new GuardedInvocation( + MH.dropArguments(CLASS_NOT_FOUND, 1, type.parameterList().subList(1, type.parameterCount())), + type.parameterType(0) == NativeJavaPackage.class ? null : TYPE_GUARD); + } + + @SuppressWarnings("unused") + private static void classNotFound(final NativeJavaPackage pkg) throws ClassNotFoundException { + throw new ClassNotFoundException(pkg.name); + } + /** * "No such property" call placeholder. * @@ -188,4 +222,7 @@ return noSuchProperty(desc, request); } + private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) { + return MH.findStatic(MethodHandles.lookup(), NativeJavaPackage.class, name, MH.type(rtype, types)); + } }
--- a/src/jdk/nashorn/internal/runtime/Property.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/runtime/Property.java Mon Jun 03 23:24:36 2013 -0700 @@ -52,6 +52,9 @@ * we can use leave flag byte initialized with (the default) zero value. */ + /** Mask for property being both writable, enumerable and configurable */ + public static final int WRITABLE_ENUMERABLE_CONFIGURABLE = 0b0000_0000_0000; + /** ECMA 8.6.1 - Is this property not writable? */ public static final int NOT_WRITABLE = 0b0000_0000_0001; @@ -352,6 +355,27 @@ } /** + * Set the value of this property in {@code owner}. This allows to bypass creation of the + * setter MethodHandle for spill and user accessor properties. + * + * @param self the this object + * @param owner the owner object + * @param value the new property value + * @param strict is this a strict setter? + */ + protected abstract void setObjectValue(ScriptObject self, ScriptObject owner, Object value, boolean strict); + + /** + * Set the Object value of this property from {@code owner}. This allows to bypass creation of the + * getter MethodHandle for spill and user accessor properties. + * + * @param self the this object + * @param owner the owner object + * @return the property value + */ + protected abstract Object getObjectValue(ScriptObject self, ScriptObject owner); + + /** * Abstract method for retrieving the setter for the property. We do not know * anything about the internal representation when we request the setter, we only * know that the setter will take the property as a parameter of the given type.
--- a/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Mon Jun 03 23:24:36 2013 -0700 @@ -30,6 +30,8 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import java.util.ArrayList; +import java.util.Arrays; import java.util.LinkedList; import jdk.nashorn.internal.codegen.Compiler; @@ -49,9 +51,16 @@ */ public final class RecompilableScriptFunctionData extends ScriptFunctionData { + /** FunctionNode with the code for this ScriptFunction */ private FunctionNode functionNode; - private final PropertyMap allocatorMap; + + /** Allocator map from makeMap() */ + private final PropertyMap allocatorMap; + + /** Code installer used for all further recompilation/specialization of this ScriptFunction */ private final CodeInstaller<ScriptEnvironment> installer; + + /** Name of class where allocator function resides */ private final String allocatorClassName; /** lazily generated allocator */ @@ -60,6 +69,23 @@ private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); /** + * Used for specialization based on runtime arguments. Whenever we specialize on + * callsite parameter types at runtime, we need to use a parameter type guard to + * ensure that the specialized version of the script function continues to be + * applicable for a particular callsite * + */ + private static final MethodHandle PARAM_TYPE_GUARD = findOwnMH("paramTypeGuard", boolean.class, Type[].class, Object[].class); + + /** + * It is usually a good gamble whever we detect a runtime callsite with a double + * (or java.lang.Number instance) to specialize the parameter to an integer, if the + * parameter in question can be represented as one. The double typically only exists + * because the compiler doesn't know any better than "a number type" and conservatively + * picks doubles when it can't prove that an integer addition wouldn't overflow + */ + private static final MethodHandle ENSURE_INT = findOwnMH("ensureInt", int.class, Object.class); + + /** * Constructor - public as scripts use it * * @param functionNode functionNode that represents this function code @@ -141,14 +167,6 @@ return; // nothing to do, we have code, at least some. } - // check if function node is lazy, need to compile it. - // note that currently function cloning is not working completely, which - // means that the compiler will mutate the function node it has been given - // once it has been compiled, it cannot be recompiled. This means that - // lazy compilation works (not compiled yet) but e.g. specializations won't - // until the copy-on-write changes for IR are in, making cloning meaningless. - // therefore, currently method specialization is disabled. TODO - if (functionNode.isLazy()) { Compiler.LOG.info("Trampoline hit: need to do lazy compilation of '", functionNode.getName(), "'"); final Compiler compiler = new Compiler(installer); @@ -156,38 +174,55 @@ assert !functionNode.isLazy(); compiler.install(functionNode); - // we don't need to update any flags - varArgs and needsCallee are instrincic - // in the function world we need to get a destination node from the compile instead - // and replace it with our function node. TODO + /* + * We don't need to update any flags - varArgs and needsCallee are instrincic + * in the function world we need to get a destination node from the compile instead + * and replace it with our function node. TODO + */ } - // we can't get here unless we have bytecode, either from eager compilation or from - // running a lazy compile on the lines above + /* + * We can't get to this program point unless we have bytecode, either from + * eager compilation or from running a lazy compile on the lines above + */ assert functionNode.hasState(CompilationState.EMITTED) : functionNode.getName() + " " + functionNode.getState() + " " + Debug.id(functionNode); // code exists - look it up and add it into the automatically sorted invoker list - addCode(functionNode, null, null); + addCode(functionNode); } - private MethodHandle addCode(final FunctionNode fn, final MethodHandle guard, final MethodHandle fallback) { - final MethodHandle target = + private MethodHandle addCode(final FunctionNode fn) { + return addCode(fn, null, null, null); + } + + private MethodHandle addCode(final FunctionNode fn, final MethodType runtimeType, final MethodHandle guard, final MethodHandle fallback) { + final MethodType targetType = new FunctionSignature(fn).getMethodType(); + MethodHandle target = MH.findStatic( LOOKUP, fn.getCompileUnit().getCode(), fn.getName(), - new FunctionSignature(fn). - getMethodType()); - MethodHandle mh = target; - if (guard != null) { - try { - mh = MH.guardWithTest(MH.asCollector(guard, Object[].class, target.type().parameterCount()), MH.asType(target, fallback.type()), fallback); - } catch (Throwable e) { - e.printStackTrace(); + targetType); + + /* + * For any integer argument. a double that is representable as an integer is OK. + * otherwise the guard would have failed. in that case introduce a filter that + * casts the double to an integer, which we know will preserve all precision. + */ + for (int i = 0; i < targetType.parameterCount(); i++) { + if (targetType.parameterType(i) == int.class) { + //representable as int + target = MH.filterArguments(target, i, ENSURE_INT); } } - final CompiledFunction cf = new CompiledFunction(mh); + MethodHandle mh = target; + if (guard != null) { + mh = MH.guardWithTest(MH.asCollector(guard, Object[].class, target.type().parameterCount()), MH.asType(target, fallback.type()), fallback); + } + + final CompiledFunction cf = new CompiledFunction(runtimeType == null ? targetType : runtimeType, mh); code.add(cf); return cf.getInvoker(); @@ -212,69 +247,162 @@ return Type.OBJECT; } - @SuppressWarnings("unused") - private static boolean paramTypeGuard(final Type[] compileTimeTypes, final Type[] runtimeTypes, Object... args) { - //System.err.println("Param type guard " + Arrays.asList(args)); + private static boolean canCoerce(final Object arg, final Type type) { + Type argType = runtimeType(arg); + if (Type.widest(argType, type) == type || arg == ScriptRuntime.UNDEFINED) { + return true; + } + System.err.println(arg + " does not fit in "+ argType + " " + type + " " + arg.getClass()); + new Throwable().printStackTrace(); return false; } - private static final MethodHandle PARAM_TYPE_GUARD = findOwnMH("paramTypeGuard", boolean.class, Type[].class, Type[].class, Object[].class); + @SuppressWarnings("unused") + private static boolean paramTypeGuard(final Type[] paramTypes, final Object... args) { + final int length = args.length; + assert args.length >= paramTypes.length; + + //i==start, skip the this, callee params etc + int start = args.length - paramTypes.length; + for (int i = start; i < args.length; i++) { + final Object arg = args[i]; + if (!canCoerce(arg, paramTypes[i - start])) { + return false; + } + } + return true; + } + + @SuppressWarnings("unused") + private static int ensureInt(final Object arg) { + if (arg instanceof Number) { + return ((Number)arg).intValue(); + } else if (arg instanceof Undefined) { + return 0; + } + throw new AssertionError(arg); + } + + /** + * Given the runtime callsite args, compute a method type that is equivalent to what + * was passed - this is typically a lot more specific that what the compiler has been + * able to deduce + * @param callSiteType callsite type for the compiled callsite target + * @param args runtime arguments to the compiled callsite target + * @return adjusted method type, narrowed as to conform to runtime callsite type instead + */ + private static MethodType runtimeType(final MethodType callSiteType, final Object[] args) { + if (args == null) { + //for example bound, or otherwise runtime arguments to callsite unavailable, then + //do not change the type + return callSiteType; + } + final Class<?>[] paramTypes = new Class<?>[callSiteType.parameterCount()]; + final int start = args.length - callSiteType.parameterCount(); + for (int i = start; i < args.length; i++) { + paramTypes[i - start] = runtimeType(args[i]).getTypeClass(); + } + return MH.type(callSiteType.returnType(), paramTypes); + } + + private static ArrayList<Type> runtimeType(final MethodType mt) { + final ArrayList<Type> type = new ArrayList<>(); + for (int i = 0; i < mt.parameterCount(); i++) { + type.add(Type.typeFor(mt.parameterType(i))); + } + return type; + } @Override MethodHandle getBestInvoker(final MethodType callSiteType, final Object[] args) { - final MethodHandle mh = super.getBestInvoker(callSiteType, args); + final MethodType runtimeType = runtimeType(callSiteType, args); + assert runtimeType.parameterCount() == callSiteType.parameterCount(); + + final MethodHandle mh = super.getBestInvoker(runtimeType, args); - if (!functionNode.canSpecialize() || !code.isLessSpecificThan(callSiteType)) { + /* + * Not all functions can be specialized, for example, if we deemed memory + * footprint too large to store a parse snapshot, or if it is meaningless + * to do so, such as e.g. for runScript + */ + if (!functionNode.canSpecialize()) { return mh; } - final FunctionNode snapshot = functionNode.getSnapshot(); - if (snapshot == null) { + /* + * Check if best invoker is equally specific or more specific than runtime + * type. In that case, we don't need further specialization, but can use + * whatever we have already. We know that it will match callSiteType, or it + * would not have been returned from getBestInvoker + */ + if (!code.isLessSpecificThan(runtimeType)) { return mh; } int i; + final FunctionNode snapshot = functionNode.getSnapshot(); + assert snapshot != null; - //classes known at runtime - final LinkedList<Type> runtimeArgs = new LinkedList<>(); - for (i = args.length - 1; i >= args.length - snapshot.getParameters().size(); i--) { - runtimeArgs.addLast(runtimeType(args[i])); - } - - //classes known at compile time + /* + * Create a list of the arg types that the compiler knows about + * typically, the runtime args are a lot more specific, and we should aggressively + * try to use those whenever possible + * We WILL try to make an aggressive guess as possible, and add guards if needed. + * For example, if the compiler can deduce that we have a number type, but the runtime + * passes and int, we might still want to keep it an int, and the gamble to + * check that whatever is passed is int representable usually pays off + * If the compiler only knows that a parameter is an "Object", it is still worth + * it to try to specialize it by looking at the runtime arg. + */ final LinkedList<Type> compileTimeArgs = new LinkedList<>(); for (i = callSiteType.parameterCount() - 1; i >= 0 && compileTimeArgs.size() < snapshot.getParameters().size(); i--) { - compileTimeArgs.addLast(Type.typeFor(callSiteType.parameterType(i))); + compileTimeArgs.addFirst(Type.typeFor(callSiteType.parameterType(i))); } - //the classes known at compile time are a safe to generate as primitives without parameter guards - //the classes known at runtime are safe to generate as primitives IFF there are parameter guards + /* + * The classes known at compile time are a safe to generate as primitives without parameter guards + * But the classes known at runtime (if more specific than compile time types) are safe to generate as primitives + * IFF there are parameter guards + */ MethodHandle guard = null; + final ArrayList<Type> runtimeParamTypes = runtimeType(runtimeType); + while (runtimeParamTypes.size() > functionNode.getParameters().size()) { + runtimeParamTypes.remove(0); + } for (i = 0; i < compileTimeArgs.size(); i++) { - final Type runtimeType = runtimeArgs.get(i); - final Type compileType = compileTimeArgs.get(i); + final Type rparam = Type.typeFor(runtimeType.parameterType(i)); + final Type cparam = compileTimeArgs.get(i); - if (compileType.isObject() && !runtimeType.isObject()) { + if (cparam.isObject() && !rparam.isObject()) { + //check that the runtime object is still coercible to the runtime type, because compiler can't prove it's always primitive if (guard == null) { - guard = PARAM_TYPE_GUARD; - guard = MH.insertArguments(guard, 0, compileTimeArgs.toArray(new Type[compileTimeArgs.size()]), runtimeArgs.toArray(new Type[runtimeArgs.size()])); + guard = MH.insertArguments(PARAM_TYPE_GUARD, 0, (Object)runtimeParamTypes.toArray(new Type[runtimeParamTypes.size()])); } } } - //System.err.println("Specialized " + name + " " + runtimeArgs + " known=" + compileTimeArgs); + Compiler.LOG.info("Callsite specialized ", name, " runtimeType=", runtimeType, " parameters=", snapshot.getParameters(), " args=", Arrays.asList(args)); assert snapshot != null; assert snapshot != functionNode; final Compiler compiler = new Compiler(installer); - final FunctionNode compiledSnapshot = compiler.compile(snapshot.setHints(null, new Compiler.Hints(compileTimeArgs.toArray(new Type[compileTimeArgs.size()])))); + + final FunctionNode compiledSnapshot = compiler.compile( + snapshot.setHints( + null, + new Compiler.Hints(runtimeParamTypes.toArray(new Type[runtimeParamTypes.size()])))); + /* + * No matter how narrow your types were, they can never be narrower than Attr during recompile made them. I.e. you + * can put an int into the function here, if you see it as a runtime type, but if the function uses a multiplication + * on it, it will still need to be a double. At least until we have overflow checks. Similarly, if an int is + * passed but it is used as a string, it makes no sense to make the parameter narrower than Object. At least until + * the "different types for one symbol in difference places" work is done + */ compiler.install(compiledSnapshot); - final MethodHandle nmh = addCode(compiledSnapshot, guard, mh); - - return nmh; + return addCode(compiledSnapshot, runtimeType, guard, mh); } private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
--- a/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java Mon Jun 03 23:24:36 2013 -0700 @@ -54,7 +54,7 @@ private final Namespace namespace; /** Current Options object. */ - private Options options; + private final Options options; /** Always allow functions as statements */ public final boolean _anon_functions; @@ -155,6 +155,9 @@ /** print symbols and their contents for the script */ public final boolean _print_symbols; + /** range analysis for known types */ + public final boolean _range_analysis; + /** is this environment in scripting mode? */ public final boolean _scripting; @@ -183,7 +186,7 @@ * @param out output print writer * @param err error print writer */ - ScriptEnvironment(final Options options, final PrintWriter out, final PrintWriter err) { + public ScriptEnvironment(final Options options, final PrintWriter out, final PrintWriter err) { this.out = out; this.err = err; this.namespace = new Namespace(); @@ -219,6 +222,7 @@ _print_parse = options.getBoolean("print.parse"); _print_lower_parse = options.getBoolean("print.lower.parse"); _print_symbols = options.getBoolean("print.symbols"); + _range_analysis = options.getBoolean("range.analysis"); _scripting = options.getBoolean("scripting"); _strict = options.getBoolean("strict"); _version = options.getBoolean("version"); @@ -258,14 +262,19 @@ } this._callsite_flags = callSiteFlags; - final Option<?> option = options.get("timezone"); - if (option != null) { - this._timezone = (TimeZone)option.getValue(); + final Option<?> timezoneOption = options.get("timezone"); + if (timezoneOption != null) { + this._timezone = (TimeZone)timezoneOption.getValue(); } else { this._timezone = TimeZone.getDefault(); } - this._locale = Locale.getDefault(); + final Option<?> localeOption = options.get("locale"); + if (localeOption != null) { + this._locale = (Locale)localeOption.getValue(); + } else { + this._locale = Locale.getDefault(); + } } /**
--- a/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java Mon Jun 03 23:24:36 2013 -0700 @@ -25,14 +25,13 @@ package jdk.nashorn.internal.runtime; +import static jdk.nashorn.internal.lookup.Lookup.MH; import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; -import static jdk.nashorn.internal.lookup.Lookup.MH; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; - import jdk.nashorn.internal.runtime.linker.JavaAdapterFactory; /** @@ -92,12 +91,13 @@ CompiledFunction bind(final CompiledFunction originalInv, final ScriptFunction fn, final Object self, final Object[] args) { final MethodHandle boundInvoker = bindInvokeHandle(originalInv.getInvoker(), fn, self, args); + //TODO the boundinvoker.type() could actually be more specific here if (isConstructor()) { ensureConstructor(originalInv); - return new CompiledFunction(boundInvoker, bindConstructHandle(originalInv.getConstructor(), fn, args)); + return new CompiledFunction(boundInvoker.type(), boundInvoker, bindConstructHandle(originalInv.getConstructor(), fn, args)); } - return new CompiledFunction(boundInvoker); + return new CompiledFunction(boundInvoker.type(), boundInvoker); } /** @@ -389,7 +389,9 @@ boundInvoker = noArgBoundInvoker; } } else { - final Object[] boundArgs = new Object[Math.min(originalInvoker.type().parameterCount(), args.length + (isTargetBound ? 0 : (needsCallee ? 2 : 1)))]; + // If target is already bound, insert additional bound arguments after "this" argument, at position 1. + final int argInsertPos = isTargetBound ? 1 : 0; + final Object[] boundArgs = new Object[Math.min(originalInvoker.type().parameterCount() - argInsertPos, args.length + (isTargetBound ? 0 : (needsCallee ? 2 : 1)))]; int next = 0; if (!isTargetBound) { if (needsCallee) { @@ -403,7 +405,7 @@ // "this" will get dropped anyway by the target invoker. We previously asserted that already bound functions // don't take a callee parameter, so we can know that the signature is (this[, args...]) therefore args // start at position 1. If the function is not bound, we start inserting arguments at position 0. - boundInvoker = MH.insertArguments(originalInvoker, isTargetBound ? 1 : 0, boundArgs); + boundInvoker = MH.insertArguments(originalInvoker, argInsertPos, boundArgs); } if (isTargetBound) {
--- a/src/jdk/nashorn/internal/runtime/ScriptObject.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/runtime/ScriptObject.java Mon Jun 03 23:24:36 2013 -0700 @@ -25,7 +25,6 @@ package jdk.nashorn.internal.runtime; -import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall; import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCall; import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup; import static jdk.nashorn.internal.lookup.Lookup.MH; @@ -151,17 +150,6 @@ /** Method handle for setting the user accessors of a ScriptObject */ public static final Call SET_USER_ACCESSORS = virtualCall(ScriptObject.class, "setUserAccessors", void.class, String.class, ScriptFunction.class, ScriptFunction.class); - /** Method handle for getter for {@link UserAccessorProperty}, given a slot */ - static final Call USER_ACCESSOR_GETTER = staticCall(MethodHandles.lookup(), ScriptObject.class, "userAccessorGetter", Object.class, ScriptObject.class, int.class, Object.class); - - /** Method handle for setter for {@link UserAccessorProperty}, given a slot */ - static final Call USER_ACCESSOR_SETTER = staticCall(MethodHandles.lookup(), ScriptObject.class, "userAccessorSetter", void.class, ScriptObject.class, int.class, String.class, Object.class, Object.class); - - private static final MethodHandle INVOKE_UA_GETTER = Bootstrap.createDynamicInvoker("dyn:call", Object.class, - Object.class, Object.class); - private static final MethodHandle INVOKE_UA_SETTER = Bootstrap.createDynamicInvoker("dyn:call", void.class, - Object.class, Object.class, Object.class); - /** * Constructor */ @@ -699,17 +687,9 @@ * @return New property. */ public final Property addOwnProperty(final String key, final int propertyFlags, final Object value) { - final MethodHandle setter = addSpill(key, propertyFlags); - - try { - setter.invokeExact((Object)this, value); - } catch (final Error|RuntimeException e) { - throw e; - } catch (final Throwable e) { - throw new RuntimeException(e); - } - - return getMap().findProperty(key); + final Property property = addSpillProperty(key, propertyFlags); + property.setObjectValue(this, this, value, false); + return property; } /** @@ -744,15 +724,7 @@ // Erase the property field value with undefined. If the property is defined // by user-defined accessors, we don't want to call the setter!! if (!(property instanceof UserAccessorProperty)) { - try { - // make the property value to be undefined - //TODO specproperties - property.getSetter(Object.class, getMap()).invokeExact((Object)this, (Object)UNDEFINED); - } catch (final RuntimeException | Error e) { - throw e; - } catch (final Throwable t) { - throw new RuntimeException(t); - } + property.setObjectValue(this, this, UNDEFINED, false); } } @@ -948,18 +920,7 @@ * @return the value of the property */ protected static Object getObjectValue(final FindProperty find) { - final MethodHandle getter = find.getGetter(Object.class); - if (getter != null) { - try { - return getter.invokeExact((Object)find.getGetterReceiver()); - } catch (final Error|RuntimeException e) { - throw e; - } catch (final Throwable e) { - throw new RuntimeException(e); - } - } - - return UNDEFINED; + return find.getObjectValue(); } /** @@ -2087,11 +2048,7 @@ property = addOwnProperty(property); } else { int i = getMap().getSpillLength(); - MethodHandle getter = MH.arrayElementGetter(Object[].class); - MethodHandle setter = MH.arrayElementSetter(Object[].class); - getter = MH.asType(MH.insertArguments(getter, 1, i), Lookup.GET_OBJECT_TYPE); - setter = MH.asType(MH.insertArguments(setter, 1, i), Lookup.SET_OBJECT_TYPE); - property = new AccessorProperty(key, propertyFlags | Property.IS_SPILL, i, getter, setter); + property = new AccessorProperty(key, propertyFlags | Property.IS_SPILL, i); notifyPropertyAdded(this, property); property = addOwnProperty(property); i = property.getSlot(); @@ -2115,20 +2072,15 @@ /** * Add a spill entry for the given key. - * @param key Property key. - * @param propertyFlags Property flags. + * @param key Property key. * @return Setter method handle. */ - private MethodHandle addSpill(final String key, final int propertyFlags) { - final Property spillProperty = addSpillProperty(key, propertyFlags); + MethodHandle addSpill(final String key) { + final Property spillProperty = addSpillProperty(key, 0); final Class<?> type = Object.class; return spillProperty.getSetter(type, getMap()); //TODO specfields } - MethodHandle addSpill(final String key) { - return addSpill(key, 0); - } - /** * Make sure arguments are paired correctly, with respect to more parameters than declared, * fewer parameters than declared and other things that JavaScript allows. This might involve @@ -2659,14 +2611,8 @@ return; } - try { - final MethodHandle setter = f.getSetter(Object.class, strict); //TODO specfields - setter.invokeExact((Object)f.getSetterReceiver(), value); - } catch (final Error|RuntimeException e) { - throw e; - } catch (final Throwable e) { - throw new RuntimeException(e); - } + f.setObjectValue(value, strict); + } else if (!isExtensible()) { if (strict) { throw typeError("object.non.extensible", key, ScriptRuntime.safeToString(this)); @@ -2677,13 +2623,7 @@ } private void spill(final String key, final Object value) { - try { - addSpill(key).invokeExact((Object)this, value); - } catch (final Error|RuntimeException e) { - throw e; - } catch (final Throwable e) { - throw new RuntimeException(e); - } + addSpillProperty(key, 0).setObjectValue(this, this, value, false); } @@ -3217,46 +3157,6 @@ return (index < 0 || (index >= spill.length)) ? null : spill[index]; } - // User defined getter and setter are always called by "dyn:call". Note that the user - // getter/setter may be inherited. If so, proto is bound during lookup. In either - // inherited or self case, slot is also bound during lookup. Actual ScriptFunction - // to be called is retrieved everytime and applied. - @SuppressWarnings("unused") - private static Object userAccessorGetter(final ScriptObject proto, final int slot, final Object self) { - final ScriptObject container = (proto != null) ? proto : (ScriptObject)self; - final Object func = container.getSpill(slot); - - if (func instanceof ScriptFunction) { - try { - return INVOKE_UA_GETTER.invokeExact(func, self); - } catch(final Error|RuntimeException t) { - throw t; - } catch(final Throwable t) { - throw new RuntimeException(t); - } - } - - return UNDEFINED; - } - - @SuppressWarnings("unused") - private static void userAccessorSetter(final ScriptObject proto, final int slot, final String name, final Object self, final Object value) { - final ScriptObject container = (proto != null) ? proto : (ScriptObject)self; - final Object func = container.getSpill(slot); - - if (func instanceof ScriptFunction) { - try { - INVOKE_UA_SETTER.invokeExact(func, self, value); - } catch(final Error|RuntimeException t) { - throw t; - } catch(final Throwable t) { - throw new RuntimeException(t); - } - } else if (name != null) { - throw typeError("property.has.no.setter", name, ScriptRuntime.safeToString(self)); - } - } - private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) { final Class<?> own = ScriptObject.class; final MethodType mt = MH.type(rtype, types);
--- a/src/jdk/nashorn/internal/runtime/ScriptRuntime.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/runtime/ScriptRuntime.java Mon Jun 03 23:24:36 2013 -0700 @@ -36,6 +36,7 @@ import java.lang.reflect.Array; import java.util.Collections; import java.util.Iterator; +import java.util.Locale; import java.util.NoSuchElementException; import java.util.Objects; import jdk.internal.dynalink.beans.StaticClass; @@ -788,7 +789,7 @@ return false; } - throw typeError("in.with.non.object", rvalType.toString().toLowerCase()); + throw typeError("in.with.non.object", rvalType.toString().toLowerCase(Locale.ENGLISH)); } /**
--- a/src/jdk/nashorn/internal/runtime/ScriptingFunctions.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/runtime/ScriptingFunctions.java Mon Jun 03 23:24:36 2013 -0700 @@ -46,7 +46,7 @@ public final class ScriptingFunctions { /** Handle to implementation of {@link ScriptingFunctions#readLine} - Nashorn extension */ - public static final MethodHandle READLINE = findOwnMH("readLine", Object.class, Object.class); + public static final MethodHandle READLINE = findOwnMH("readLine", Object.class, Object.class, Object.class); /** Handle to implementation of {@link ScriptingFunctions#readFully} - Nashorn extension */ public static final MethodHandle READFULLY = findOwnMH("readFully", Object.class, Object.class, Object.class); @@ -78,13 +78,17 @@ * Nashorn extension: global.readLine (scripting-mode-only) * Read one line of input from the standard input. * - * @param self self reference + * @param self self reference + * @param prompt String used as input prompt * * @return line that was read * * @throws IOException if an exception occurs */ - public static Object readLine(final Object self) throws IOException { + public static Object readLine(final Object self, final Object prompt) throws IOException { + if (prompt != UNDEFINED) { + System.out.print(JSType.toString(prompt)); + } final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); return reader.readLine(); }
--- a/src/jdk/nashorn/internal/runtime/SetMethodCreator.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/runtime/SetMethodCreator.java Mon Jun 03 23:24:36 2013 -0700 @@ -183,17 +183,10 @@ private SetMethod createNewSpillPropertySetter() { final int nextSpill = getMap().getSpillLength(); - final Property property = createSpillProperty(nextSpill); + final Property property = new AccessorProperty(getName(), Property.IS_SPILL, nextSpill); return new SetMethod(createSpillMethodHandle(nextSpill, property), property); } - private Property createSpillProperty(final int nextSpill) { - final MethodHandle getter = MH.asType(MH.insertArguments(MH.arrayElementGetter(Object[].class), 1, nextSpill), Lookup.GET_OBJECT_TYPE); - final MethodHandle setter = MH.asType(MH.insertArguments(MH.arrayElementSetter(Object[].class), 1, nextSpill), Lookup.SET_OBJECT_TYPE); - - return new AccessorProperty(getName(), Property.IS_SPILL, nextSpill, getter, setter); - } - private MethodHandle createSpillMethodHandle(final int nextSpill, Property property) { final PropertyMap oldMap = getMap(); final PropertyMap newMap = getNewMap(property);
--- a/src/jdk/nashorn/internal/runtime/UserAccessorProperty.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/runtime/UserAccessorProperty.java Mon Jun 03 23:24:36 2013 -0700 @@ -26,7 +26,15 @@ package jdk.nashorn.internal.runtime; import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; + +import jdk.nashorn.internal.codegen.CompilerConstants; import jdk.nashorn.internal.lookup.Lookup; +import jdk.nashorn.internal.runtime.linker.Bootstrap; + +import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall; +import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; +import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; /** * Property with user defined getters/setters. Actual getter and setter @@ -51,6 +59,22 @@ /** User defined setter function slot. */ private final int setterSlot; + /** Getter method handle */ + private final static CompilerConstants.Call USER_ACCESSOR_GETTER = staticCall(MethodHandles.lookup(), UserAccessorProperty.class, + "userAccessorGetter", Object.class, ScriptObject.class, int.class, Object.class); + + /** Setter method handle */ + private final static CompilerConstants.Call USER_ACCESSOR_SETTER = staticCall(MethodHandles.lookup(), UserAccessorProperty.class, + "userAccessorSetter", void.class, ScriptObject.class, int.class, String.class, Object.class, Object.class); + + /** Dynamic invoker for getter */ + private static final MethodHandle INVOKE_UA_GETTER = Bootstrap.createDynamicInvoker("dyn:call", Object.class, + Object.class, Object.class); + + /** Dynamic invoker for setter */ + private static final MethodHandle INVOKE_UA_SETTER = Bootstrap.createDynamicInvoker("dyn:call", void.class, + Object.class, Object.class, Object.class); + /** * Constructor * @@ -134,8 +158,18 @@ } @Override + protected Object getObjectValue(final ScriptObject self, final ScriptObject owner) { + return userAccessorGetter(owner, getGetterSlot(), self); + } + + @Override + protected void setObjectValue(final ScriptObject self, final ScriptObject owner, final Object value, final boolean strict) { + userAccessorSetter(owner, getSetterSlot(), strict ? getKey() : null, self, value); + } + + @Override public MethodHandle getGetter(final Class<?> type) { - return Lookup.filterReturnType(ScriptObject.USER_ACCESSOR_GETTER.methodHandle(), type); + return Lookup.filterReturnType(USER_ACCESSOR_GETTER.methodHandle(), type); } @Override @@ -146,7 +180,7 @@ @Override public MethodHandle getSetter(final Class<?> type, final PropertyMap currentMap) { - return ScriptObject.USER_ACCESSOR_SETTER.methodHandle(); + return USER_ACCESSOR_SETTER.methodHandle(); } @Override @@ -155,4 +189,42 @@ return (value instanceof ScriptFunction) ? (ScriptFunction) value : null; } + // User defined getter and setter are always called by "dyn:call". Note that the user + // getter/setter may be inherited. If so, proto is bound during lookup. In either + // inherited or self case, slot is also bound during lookup. Actual ScriptFunction + // to be called is retrieved everytime and applied. + static Object userAccessorGetter(final ScriptObject proto, final int slot, final Object self) { + final ScriptObject container = (proto != null) ? proto : (ScriptObject)self; + final Object func = container.getSpill(slot); + + if (func instanceof ScriptFunction) { + try { + return INVOKE_UA_GETTER.invokeExact(func, self); + } catch(final Error|RuntimeException t) { + throw t; + } catch(final Throwable t) { + throw new RuntimeException(t); + } + } + + return UNDEFINED; + } + + static void userAccessorSetter(final ScriptObject proto, final int slot, final String name, final Object self, final Object value) { + final ScriptObject container = (proto != null) ? proto : (ScriptObject)self; + final Object func = container.getSpill(slot); + + if (func instanceof ScriptFunction) { + try { + INVOKE_UA_SETTER.invokeExact(func, self, value); + } catch(final Error|RuntimeException t) { + throw t; + } catch(final Throwable t) { + throw new RuntimeException(t); + } + } else if (name != null) { + throw typeError("property.has.no.setter", name, ScriptRuntime.safeToString(self)); + } + } + }
--- a/src/jdk/nashorn/internal/runtime/linker/InvokeByName.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/runtime/linker/InvokeByName.java Mon Jun 03 23:24:36 2013 -0700 @@ -40,7 +40,7 @@ * private static final InvokeByName TO_JSON = new InvokeByName("toJSON", Object.class, Object.class, Object.class); * ... * final Object toJSONFn = TO_JSON.getGetter().invokeExact(obj); - * value = TO_JSON.getInvoker().invokeExact(toJSON, obj, key); + * value = TO_JSON.getInvoker().invokeExact(toJSONFn, obj, key); * </pre> * In practice, you can have stronger type assumptions if it makes sense for your code, just remember that you must use * the same parameter types as the formal types of the arguments for {@code invokeExact} to work: @@ -50,7 +50,7 @@ * final ScriptObject sobj = (ScriptObject)obj; * final Object toJSONFn = TO_JSON.getGetter().invokeExact(sobj); * if(toJSONFn instanceof ScriptFunction) { - * value = TO_JSON.getInvoker().invokeExact(toJSON, sobj, key); + * value = TO_JSON.getInvoker().invokeExact(toJSONFn, sobj, key); * } * </pre> * Note that in general you will not want to reuse a single instance of this class for implementing more than one call @@ -59,6 +59,7 @@ * separate instance of this class for every place. */ public class InvokeByName { + private final String name; private final MethodHandle getter; private final MethodHandle invoker; @@ -81,6 +82,7 @@ * @param ptypes the parameter types of the function. */ public InvokeByName(final String name, final Class<?> targetClass, final Class<?> rtype, final Class<?>... ptypes) { + this.name = name; getter = Bootstrap.createDynamicInvoker("dyn:getMethod|getProp|getItem:" + name, Object.class, targetClass); final Class<?>[] finalPtypes; @@ -97,6 +99,14 @@ } /** + * Returns the name of the function retrieved through this invoker. + * @return the name of the function retrieved through this invoker. + */ + public String getName() { + return name; + } + + /** * Returns the property getter that can be invoked on an object to retrieve the function object that will be * subsequently invoked by the invoker returned by {@link #getInvoker()}. * @return the property getter method handle for the function.
--- a/src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java Mon Jun 03 23:24:36 2013 -0700 @@ -38,7 +38,7 @@ import jdk.internal.dynalink.linker.TypeBasedGuardingDynamicLinker; import jdk.internal.dynalink.support.CallSiteDescriptorFactory; import jdk.nashorn.internal.runtime.JSType; -import netscape.javascript.JSObject; +import jdk.nashorn.api.scripting.JSObject; /** * A Dynalink linker to handle web browser built-in JS (DOM etc.) objects as well
--- a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java Mon Jun 03 23:24:36 2013 -0700 @@ -310,7 +310,34 @@ Type.getMethodDescriptor(Type.VOID_TYPE), null, null)); mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getClassOverrides", GET_CLASS_INITIALIZER_DESCRIPTOR); - // Assign MethodHandle fields through invoking getHandle() + final Label initGlobal; + if(samName != null) { + // If the class is a SAM, allow having a ScriptFunction passed as class overrides + final Label notAFunction = new Label(); + mv.dup(); + mv.instanceOf(SCRIPT_FUNCTION_TYPE); + mv.ifeq(notAFunction); + mv.checkcast(SCRIPT_FUNCTION_TYPE); + + // Assign MethodHandle fields through invoking getHandle() for a ScriptFunction, only assigning the SAM + // method(s). + for (final MethodInfo mi : methodInfos) { + if(mi.getName().equals(samName)) { + mv.dup(); + mv.aconst(Type.getMethodType(mi.type.toMethodDescriptorString())); + mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getHandle", GET_HANDLE_FUNCTION_DESCRIPTOR); + } else { + mv.visitInsn(ACONST_NULL); + } + mv.putstatic(generatedClassName, mi.methodHandleClassFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR); + } + initGlobal = new Label(); + mv.goTo(initGlobal); + mv.visitLabel(notAFunction); + } else { + initGlobal = null; + } + // Assign MethodHandle fields through invoking getHandle() for a ScriptObject for (final MethodInfo mi : methodInfos) { mv.dup(); mv.aconst(mi.getName()); @@ -319,6 +346,9 @@ mv.putstatic(generatedClassName, mi.methodHandleClassFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR); } + if(initGlobal != null) { + mv.visitLabel(initGlobal); + } // Assign "staticGlobal = Context.getGlobal()" invokeGetGlobalWithNullCheck(mv); mv.putstatic(generatedClassName, STATIC_GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
--- a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterClassLoader.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterClassLoader.java Mon Jun 03 23:24:36 2013 -0700 @@ -43,6 +43,7 @@ import java.security.PrivilegedAction; import java.security.ProtectionDomain; import java.security.SecureClassLoader; + import jdk.internal.dynalink.beans.StaticClass; import jdk.internal.org.objectweb.asm.ClassWriter; import jdk.internal.org.objectweb.asm.Opcodes; @@ -58,6 +59,7 @@ * "class loader", it does not, in fact, extend {@code ClassLoader}, but rather uses them internally. Instances of this * class are normally created by {@link JavaAdapterBytecodeGenerator}. */ +@SuppressWarnings("javadoc") class JavaAdapterClassLoader extends JavaAdapterGeneratorBase { private static final Type PRIVILEGED_ACTION_TYPE = Type.getType(PrivilegedAction.class);
--- a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java Mon Jun 03 23:24:36 2013 -0700 @@ -39,6 +39,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; + import jdk.internal.dynalink.beans.StaticClass; import jdk.internal.dynalink.support.LinkRequestImpl; import jdk.nashorn.internal.objects.NativeJava; @@ -66,6 +67,7 @@ * </p> */ +@SuppressWarnings("javadoc") public final class JavaAdapterFactory { /** * A mapping from an original Class object to AdapterInfo representing the adapter for the class it represents.
--- a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterGeneratorBase.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterGeneratorBase.java Mon Jun 03 23:24:36 2013 -0700 @@ -33,6 +33,7 @@ * Base class for both {@link JavaAdapterBytecodeGenerator} and {@link JavaAdapterClassLoader}, containing those * bytecode types, type names and method descriptor that are used by both. */ +@SuppressWarnings("javadoc") abstract class JavaAdapterGeneratorBase { static final Type CONTEXT_TYPE = Type.getType(Context.class); static final Type OBJECT_TYPE = Type.getType(Object.class);
--- a/src/jdk/nashorn/internal/runtime/options/Option.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/runtime/options/Option.java Mon Jun 03 23:24:36 2013 -0700 @@ -42,10 +42,6 @@ this.value = value; } - void setValue(final T value) { - this.value = value; - } - /** * Return the value of an option * @return the option value
--- a/src/jdk/nashorn/internal/runtime/options/OptionTemplate.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/runtime/options/OptionTemplate.java Mon Jun 03 23:24:36 2013 -0700 @@ -25,6 +25,7 @@ package jdk.nashorn.internal.runtime.options; +import java.util.Locale; import java.util.TimeZone; import jdk.nashorn.internal.runtime.QuotedStringTokenizer; @@ -151,6 +152,9 @@ case "timezone": this.defaultValue = TimeZone.getDefault().getID(); break; + case "locale": + this.defaultValue = Locale.getDefault().toLanguageTag(); + break; default: break; } @@ -263,7 +267,7 @@ this.params = arg; break; case "type": - this.type = arg.toLowerCase(); + this.type = arg.toLowerCase(Locale.ENGLISH); break; case "default": this.defaultValue = arg;
--- a/src/jdk/nashorn/internal/runtime/options/Options.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/runtime/options/Options.java Mon Jun 03 23:24:36 2013 -0700 @@ -499,10 +499,10 @@ case "timezone": // default value "TimeZone.getDefault()" return new Option<>(TimeZone.getTimeZone(value)); + case "locale": + return new Option<>(Locale.forLanguageTag(value)); case "keyvalues": return new KeyValueOption(value); - case "values": - return new ValueOption(value); case "log": final KeyValueOption kv = new KeyValueOption(value); Logging.initialize(kv.getValues());
--- a/src/jdk/nashorn/internal/runtime/options/ValueOption.java Thu May 30 10:58:35 2013 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.nashorn.internal.runtime.options; - -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.StringTokenizer; - -/** - * This option represents a collection of comma separated values - */ -public class ValueOption extends Option<String> { - - private Collection<String> values; - - ValueOption(final String value) { - super(value); - if (value != null) { - values = new LinkedHashSet<>(); - final StringTokenizer st = new StringTokenizer(getValue(), ","); - while (st.hasMoreElements()) { - values.add(st.nextToken()); - } - } - } - - /** - * Get the values in the option - * @return collection of strings - */ - public Collection<String> getValues() { - return Collections.unmodifiableCollection(values); - } - -}
--- a/src/jdk/nashorn/internal/runtime/regexp/DefaultRegExp.java Thu May 30 10:58:35 2013 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,163 +0,0 @@ -/* - * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.nashorn.internal.runtime.regexp; - -import jdk.nashorn.internal.runtime.ParserException; - -import static java.util.regex.Pattern.CASE_INSENSITIVE; -import static java.util.regex.Pattern.MULTILINE; -import static java.util.regex.Pattern.UNICODE_CASE; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.regex.PatternSyntaxException; - -/** - * Default regular expression implementation based on java.util.regex package. - * - * Note that this class is not thread-safe as it stores the current match result - * and the string being matched in instance fields. - */ -public class DefaultRegExp extends RegExp { - - /** Java regexp pattern to use for match. We compile to one of these */ - private Pattern pattern; - - /** The matcher */ - private RegExpMatcher matcher; - - /** - * Construct a Regular expression from the given {@code source} and {@code flags} strings. - * - * @param source RegExp source string - * @param flags RegExp flag string - * @throws ParserException if flags is invalid or source string has syntax error. - */ - public DefaultRegExp(final String source, final String flags) throws ParserException { - super(source, flags); - - int intFlags = 0; - - if (isIgnoreCase()) { - intFlags |= CASE_INSENSITIVE | UNICODE_CASE; - } - if (isMultiline()) { - intFlags |= MULTILINE; - } - - try { - RegExpScanner parsed; - - try { - parsed = RegExpScanner.scan(source); - } catch (final PatternSyntaxException e) { - // refine the exception with a better syntax error, if this - // passes, just rethrow what we have - Pattern.compile(source, intFlags); - throw e; - } - - if (parsed != null) { - this.pattern = Pattern.compile(parsed.getJavaPattern(), intFlags); - this.groupsInNegativeLookahead = parsed.getGroupsInNegativeLookahead(); - } - } catch (final PatternSyntaxException e2) { - throwParserException("syntax", e2.getMessage()); - } - } - - @Override - public RegExpMatcher match(final String str) { - if (pattern == null) { - return null; // never matches or similar, e.g. a[] - } - - RegExpMatcher currentMatcher = this.matcher; - - if (currentMatcher == null || matcher.getInput() != str) { - currentMatcher = new DefaultMatcher(str); - this.matcher = currentMatcher; - } - - return currentMatcher; - } - - class DefaultMatcher implements RegExpMatcher { - final String input; - final Matcher defaultMatcher; - - DefaultMatcher(final String input) { - this.input = input; - this.defaultMatcher = pattern.matcher(input); - } - - @Override - public boolean search(final int start) { - return defaultMatcher.find(start); - } - - @Override - public String getInput() { - return input; - } - - @Override - public int start() { - return defaultMatcher.start(); - } - - @Override - public int start(final int group) { - return defaultMatcher.start(group); - } - - @Override - public int end() { - return defaultMatcher.end(); - } - - @Override - public int end(final int group) { - return defaultMatcher.end(group); - } - - @Override - public String group() { - return defaultMatcher.group(); - } - - @Override - public String group(final int group) { - return defaultMatcher.group(group); - } - - @Override - public int groupCount() { - return defaultMatcher.groupCount(); - } - } - -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk/nashorn/internal/runtime/regexp/JdkRegExp.java Mon Jun 03 23:24:36 2013 -0700 @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.nashorn.internal.runtime.regexp; + +import jdk.nashorn.internal.runtime.ParserException; + +import static java.util.regex.Pattern.CASE_INSENSITIVE; +import static java.util.regex.Pattern.MULTILINE; +import static java.util.regex.Pattern.UNICODE_CASE; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +/** + * Default regular expression implementation based on java.util.regex package. + * + * Note that this class is not thread-safe as it stores the current match result + * and the string being matched in instance fields. + */ +public class JdkRegExp extends RegExp { + + /** Java regexp pattern to use for match. We compile to one of these */ + private Pattern pattern; + + /** The matcher */ + private RegExpMatcher matcher; + + /** + * Construct a Regular expression from the given {@code source} and {@code flags} strings. + * + * @param source RegExp source string + * @param flags RegExp flag string + * @throws ParserException if flags is invalid or source string has syntax error. + */ + public JdkRegExp(final String source, final String flags) throws ParserException { + super(source, flags); + + int intFlags = 0; + + if (isIgnoreCase()) { + intFlags |= CASE_INSENSITIVE | UNICODE_CASE; + } + if (isMultiline()) { + intFlags |= MULTILINE; + } + + try { + RegExpScanner parsed; + + try { + parsed = RegExpScanner.scan(source); + } catch (final PatternSyntaxException e) { + // refine the exception with a better syntax error, if this + // passes, just rethrow what we have + Pattern.compile(source, intFlags); + throw e; + } + + if (parsed != null) { + this.pattern = Pattern.compile(parsed.getJavaPattern(), intFlags); + this.groupsInNegativeLookahead = parsed.getGroupsInNegativeLookahead(); + } + } catch (final PatternSyntaxException e2) { + throwParserException("syntax", e2.getMessage()); + } + } + + @Override + public RegExpMatcher match(final String str) { + if (pattern == null) { + return null; // never matches or similar, e.g. a[] + } + + RegExpMatcher currentMatcher = this.matcher; + + if (currentMatcher == null || matcher.getInput() != str) { + currentMatcher = new DefaultMatcher(str); + this.matcher = currentMatcher; + } + + return currentMatcher; + } + + class DefaultMatcher implements RegExpMatcher { + final String input; + final Matcher defaultMatcher; + + DefaultMatcher(final String input) { + this.input = input; + this.defaultMatcher = pattern.matcher(input); + } + + @Override + public boolean search(final int start) { + return defaultMatcher.find(start); + } + + @Override + public String getInput() { + return input; + } + + @Override + public int start() { + return defaultMatcher.start(); + } + + @Override + public int start(final int group) { + return defaultMatcher.start(group); + } + + @Override + public int end() { + return defaultMatcher.end(); + } + + @Override + public int end(final int group) { + return defaultMatcher.end(group); + } + + @Override + public String group() { + return defaultMatcher.group(); + } + + @Override + public String group(final int group) { + return defaultMatcher.group(group); + } + + @Override + public int groupCount() { + return defaultMatcher.groupCount(); + } + } + +}
--- a/src/jdk/nashorn/internal/runtime/regexp/JoniRegExp.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/runtime/regexp/JoniRegExp.java Mon Jun 03 23:24:36 2013 -0700 @@ -113,7 +113,7 @@ public static class Factory extends RegExpFactory { @Override - protected RegExp compile(final String pattern, final String flags) throws ParserException { + public RegExp compile(final String pattern, final String flags) throws ParserException { return new JoniRegExp(pattern, flags); }
--- a/src/jdk/nashorn/internal/runtime/regexp/RegExpFactory.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/runtime/regexp/RegExpFactory.java Mon Jun 03 23:24:36 2013 -0700 @@ -29,7 +29,7 @@ import jdk.nashorn.internal.runtime.options.Options; /** - * Factory class for regular expressions. This class creates instances of {@link DefaultRegExp}. + * Factory class for regular expressions. This class creates instances of {@link JdkRegExp}. * An alternative factory can be installed using the {@code nashorn.regexp.impl} system property. */ public class RegExpFactory { @@ -62,8 +62,8 @@ * @return new RegExp * @throws ParserException if flags is invalid or pattern string has syntax error. */ - protected RegExp compile(final String pattern, final String flags) throws ParserException { - return new DefaultRegExp(pattern, flags); + public RegExp compile(final String pattern, final String flags) throws ParserException { + return new JdkRegExp(pattern, flags); } /**
--- a/src/jdk/nashorn/internal/runtime/regexp/RegExpScanner.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/runtime/regexp/RegExpScanner.java Mon Jun 03 23:24:36 2013 -0700 @@ -868,6 +868,9 @@ * \ ClassEscape */ private boolean classAtomNoDash() { + if (atEOF()) { + return false; + } final int startIn = position; final int startOut = sb.length();
--- a/src/jdk/nashorn/internal/runtime/regexp/joni/Analyser.java Thu May 30 10:58:35 2013 -0700 +++ b/src/jdk/nashorn/internal/runtime/regexp/joni/Analyser.java Mon Jun 03 23:24:36 2013 -0700 @@ -21,10 +21,7 @@ import static jdk.nashorn.internal.runtime.regexp.joni.BitStatus.bsAll; import static jdk.nashorn.internal.runtime.regexp.joni.BitStatus.bsAt; -import static jdk.nashorn.internal.runtime.regexp.joni.BitStatus.bsClear; import static jdk.nashorn.internal.runtime.regexp.joni.BitStatus.bsOnAt; -import static jdk.nashorn.internal.runtime.regexp.joni.BitStatus.bsOnAtSimple; -import static jdk.nashorn.internal.runtime.regexp.joni.Option.isCaptureGroup; import static jdk.nashorn.internal.runtime.regexp.joni.Option.isFindCondition; import static jdk.nashorn.internal.runtime.regexp.joni.Option.isIgnoreCase; import static jdk.nashorn.internal.runtime.regexp.joni.Option.isMultiline; @@ -36,8 +33,6 @@ import jdk.nashorn.internal.runtime.regexp.joni.ast.AnchorNode; import jdk.nashorn.internal.runtime.regexp.joni.ast.BackRefNode; import jdk.nashorn.internal.runtime.regexp.joni.ast.CClassNode; -import jdk.nashorn.internal.runtime.regexp.joni.ast.CTypeNode; -import jdk.nashorn.internal.runtime.regexp.joni.ast.CallNode; import jdk.nashorn.internal.runtime.regexp.joni.ast.ConsAltNode; import jdk.nashorn.internal.runtime.regexp.joni.ast.EncloseNode; import jdk.nashorn.internal.runtime.regexp.joni.ast.Node; @@ -49,9 +44,7 @@ import jdk.nashorn.internal.runtime.regexp.joni.constants.RegexState; import jdk.nashorn.internal.runtime.regexp.joni.constants.StackPopLevel; import jdk.nashorn.internal.runtime.regexp.joni.constants.TargetInfo; -import jdk.nashorn.internal.runtime.regexp.joni.encoding.CharacterType; import jdk.nashorn.internal.runtime.regexp.joni.encoding.ObjPtr; -import jdk.nashorn.internal.runtime.regexp.joni.encoding.Ptr; final class Analyser extends Parser { @@ -74,38 +67,9 @@ //regex.repeatRangeAlloc = 0; regex.repeatRangeLo = null; regex.repeatRangeHi = null; - regex.numCombExpCheck = 0; - - if (Config.USE_COMBINATION_EXPLOSION_CHECK) regex.numCombExpCheck = 0; parse(); - if (Config.USE_NAMED_GROUP) { - /* mixed use named group and no-named group */ - if (env.numNamed > 0 && syntax.captureOnlyNamedGroup() && !isCaptureGroup(regex.options)) { - if (env.numNamed != env.numMem) { - root = disableNoNameGroupCapture(root); - } else { - numberedRefCheck(root); - } - } - } // USE_NAMED_GROUP - - if (Config.USE_NAMED_GROUP) { - if (env.numCall > 0) { - env.unsetAddrList = new UnsetAddrList(env.numCall); - setupSubExpCall(root); - // r != 0 ??? - subexpRecursiveCheckTrav(root); - // r < 0 -< err, FOUND_CALLED_NODE = 1 - subexpInfRecursiveCheckTrav(root); - // r != 0 recursion infinite ??? - regex.numCall = env.numCall; - } else { - regex.numCall = 0; - } - } // USE_NAMED_GROUP - if (Config.DEBUG_PARSE_TREE_RAW && Config.DEBUG_PARSE_TREE) { Config.log.println("<RAW TREE>"); Config.log.println(root + "\n"); @@ -129,27 +93,6 @@ regex.btMemEnd |= regex.captureHistory; } - if (Config.USE_COMBINATION_EXPLOSION_CHECK) { - if (env.backrefedMem == 0 || (Config.USE_SUBEXP_CALL && env.numCall == 0)) { - setupCombExpCheck(root, 0); - - if (Config.USE_SUBEXP_CALL && env.hasRecursion) { - env.numCombExpCheck = 0; - } else { // USE_SUBEXP_CALL - if (env.combExpMaxRegNum > 0) { - for (int i=1; i<env.combExpMaxRegNum; i++) { - if (bsAt(env.backrefedMem, i)) { - env.numCombExpCheck = 0; - break; - } - } - } - } - - } // USE_SUBEXP_CALL - regex.numCombExpCheck = env.numCombExpCheck; - } // USE_COMBINATION_EXPLOSION_CHECK - regex.clearOptimizeInfo(); if (!Config.DONT_OPTIMIZE) setOptimizedInfoFromTree(root); @@ -167,7 +110,6 @@ } if (Config.DEBUG_COMPILE) { - if (Config.USE_NAMED_GROUP) Config.log.print(regex.nameTableToString()); Config.log.println("stack used: " + regex.stackNeeded); if (Config.USE_STRING_TEMPLATES) Config.log.print("templates: " + regex.templateNum + "\n"); Config.log.println(new ByteCodePrinter(regex).byteCodeListToString()); @@ -177,157 +119,6 @@ regex.state = RegexState.NORMAL; } - private void noNameDisableMapFor_cosAlt(Node node, int[]map, Ptr counter) { - ConsAltNode can = (ConsAltNode)node; - do { - can.setCar(noNameDisableMap(can.car, map, counter)); - } while ((can = can.cdr) != null); - } - - private void noNameDisableMapFor_quantifier(Node node, int[]map, Ptr counter) { - QuantifierNode qn = (QuantifierNode)node; - Node target = qn.target; - Node old = target; - target = noNameDisableMap(target, map, counter); - - if (target != old) { - qn.setTarget(target); - if (target.getType() == NodeType.QTFR) qn.reduceNestedQuantifier((QuantifierNode)target); - } - } - - private Node noNameDisableMapFor_enclose(Node node, int[]map, Ptr counter) { - EncloseNode en = (EncloseNode)node; - if (en.type == EncloseType.MEMORY) { - if (en.isNamedGroup()) { - counter.p++; - map[en.regNum] = counter.p; - en.regNum = counter.p; - //en.target = noNameDisableMap(en.target, map, counter); - en.setTarget(noNameDisableMap(en.target, map, counter)); // ??? - } else { - node = en.target; - en.target = null; // remove first enclose: /(a)(?<b>c)/ - node = noNameDisableMap(node, map, counter); - } - } else { - //en.target = noNameDisableMap(en.target, map, counter); - en.setTarget(noNameDisableMap(en.target, map, counter)); // ??? - } - return node; - } - - private void noNameDisableMapFor_anchor(Node node, int[]map, Ptr counter) { - AnchorNode an = (AnchorNode)node; - switch (an.type) { - case AnchorNode.PREC_READ: - case AnchorNode.PREC_READ_NOT: - case AnchorNode.LOOK_BEHIND: - case AnchorNode.LOOK_BEHIND_NOT: - an.setTarget(noNameDisableMap(an.target, map, counter)); - } - } - - private Node noNameDisableMap(Node node, int[]map, Ptr counter) { - switch (node.getType()) { - case NodeType.LIST: - case NodeType.ALT: - noNameDisableMapFor_cosAlt(node, map, counter); - break; - case NodeType.QTFR: - noNameDisableMapFor_quantifier(node, map, counter); - break; - case NodeType.ENCLOSE: - node = noNameDisableMapFor_enclose(node, map, counter); - break; - case NodeType.ANCHOR: - noNameDisableMapFor_anchor(node, map, counter); - break; - } // switch - return node; - } - - private void renumberByMap(Node node, int[]map) { - switch (node.getType()) { - case NodeType.LIST: - case NodeType.ALT: - ConsAltNode can = (ConsAltNode)node; - do { - renumberByMap(can.car, map); - } while ((can = can.cdr) != null); - break; - - case NodeType.QTFR: - renumberByMap(((QuantifierNode)node).target, map); - break; - - case NodeType.ENCLOSE: - renumberByMap(((EncloseNode)node).target, map); - break; - - case NodeType.BREF: - ((BackRefNode)node).renumber(map); - break; - } // switch - } - - protected final void numberedRefCheck(Node node) { - switch (node.getType()) { - case NodeType.LIST: - case NodeType.ALT: - ConsAltNode can = (ConsAltNode)node; - do { - numberedRefCheck(can.car); - } while ((can = can.cdr) != null); - break; - - case NodeType.QTFR: - numberedRefCheck(((QuantifierNode)node).target); - break; - - case NodeType.ENCLOSE: - numberedRefCheck(((EncloseNode)node).target); - break; - - case NodeType.BREF: - BackRefNode br = (BackRefNode)node; - if (!br.isNameRef()) newValueException(ERR_NUMBERED_BACKREF_OR_CALL_NOT_ALLOWED); - break; - } // switch - } - - protected final Node disableNoNameGroupCapture(Node root) { - int[]map = new int[env.numMem + 1]; - - for (int i=1; i<=env.numMem; i++) map[i] = 0; - - root = noNameDisableMap(root, map, new Ptr(0)); - renumberByMap(root, map); - - for (int i=1, pos=1; i<=env.numMem; i++) { - if (map[i] > 0) { - env.memNodes[pos] = env.memNodes[i]; - pos++; - } - } - - int loc = env.captureHistory; - env.captureHistory = bsClear(); - - for (int i=1; i<=Config.MAX_CAPTURE_HISTORY_GROUP; i++) { - if (bsAt(loc, i)) { - env.captureHistory = bsOnAtSimple(env.captureHistory, map[i]); - } - } - - env.numMem = env.numNamed; - regex.numMem = env.numNamed; - - regex.renumberNameTable(map); - - return root; - } - private void swap(Node a, Node b) { a.swap(b); @@ -352,17 +143,6 @@ } while ((can = can.cdr) != null); break; - case NodeType.CALL: - if (Config.USE_SUBEXP_CALL) { - CallNode cn = (CallNode)node; - if (cn.isRecursion()) { - return TargetInfo.IS_EMPTY_REC; /* tiny version */ - } else { - info = quantifiersMemoryInfo(cn.target); - } - } // USE_SUBEXP_CALL - break; - case NodeType.QTFR: QuantifierNode qn = (QuantifierNode)node; if (qn.upper != 0) { @@ -417,18 +197,6 @@ } break; - case NodeType.CALL: - if (Config.USE_SUBEXP_CALL) { - CallNode cn = (CallNode)node; - if (cn.isRecursion()) { - EncloseNode en = (EncloseNode)cn.target; - if (en.isMinFixed()) min = en.minLength; - } else { - min = getMinMatchLength(cn.target); - } - } // USE_SUBEXP_CALL - break; - case NodeType.LIST: ConsAltNode can = (ConsAltNode)node; do { @@ -474,15 +242,13 @@ EncloseNode en = (EncloseNode)node; switch (en.type) { case EncloseType.MEMORY: - if (Config.USE_SUBEXP_CALL) { - if (en.isMinFixed()) { - min = en.minLength; - } else { - min = getMinMatchLength(en.target); - en.minLength = min; - en.setMinFixed(); - } - } // USE_SUBEXP_CALL + if (en.isMinFixed()) { + min = en.minLength; + } else { + min = getMinMatchLength(en.target); + en.minLength = min; + en.setMinFixed(); + } break; case EncloseType.OPTION: @@ -547,17 +313,6 @@ } break; - case NodeType.CALL: - if (Config.USE_SUBEXP_CALL) { - CallNode cn = (CallNode)node; - if (!cn.isRecursion()) { - max = getMaxMatchLength(cn.target); - } else { - max = MinMaxLen.INFINITE_DISTANCE; - } - } // USE_SUBEXP_CALL - break; - case NodeType.QTFR: QuantifierNode qn = (QuantifierNode)node; if (qn.upper != 0) { @@ -576,15 +331,13 @@ EncloseNode en = (EncloseNode)node; switch (en.type) { case EncloseType.MEMORY: - if (Config.USE_SUBEXP_CALL) { - if (en.isMaxFixed()) { - max = en.maxLength; - } else { - max = getMaxMatchLength(en.target); - en.maxLength = max; - en.setMaxFixed(); - } - } // USE_SUBEXP_CALL + if (en.isMaxFixed()) { + max = en.maxLength; + } else { + max = getMaxMatchLength(en.target); + en.maxLength = max; + en.setMaxFixed(); + } break; case EncloseType.OPTION: @@ -663,17 +416,6 @@ } break; - case NodeType.CALL: - if (Config.USE_SUBEXP_CALL) { - CallNode cn = (CallNode)node; - if (!cn.isRecursion()) { - len = getCharLengthTree(cn.target, level); - } else { - returnCode = GET_CHAR_LEN_VARLEN; - } - } // USE_SUBEXP_CALL - break; - case NodeType.CTYPE: len = 1; @@ -686,17 +428,15 @@ EncloseNode en = (EncloseNode)node; switch(en.type) { case EncloseType.MEMORY: - if (Config.USE_SUBEXP_CALL) { - if (en.isCLenFixed()) { - len = en.charLength; - } else { - len = getCharLengthTree(en.target, level); - if (returnCode == 0) { - en.charLength = len; - en.setCLenFixed(); - } + if (en.isCLenFixed()) { + len = en.charLength; + } else { + len = getCharLengthTree(en.target, level); + if (returnCode == 0) { + en.charLength = len; + en.setCLenFixed(); } - } // USE_SUBEXP_CALL + } break; case EncloseType.OPTION: @@ -727,10 +467,6 @@ switch(x.getType()) { case NodeType.CTYPE: switch(yType) { - case NodeType.CTYPE: - CTypeNode cny = (CTypeNode)y; - CTypeNode cnx = (CTypeNode)x; - return cny.ctype == cnx.ctype && cny.not != cnx.not; case NodeType.CCLASS: // !swap:! @@ -756,37 +492,6 @@ CClassNode xc = (CClassNode)x; switch(yType) { - case NodeType.CTYPE: - switch(((CTypeNode)y).ctype) { - case CharacterType.WORD: - if (!((CTypeNode)y).not) { - if (xc.mbuf == null && !xc.isNot()) { - for (int i=0; i<BitSet.SINGLE_BYTE_SIZE; i++) { - if (xc.bs.at(i)) { - if (EncodingHelper.isWord(i)) return false; - } - } - return true; - } - return false; - } else { - for (int i=0; i<BitSet.SINGLE_BYTE_SIZE; i++) { - if (!EncodingHelper.isWord(i)) { - if (!xc.isNot()) { - if (xc.bs.at(i)) return false; - } else { - if (!xc.bs.at(i)) return false; - } - } - } - return true; - } - // break; not reached - - default: - break; - } // inner switch - break; case NodeType.CCLASS: CClassNode yc = (CClassNode)y; @@ -820,17 +525,6 @@ if (xs.length() == 0) break; switch (yType) { - case NodeType.CTYPE: - CTypeNode cy = ((CTypeNode)y); - switch (cy.ctype) { - case CharacterType.WORD: - return !cy.not; - - default: - break; - - } // inner switch - break; case NodeType.CCLASS: CClassNode cc = (CClassNode)y; @@ -873,9 +567,6 @@ case NodeType.CANY: break; - case NodeType.CALL: - break; // if (Config.USE_SUBEXP_CALL) - case NodeType.CTYPE: case NodeType.CCLASS: if (!exact) n = node; @@ -977,316 +668,6 @@ return invalid; } - private static final int RECURSION_EXIST = 1; - private static final int RECURSION_INFINITE = 2; - private int subexpInfRecursiveCheck(Node node, boolean head) { - int r = 0; - - switch (node.getType()) { - case NodeType.LIST: - int min; - ConsAltNode x = (ConsAltNode)node; - do { - int ret = subexpInfRecursiveCheck(x.car, head); - if (ret == RECURSION_INFINITE) return ret; - r |= ret; - if (head) { - min = getMinMatchLength(x.car); - if (min != 0) head = false; - } - } while ((x = x.cdr) != null); - break; - - case NodeType.ALT: - ConsAltNode can = (ConsAltNode)node; - r = RECURSION_EXIST; - do { - int ret = subexpInfRecursiveCheck(can.car, head); - if (ret == RECURSION_INFINITE) return ret; - r &= ret; - } while ((can = can.cdr) != null); - break; - - case NodeType.QTFR: - QuantifierNode qn = (QuantifierNode)node; - r = subexpInfRecursiveCheck(qn.target, head); - if (r == RECURSION_EXIST) { - if (qn.lower == 0) r = 0; - } - break; - - case NodeType.ANCHOR: - AnchorNode an = (AnchorNode)node; - switch (an.type) { - case AnchorType.PREC_READ: - case AnchorType.PREC_READ_NOT: - case AnchorType.LOOK_BEHIND: - case AnchorType.LOOK_BEHIND_NOT: - r = subexpInfRecursiveCheck(an.target, head); - break; - } // inner switch - break; - - case NodeType.CALL: - r = subexpInfRecursiveCheck(((CallNode)node).target, head); - break; - - case NodeType.ENCLOSE: - EncloseNode en = (EncloseNode)node; - if (en.isMark2()) { - return 0; - } else if (en.isMark1()) { - return !head ? RECURSION_EXIST : RECURSION_INFINITE; - // throw exception here ??? - } else { - en.setMark2(); - r = subexpInfRecursiveCheck(en.target, head); - en.clearMark2(); - } - break; - - default: - break; - } // switch - return r; - } - - protected final int subexpInfRecursiveCheckTrav(Node node) { - int r = 0; - - switch (node.getType()) { - case NodeType.LIST: - case NodeType.ALT: - ConsAltNode can = (ConsAltNode)node; - do { - r = subexpInfRecursiveCheckTrav(can.car); - } while (r == 0 && (can = can.cdr) != null); - break; - - case NodeType.QTFR: - r = subexpInfRecursiveCheckTrav(((QuantifierNode)node).target); - break; - - case NodeType.ANCHOR: - AnchorNode an = (AnchorNode)node; - switch (an.type) { - case AnchorType.PREC_READ: - case AnchorType.PREC_READ_NOT: - case AnchorType.LOOK_BEHIND: - case AnchorType.LOOK_BEHIND_NOT: - r = subexpInfRecursiveCheckTrav(an.target); - break; - } // inner switch - break; - - case NodeType.ENCLOSE: - EncloseNode en = (EncloseNode)node; - if (en.isRecursion()) { - en.setMark1(); - r = subexpInfRecursiveCheck(en.target, true); - if (r > 0) newValueException(ERR_NEVER_ENDING_RECURSION); - en.clearMark1(); - } - r = subexpInfRecursiveCheckTrav(en.target); - break; - - default: - break; - } // switch - - return r; - } - - private int subexpRecursiveCheck(Node node) { - int r = 0; - - switch (node.getType()) { - case NodeType.LIST: - case NodeType.ALT: - ConsAltNode can = (ConsAltNode)node; - do { - r |= subexpRecursiveCheck(can.car); - } while ((can = can.cdr) != null); - break; - - case NodeType.QTFR: - r = subexpRecursiveCheck(((QuantifierNode)node).target); - break; - - case NodeType.ANCHOR: - AnchorNode an = (AnchorNode)node; - switch (an.type) { - case AnchorType.PREC_READ: - case AnchorType.PREC_READ_NOT: - case AnchorType.LOOK_BEHIND: - case AnchorType.LOOK_BEHIND_NOT: - r = subexpRecursiveCheck(an.target); - break; - } // inner switch - break; - - case NodeType.CALL: - CallNode cn = (CallNode)node; - r = subexpRecursiveCheck(cn.target); - if (r != 0) cn.setRecursion(); - break; - - case NodeType.ENCLOSE: - EncloseNode en = (EncloseNode)node; - if (en.isMark2()) { - return 0; - } else if (en.isMark1()) { - return 1; /* recursion */ - } else { - en.setMark2(); - r = subexpRecursiveCheck(en.target); - en.clearMark2(); - } - break; - - default: - break; - } // switch - - return r; - } - - private static final int FOUND_CALLED_NODE = 1; - protected final int subexpRecursiveCheckTrav(Node node) { - int r = 0; - - switch (node.getType()) { - case NodeType.LIST: - case NodeType.ALT: - ConsAltNode can = (ConsAltNode)node; - do { - int ret = subexpRecursiveCheckTrav(can.car); - if (ret == FOUND_CALLED_NODE) { - r = FOUND_CALLED_NODE; - } - // else if (ret < 0) return ret; ??? - } while ((can = can.cdr) != null); - break; - - case NodeType.QTFR: - QuantifierNode qn = (QuantifierNode)node; - r = subexpRecursiveCheckTrav(qn.target); - if (qn.upper == 0) { - if (r == FOUND_CALLED_NODE) qn.isRefered = true; - } - break; - - case NodeType.ANCHOR: - AnchorNode an = (AnchorNode)node; - switch (an.type) { - case AnchorType.PREC_READ: - case AnchorType.PREC_READ_NOT: