changeset 306:ddbf41575a2b jdk8-b93

Merge
author lana
date Mon, 03 Jun 2013 23:24:36 -0700
parents 1c7481ac7fe0 4463e94d9b0d
children e169ab024d26 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 &ndash; passing an implementation JavaScript object 
+or function to a constructor, or using <code>Java.extend</code> with <code>new</code> &ndash; all produce classes that take an
+extra JavaScript object parameter in their constructors that specifies the implementation. The implementation is therefore always bound
+to the actual instance being created with <code>new</code>, and not to the whole class. This has some advantages, for example in the
+memory footprint of the runtime, as Nashorn can just create a single "universal adapter" for every combination of types being implemented.
+In reality, the below code shows that different instantiations of, say, <code>Runnable</code> have the same class regardless of them having
+different JavaScript implementation objects:
+</p>
+<pre><code>
+var Runnable = java.lang.Runnable;
+var r1 = new Runnable(function() { print("I'm runnable 1!") })
+var r2 = new Runnable(function() { print("I'm runnable 2!") })
+r1.run()
+r2.run()
+print("We share the same class: " + (r1.class === r2.class))
+</code></pre>
+<p>
+prints:
+</p>
+<pre><code>
+I'm runnable 1!
+I'm runnable 2!
+We share the same class: true
+</code></pre>
+<p>
+Sometimes, however, you'll want to extend a Java class or implement an interface with implementation bound to the class, not to
+its instances. Such a need arises, for example, when you need to pass the class for instantiation to an external API; prime example
+of this is the JavaFX framework where you need to pass an Application class to the FX API and let it instantiate it.
+</p>
+<p>
+Fortunately, there's a solution for that: <code>Java.extend()</code> &ndash; aside from being able to take any number of type parameters
+denoting a class to extend and interfaces to implement &ndash; can also take one last argument that has to be a JavaScript object
+that serves as the implementation for the methods. In this case, <code>Java.extend()</code> will create a class that has the same
+constructors as the original class had, as they don't need to take an an extra implementation object parameter. The example below
+shows how you can create class-bound implementations, and shows that in this case, the implementation classes for different invocations
+are indeed different:
+</p>
+<pre><code>
+var RunnableImpl1 = Java.extend(java.lang.Runnable, function() { print("I'm runnable 1!") })
+var RunnableImpl2 = Java.extend(java.lang.Runnable, function() { print("I'm runnable 2!") })
+var r1 = new RunnableImpl1()
+var r2 = new RunnableImpl2()
+r1.run()
+r2.run()
+print("We share the same class: " + (r1.class === r2.class))
+</code></pre>
+<p>
+prints:
+</p>
+<pre><code>
+I'm runnable 1!
+I'm runnable 2!
+We share the same class: false
+</code></pre>
+<p>
+As you can see, the major difference here is that we moved the implementation object into the invocation of <code>Java.extend</code>
+from the constructor invocations &ndash; indeed the constructor invocations now don't even need to take an extra parameter! Since
+the implementations are bound to a class, the two classes obviously can't be the same, and we indeed see that the two runnables no
+longer share the same class &ndash; every invocation of <code>Java.extend()</code> with a class-specific implementation object triggers
+the creation of a new Java adapter class.
+</p>
+<p>
+Finally, the adapter classes with class-bound implementations can <i>still</i> take an additional constructor parameter to further
+override the behavior on a per-instance basis. Thus, you can even combine the two approaches: you can provide part of the implementation
+in a class-based JavaScript implementation object passed to <code>Java.extend</code>, and part in another object passed to the constructor.
+Whatever functions are provided by the constructor-passed object will override the functions in the class-bound object.
+</p>
+<pre><code>
+var RunnableImpl = Java.extend(java.lang.Runnable, function() { print("I'm runnable 1!") })
+var r1 = new RunnableImpl()
+var r2 = new RunnableImpl(function() { print("I'm runnable 2!") })
+r1.run()
+r2.run()
+print("We share the same class: " + (r1.class === r2.class))
+</code></pre>
+<p>
+prints:
+</p>
+<pre><code>
+I'm runnable 1!
+I'm runnable 2!
+We share the same class: true
+</code></pre>
 <hr>
 <a name="jsoverload" id="jsoverload"></a>
 <h3>Overload Resolution</h3>
 <p>Java methods can be overloaded by argument types. In Java,
 overload resolution occurs at compile time (performed by javac).
-When calling Java methods from a script, the script
-interpreter/compiler needs to select the appropriate method. With
-the JavaScript engine, you do not need to do anything special - the
-correct Java method overload variant is selected based on the
-argument types. But, sometimes you may want (or have) to explicitly
-select a particular overload variant.</p>
+When calling Java methods from Nashorn, the appropriate method will be
+selected based on the argument types at invocation time. You do not need
+to do anything special &ndash; the correct Java method overload variant 
+is selected based automatically. You still have the option of explicitly
+specifying a particular overload variant. Reasons for this include 
+either running into a genuine ambiguity with actual argument types, or 
+rarely reasons of performance &ndash; if you specify the actual overload
+then the engine doesn't have to perform resolution during invocation.
+Individual overloads of a Java methods are exposed as special properties
+with the name of the method followed with its signature in parentheses. 
+You can invoke them like this:</p>
 <pre><code>
 // <a href="source/overload.js">overload.js</a>
 
 var out = java.lang.System.out;
 
 // select a particular print function 
-out["println(java.lang.Object)"]("hello");
+out["println(Object)"]("hello");
 </code>
 </pre>
+<p>
+Note that you normally don't even have to use qualified class names in 
+the signatures as long as the unqualified name of the type is sufficient
+for uniquely identifying the signature. In practice this means that only
+in the extremely unlikely case that two overloads only differ in 
+parameter types that have identical unqualified names but come from 
+different packages would you need to use the fully qualified name of the
+class.
+</p>
+<hr>
+<a name="dataTypeMapping" id="dataTypeMapping"></a>
+<h3>Mapping of Data Types Between Java and JavaScript</h3>
+<p>
+We have previously shown some of the data type mappings between Java and JavaScript.
+We saw that arrays need to be explicitly converted. We have also shown that JavaScript functions
+are automatically converted to SAM types when passed as parameters to Java methods. Most other
+conversions work as you would expect.
+</p>
+<p>
+Every JavaScript object is also a <code>java.util.Map</code> so APIs receiving maps will receive them directly.
+</p>
+<p>
+When numbers are passed to a Java API, they will be converted to the expected target numeric type, either boxed or
+primitive, but if the target type is less specific, say <code>Number</code> or <code>Object</code>, you can only
+count on them being a <code>Number</code>, and have to test specifically for whether it's a boxed <code>Double</code>,
+<code>Integer</code>, <code>Long</code>, etc. &ndash; it can be any of these due to internal optimizations. Also, you 
+can pass any JavaScript value to a Java API expecting either a boxed or primitive number; the JavaScript specification's
+<code>ToNumber</code> conversion algorithm will be applied to the value.
+</p>
+<p>
+In a similar vein, if a Java method expects a <code>String</code> or a <code>Boolean</code>, the values will be
+converted using all conversions allowed by the JavaScript specification's <code>ToString</code> and <code>ToBoolean</code>
+conversions.
+</p>
+<p>
+Finally, a word of caution about strings. Due to internal performance optimizations of string operations, JavaScript strings are
+not always necessarily of type <code>java.lang.String</code>, but they will always be of type <code>java.lang.CharSequence</code>.
+If you pass them to a Java method that expects a <code>java.lang.String</code> parameter, then you will naturally receive a Java
+String, but if the signature of your method is more generic, i.e. it receives a <code>java.lang.Object</code> parameter, you can 
+end up with an object of private engine implementation class that implements <code>CharSequence</code> but is not a Java String.
+</p>
 <hr>
 <a name="engineimpl" id="engineimpl"></a>
 <h2>Implementing Your Own Script Engine</h2>
--- 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 &quot;&quot;"/>
+    </javac>
 
     <!-- tests that check nashorn internals and internal API -->
     <jar jarfile="${nashorn.internal.tests.jar}">
@@ -305,6 +307,8 @@
       <include name="**/codegen/*Test.class"/>
       <include name="**/parser/*Test.class"/>
       <include name="**/runtime/*Test.class"/>
+      <include name="**/runtime/regexp/*Test.class"/>
+      <include name="**/runtime/regexp/joni/*Test.class"/>
       <include name="**/framework/*Test.class"/>
     </fileset>
 
--- 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);
 
+        if (forNode.isForIn()) {
+            enterForIn(forNode);
+        } else {
+            enterFor(forNode);
+        }
+
+        return false;
+    }
+
+    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();
 
-        final Label breakLabel    = forNode.getBreakLabel();
-        final Label continueLabel = forNode.getContinueLabel();
-        final Label loopLabel     = new Label("loop");
+        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();
 
-        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();
+        // 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;
             }
-
-            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);
-        } else {
-            if (init != null) {
-                init.accept(this);
+            @Override
+            protected void evaluate() {
+                method.load(iter);
+                method.invoke(interfaceCallNoLookup(Iterator.class, "next", Object.class));
             }
-
-            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);
-        }
-
-        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();
-            }
-        }
-        return nextSlot;
+        }.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);
+        final long         relativeStart       = JSType.toLong(start);
+        final long         relativeEnd         = (end == ScriptRuntime.UNDEFINED) ? len : JSType.toLong(end);
 
-        long k = relativeStart < 0 ?
-                Math.max(len + relativeStart, 0) :
-                Math.min(
-                    Math.max(relativeStartUint32, relativeStart),
-                    len);
-
-        final double endNum = (end == ScriptRuntime.UNDEFINED)? Double.NaN : JSType.toNumber(end);
-        final long relativeEndUint32 = (end == ScriptRuntime.UNDEFINED)? len : JSType.toUint32(endNum);
-        final long relativeEnd       = (end == ScriptRuntime.UNDEFINED)? len : JSType.toInteger(endNum);
-
-        final long finale = relativeEnd < 0 ?
-                Math.max(len + relativeEnd, 0) :
-                Math.min(
-                    Math.max(relativeEndUint32, relativeEnd),
-                    len);
+        long k = relativeStart < 0 ? Math.max(len + relativeStart, 0) : Math.min(relativeStart, len);
+        final long finale = relativeEnd < 0 ? Math.max(len + relativeEnd, 0) : Math.min(relativeEnd, len);
 
         if (k >= finale) {
             return new NativeArray(0);
@@ -909,21 +895,10 @@
         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);
+        super(property);
 
-        this.getters = new MethodHandle[NOOF_TYPES];
-
-        this.primitiveGetter = bindTo(primitiveGetter, delegate);
-        this.primitiveSetter = bindTo(primitiveSetter, delegate);
-        this.objectGetter    = bindTo(objectGetter, delegate);
-        this.objectSetter    = bindTo(objectSetter, delegate);
+        this.primitiveGetter = bindTo(property.primitiveGetter, delegate);
+        this.primitiveSetter = bindTo(property.primitiveSetter, delegate);
+        this.objectGetter    = bindTo(property.objectGetter, delegate);
+        this.objectSetter    = bindTo(property.objectSetter, delegate);
 
         setCurrentType(property.getCurrentType());
     }
 
     /**
+     * Constructor for spill properties. Array getters and setters will be created on demand.
+     *
+     * @param key    the property key
+     * @param flags  the property flags
+     * @param slot   spill slot
+     */
+    public AccessorProperty(final String key, final int flags, final int slot) {
+        super(key, flags, slot);
+        assert (flags & IS_SPILL) == IS_SPILL;
+
+        setCurrentType(Object.class);
+    }
+
+    /**
      * Constructor. Similar to the constructor with both primitive getters and setters, the difference
      * here being that only one getter and setter (setter is optional for non writable fields) is given
      * 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();
 
-        if (!functionNode.canSpecialize() || !code.isLessSpecificThan(callSiteType)) {
+        final MethodHandle mh = super.getBestInvoker(runtimeType, args);
+
+        /*
+         * Not all functions can be specialized, for example, if we deemed memory
+         * footprint too large to store a parse snapshot, or if it is meaningless
+         * to do so, such as e.g. for runScript
+         */
+        if (!functionNode.canSpecialize()) {
             return mh;
         }
 
-        final FunctionNode snapshot = functionNode.getSnapshot();
-        if (snapshot == null) {
+        /*
+         * Check if best invoker is equally specific or more specific than runtime
+         * type. In that case, we don't need further specialization, but can use
+         * whatever we have already. We know that it will match callSiteType, or it
+         * would not have been returned from getBestInvoker
+         */
+        if (!code.isLessSpecificThan(runtimeType)) {
             return mh;
         }
 
         int i;
+        final FunctionNode snapshot = functionNode.getSnapshot();
+        assert snapshot != null;
 
-        //classes known at runtime
-        final LinkedList<Type> runtimeArgs = new LinkedList<>();
-        for (i = args.length - 1; i >= args.length - snapshot.getParameters().size(); i--) {
-            runtimeArgs.addLast(runtimeType(args[i]));
+        /*
+         * 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.addFirst(Type.typeFor(callSiteType.parameterType(i)));
         }
 
-        //classes known at compile time
-        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)));
+        /*
+         * 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 rparam = Type.typeFor(runtimeType.parameterType(i));
+            final Type cparam = compileTimeArgs.get(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
-        MethodHandle guard = null;
-        for (i = 0; i < compileTimeArgs.size(); i++) {
-            final Type runtimeType = runtimeArgs.get(i);
-            final Type compileType = 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:
-            case AnchorType.LOOK_BEHIND:
-