changeset 149:999cc1bf5520 jdk8-b84

Merge
author lana
date Mon, 01 Apr 2013 21:42:31 -0700
parents fbbdef940138 db8a33cb22b8
children e0378f0a50da 9b845033c888
files src/jdk/nashorn/api/scripting/resources/init.js src/jdk/nashorn/internal/ir/ReferenceNode.java src/jdk/nashorn/internal/ir/annotations/ChildNode.java src/jdk/nashorn/internal/ir/annotations/ParentNode.java src/jdk/nashorn/internal/objects/ScriptFunctionTrampolineImpl.java src/jdk/nashorn/internal/runtime/SpecializedMethodChooser.java test/script/basic/runsunspider.js.EXPECTED test/script/sandbox/reflection.js.EXPECTED test/script/sandbox/unsafe.js.EXPECTED test/src/jdk/nashorn/internal/runtime/Nashorn401TestSubject.java test/src/jdk/nashorn/internal/test/models/ConstructorWithArgument.java test/src/jdk/nashorn/internal/test/models/DessertTopping.java test/src/jdk/nashorn/internal/test/models/DessertToppingFloorWaxDriver.java test/src/jdk/nashorn/internal/test/models/FinalClass.java test/src/jdk/nashorn/internal/test/models/FloorWax.java test/src/jdk/nashorn/internal/test/models/NoAccessibleConstructorClass.java test/src/jdk/nashorn/internal/test/models/NonPublicClass.java test/src/jdk/nashorn/internal/test/models/OuterClass.java test/src/jdk/nashorn/internal/test/models/OverloadedSam.java test/src/jdk/nashorn/internal/test/models/OverrideObject.java test/src/jdk/nashorn/internal/test/models/StringArgs.java test/src/jdk/nashorn/internal/test/models/Toothpaste.java
diffstat 190 files changed, 4873 insertions(+), 4711 deletions(-) [+]
line wrap: on
line diff
--- a/bin/jjs	Thu Mar 28 10:55:17 2013 -0700
+++ b/bin/jjs	Mon Apr 01 21:42:31 2013 -0700
@@ -26,4 +26,4 @@
 
 [ -z "$JAVA_HOME" ] && echo "Please set JAVA_HOME" && exit 1;
 
-$JAVA_HOME/bin/java -server -XX:-TieredCompilation -Xms2G -Xmx2G -esa -ea -Djava.ext.dirs=$JAVA_HOME/jre/lib/ext:`dirname $0`/../dist -XX:+HeapDumpOnOutOfMemoryError -Djava.lang.invoke.MethodHandle.DEBUG_NAMES=false -Dnashorn.debug=true jdk.nashorn.tools.Shell $*
+$JAVA_HOME/bin/java -server -XX:-TieredCompilation -Xms2G -Xmx2G -esa -ea -Djava.ext.dirs=`dirname $0`/../dist:$JAVA_HOME/jre/lib/ext -XX:+HeapDumpOnOutOfMemoryError -Djava.lang.invoke.MethodHandle.DEBUG_NAMES=false -Dnashorn.debug=true jdk.nashorn.tools.Shell $*
--- a/bin/jjssecure	Thu Mar 28 10:55:17 2013 -0700
+++ b/bin/jjssecure	Mon Apr 01 21:42:31 2013 -0700
@@ -26,4 +26,4 @@
 
 [ -z "$JAVA_HOME" ] && echo "Please set JAVA_HOME" && exit 1;
 
-$JAVA_HOME/bin/java -Xms2G -Xmx2G -XX:-TieredCompilation -server -esa -ea -Djava.security.properties=`dirname $0`/../make/java.security.override -Djava.ext.dirs=$JAVA_HOME/jre/lib/ext:`dirname $0`/../dist -XX:+HeapDumpOnOutOfMemoryError -Dnashorn.debug=true -Djava.lang.invoke.MethodHandle.DEBUG_NAMES=true -Dnashorn.home=`dirname $0`/.. -Djava.security.manager jdk.nashorn.tools.Shell $*
+$JAVA_HOME/bin/java -Xms2G -Xmx2G -XX:-TieredCompilation -server -esa -ea -Djava.security.properties=`dirname $0`/../make/java.security.override -Djava.ext.dirs=`dirname $0`/../dist:$JAVA_HOME/jre/lib/ext -XX:+HeapDumpOnOutOfMemoryError -Dnashorn.debug=true -Djava.lang.invoke.MethodHandle.DEBUG_NAMES=true -Dnashorn.home=`dirname $0`/.. -Djava.security.manager jdk.nashorn.tools.Shell $*
--- a/bin/nashorn	Thu Mar 28 10:55:17 2013 -0700
+++ b/bin/nashorn	Mon Apr 01 21:42:31 2013 -0700
@@ -26,4 +26,4 @@
 
 [ -z "$JAVA_HOME" ] && echo "Please set JAVA_HOME" && exit 1;
 
-$JAVA_HOME/bin/jrunscript -J-Xms2G -J-Xmx2G -J-XX:-TieredCompilation -J-server -J-esa -J-ea -J-Djava.ext.dirs=$JAVA_HOME/jre/lib/ext:`dirname $0`/../dist -J-XX:+HeapDumpOnOutOfMemoryError -J-Djava.lang.invoke.MethodHandle.DEBUG_NAMES=false -J-Dnashorn.debug=true -l nashorn $*
+$JAVA_HOME/bin/jrunscript -J-Xms2G -J-Xmx2G -J-XX:-TieredCompilation -J-server -J-esa -J-ea -J-Djava.ext.dirs=`dirname $0`/../dist:$JAVA_HOME/jre/lib/ext -J-XX:+HeapDumpOnOutOfMemoryError -J-Djava.lang.invoke.MethodHandle.DEBUG_NAMES=false -J-Dnashorn.debug=true -l nashorn $*
--- a/bin/nashornsecure	Thu Mar 28 10:55:17 2013 -0700
+++ b/bin/nashornsecure	Mon Apr 01 21:42:31 2013 -0700
@@ -26,4 +26,4 @@
 
 [ -z "$JAVA_HOME" ] && echo "Please set JAVA_HOME" && exit 1;
 
-$JAVA_HOME/bin/jrunscript -J-Djava.security.properties=`dirname $0`/../make/java.security.override -J-Djava.security.manager -J-Xms2G -J-Xmx2G -J-XX:-TieredCompilation -J-server -J-esa -J-ea -J-Djava.ext.dirs=$JAVA_HOME/jre/lib/ext:`dirname $0`/../dist -J-XX:+HeapDumpOnOutOfMemoryError -J-Djava.lang.invoke.MethodHandle.DEBUG_NAMES=false -J-Dnashorn.debug=true -l nashorn $*
+$JAVA_HOME/bin/jrunscript -J-Djava.security.properties=`dirname $0`/../make/java.security.override -J-Djava.security.manager -J-Xms2G -J-Xmx2G -J-XX:-TieredCompilation -J-server -J-esa -J-ea -J-Djava.ext.dirs=`dirname $0`/../dist:$JAVA_HOME/jre/lib/ext -J-XX:+HeapDumpOnOutOfMemoryError -J-Djava.lang.invoke.MethodHandle.DEBUG_NAMES=false -J-Dnashorn.debug=true -l nashorn $*
--- a/docs/DEVELOPER_README	Thu Mar 28 10:55:17 2013 -0700
+++ b/docs/DEVELOPER_README	Mon Apr 01 21:42:31 2013 -0700
@@ -13,6 +13,17 @@
 This documentation of the system property flags assume that the
 default value of the flag is false, unless otherwise specified.
 
+SYSTEM PROPERTY: -Dnashorn.args=<string>
+
+This property takes as its value a space separated list of Nashorn
+command line options that should be passed to Nashorn. This might be useful 
+in environments where it is hard to tell how a nashorn.jar is launched.
+
+Example:
+
+> java -Dnashorn.args="--lazy-complation --log=compiler" large-java-app-with-nashorn.jar 
+> ant -Dnashorn.args="--log=codegen" antjob
+
 SYSTEM PROPERTY: -Dnashorn.unstable.relink.threshold=x
 
 This property controls how many call site misses are allowed before a 
--- a/docs/JavaScriptingProgrammersGuide.html	Thu Mar 28 10:55:17 2013 -0700
+++ b/docs/JavaScriptingProgrammersGuide.html	Mon Apr 01 21:42:31 2013 -0700
@@ -533,9 +533,8 @@
 <hr>
 <a name="jsarrays" id="jsarrays"></a>
 <h3>Creating, Converting and Using Java Arrays</h3>
-<p>While creating a Java object is the same as in Java, to create
-Java arrays in JavaScript we can use Java reflection
-explicitly. But once created the element access or length access is
+<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>
@@ -543,7 +542,8 @@
 // <a href="source/javaarray.js">javaarray.js</a>
 
 // create Java String array of 5 elements
-var a = java.lang.reflect.Array.newInstance(java.lang.String.class, 5);
+var StringArray = Java.type("java.lang.String[]");
+var a = new StringArray(5);
 
 // Accessing elements and length access is by usual Java syntax
 a[0] = "scripting is great!";
--- a/docs/source/javaarray.js	Thu Mar 28 10:55:17 2013 -0700
+++ b/docs/source/javaarray.js	Mon Apr 01 21:42:31 2013 -0700
@@ -30,7 +30,8 @@
  */
 
 // create Java String array of 5 elements
-var a = java.lang.reflect.Array.newInstance(java.lang.String.class, 5);
+var StringArray = Java.type("java.lang.String[]");
+var a = new StringArray(5);
 
 // Accessing elements and length access is by usual Java syntax
 a[0] = "scripting is great!";
--- a/make/build.xml	Thu Mar 28 10:55:17 2013 -0700
+++ b/make/build.xml	Mon Apr 01 21:42:31 2013 -0700
@@ -124,7 +124,7 @@
     <echo message="release=${nashorn.version}" file="${build.classes.dir}/jdk/nashorn/internal/runtime/resources/version.properties" append="true"/>
   </target>
 
-  <target name="jar" depends="compile, run-nasgen" description="Creates nashorn.jar">
+  <target name="jar" depends="compile, run-nasgen, generate-cc-template" description="Creates nashorn.jar">
     <jar jarfile="${dist.jar}" manifest="${meta.inf.dir}/MANIFEST.MF" index="true" filesetmanifest="merge">
       <fileset dir="${build.classes.dir}"/>
       <manifest>
@@ -191,12 +191,12 @@
 
     <!-- tests that check nashorn internals and internal API -->
     <jar jarfile="${nashorn.internal.tests.jar}">
-      <fileset dir="${build.test.classes.dir}" excludes="**/api/*"/>
+      <fileset dir="${build.test.classes.dir}" excludes="**/api/**"/>
     </jar>
 
     <!-- tests that check nashorn script engine (jsr-223) API -->
     <jar jarfile="${nashorn.api.tests.jar}">
-      <fileset dir="${build.test.classes.dir}" includes="**/api/*"/>
+      <fileset dir="${build.test.classes.dir}" includes="**/api/**"/>
     </jar>
 
   </target>
--- a/make/code_coverage.xml	Thu Mar 28 10:55:17 2013 -0700
+++ b/make/code_coverage.xml	Mon Apr 01 21:42:31 2013 -0700
@@ -36,7 +36,12 @@
       <equals arg1="${jcov}" arg2="dynamic" trim="true"/>
     </condition>
 
+    <condition property="cc.generate.template" value="true">
+      <equals arg1="${cc.dynamic.genereate.template}" arg2="true" trim="true"/>
+    </condition>
+
     <mkdir dir="${cc.dir}"/>
+    <mkdir dir="${build.dir}/to_be_instrumented"/>
 
     <!-- info -->
     <echo message="jcov=${jcov}"/>
@@ -51,25 +56,66 @@
     <property name="run.test.cc.jvmargs" value=""/>
   </target>
 
+  <target name="prepare-to-be-instrumented" depends="compile" description="Prepares to_be_instrumented dir">
+    <copy todir="${build.dir}/to_be_instrumented">
+      <fileset dir="${build.classes.dir}">
+        <include name="**/*.class"/>
+        <include name="**/*.clazz"/>
+      </fileset>
+    </copy>
+
+    <move todir="${build.dir}/to_be_instrumented/jdk/nashorn/internal/objects">
+      <fileset dir="${build.dir}/to_be_instrumented/jdk/nashorn/internal/objects">
+        <include name="**/*.clazz"/>
+      </fileset>
+      <mapper type="glob" from="*.clazz" to="*.class"/>
+    </move>
+  </target>
+
+  <target name="generate-cc-template" depends="prepare-to-be-instrumented" description="Generates code coverage template for dynamic CC" if="cc.generate.template">
+    <property name="cc.instrumented.path" location="${build.dir}/to_be_instrumented"/>
+    <java classname="com.sun.tdk.jcov.TmplGen">
+      <arg value="-verbose"/>
+      <arg line="-include ${cc.include}"/>
+      <arg line="-type all"/>
+      <arg line="-template ${cc.template}"/>
+      <arg value="${cc.instrumented.path}"/>
+      <classpath>
+        <pathelement location="${jcov.jar}"/>
+      </classpath>
+    </java>
+
+    <java classname="com.sun.tdk.jcov.RepGen">
+      <arg value="-verbose"/>
+      <arg line="-output ${cc.dir}/CC_template_report"/>
+      <arg value="${cc.template}"/>
+      <classpath>
+        <pathelement location="${jcov.jar}"/>
+      </classpath>
+    </java>
+  </target>
+
   <target name="init-cc" depends="init-cc-disabled, init-cc-enabled">
     <property name="run.test.cc.jvmargs" value=""/>
   </target>
 
   <target name="init-cc-cleanup" if="${cc.enabled}">
     <delete dir="${cc.dir}" failonerror="false" />
+    <delete dir="${build.dir}/to_be_instrumented" failonerror="false" />
   </target>
 
   <target name="check-merging-files" depends="init">
-	<resourcecount property="cc.xmls">
-  		<filelist dir="${cc.dir}" files="*.xml" />
-	</resourcecount>	
+	<echo message="checking avalibility of ${cc.template}"/>
     <condition property="nothing-to-merge" value="true">
-      <equals arg1="${cc.xmls}" arg2="1" trim="true"/>
+		<not>
+	      <available file="${cc.template}"/>
+		</not>
     </condition>
+	<echo message="nothing-to-merge = ${nothing-to-merge}"/>
   </target>
 
   <target name="fix-merging-files" depends="check-merging-files" if="${nothing-to-merge}">
-	<echo message="making pre-merge workaround"/>
+	<echo message="making pre-merge workaround due to missed template"/>
 	<move todir="${cc.dir}" includeemptydirs="false">
 		<fileset dir="${cc.dir}">
 			<include name="*.xml"/>
@@ -81,12 +127,12 @@
   <target name="merge-code-coverage" depends="fix-merging-files" unless="${nothing-to-merge}">
 	<echo message="merging files"/>
     <fileset dir="${cc.dir}" id="cc.xmls">
-      <include name="**/*${jcov}*.xml"/>
+      <include name="**/*_${jcov}_*.xml"/>
       <include name="**/CC_template.xml"/>
     </fileset>
 
     <pathconvert pathsep=" " property="cc.all.xmls" refid="cc.xmls"/>
-
+	<echo message="merging files - ${cc.all.xmls}" />
     <java classname="com.sun.tdk.jcov.Merger">
       <arg value="-verbose"/>
       <arg value="-output"/>
--- a/make/java.security.override	Thu Mar 28 10:55:17 2013 -0700
+++ b/make/java.security.override	Mon Apr 01 21:42:31 2013 -0700
@@ -3,7 +3,7 @@
 # We ensure that by overriding "package.access" security property.
 
 # The following "package.access" value was copied from  default java.security 
-# of jre/lib/security and appended with nashorn IR, Codegen and Parser packages.
+# of jre/lib/security and appended with nashorn sensitive packages.
 
 #
 # List of comma-separated packages that start with or equal this string
@@ -11,4 +11,4 @@
 # passed to checkPackageAccess unless the
 # corresponding RuntimePermission ("accessClassInPackage."+package) has
 # been granted.
-package.access=sun.,com.sun.xml.internal.ws.,com.sun.xml.internal.bind.,com.sun.imageio.,com.sun.org.apache.xerces.internal.utils.,com.sun.org.apache.xalan.internal.utils.,com.sun.org.glassfish.external.,com.sun.org.glassfish.gmbal.,jdk.internal.,jdk.nashorn.internal.ir., jdk.nashorn.internal.codegen., jdk.nashorn.internal.lookup., jdk.nashorn.internal.parser.
+package.access=sun.,com.sun.xml.internal.ws.,com.sun.xml.internal.bind.,com.sun.imageio.,com.sun.org.apache.xerces.internal.utils.,com.sun.org.apache.xalan.internal.utils.,com.sun.org.glassfish.external.,com.sun.org.glassfish.gmbal.,jdk.internal.,jdk.nashorn.internal.,jdk.nashorn.tools.
--- a/make/project.properties	Thu Mar 28 10:55:17 2013 -0700
+++ b/make/project.properties	Mon Apr 01 21:42:31 2013 -0700
@@ -210,7 +210,7 @@
 # add '-Dtest.js.outofprocess' to run each test in a new sub-process
 run.test.jvmargs.main=-server -Xmx${run.test.xmx} -XX:-TieredCompilation -esa -ea -Dnashorn.debug=true -Dfile.encoding=UTF-8
 #-XX:+HeapDumpOnOutOfMemoryError -XX:-UseCompressedKlassPointers -XX:+PrintHeapAtGC -XX:ClassMetaspaceSize=300M  
-run.test.jvmargs.octane.main=-Xms${run.test.xms} ${run.test.jvmargs}
+run.test.jvmargs.octane.main=-Xms${run.test.xms} ${run.test.jvmargs.main}
 
 run.test.jvmsecurityargs=-Xverify:all -Djava.security.properties=${basedir}/make/java.security.override -Djava.security.manager -Djava.security.policy=${basedir}/build/nashorn.policy
 
@@ -235,10 +235,12 @@
 	#naming of CC results
 	#NB directory specified in the cc.dir will be cleaned up!!!
 cc.dir=${basedir}/../Codecoverage_Nashorn
-cc.result.file.name=cc_nashorn.xml
+cc.result.file.name=CC_${jcov}_nashorn.xml
 	#dynamic CC parameters; please redefine in the ${user.home}/.nashorn.project.local.properties
 jcov2.lib.dir=${basedir}/../jcov2/lib
 jcov.jar=${jcov2.lib.dir}/jcov.jar
 cc.include=jdk\.nashorn\.*
 cc.exclude=jdk\.nashorn\.internal\.scripts\.*
+cc.dynamic.genereate.template=true
+cc.template=${cc.dir}/CC_template.xml
 cc.dynamic.args=-javaagent:${jcov.jar}=include=${cc.include},exclude=${cc.exclude},type=all,verbose=0,file=${cc.dir}/${cc.result.file.name}
--- a/src/jdk/nashorn/api/scripting/Formatter.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/api/scripting/Formatter.java	Mon Apr 01 21:42:31 2013 -0700
@@ -46,7 +46,7 @@
  * <p>Pattern and the logic for parameter position: java.util.Formatter
  *
  */
-public final class Formatter {
+final class Formatter {
 
     private Formatter() {
     }
@@ -59,8 +59,8 @@
      * @param args arguments referenced by the format specifiers in format
      * @return a formatted string
      */
-    public static String format(final String format, final Object[] args) {
-        Matcher m = FS_PATTERN.matcher(format);
+    static String format(final String format, final Object[] args) {
+        final Matcher m = FS_PATTERN.matcher(format);
         int positionalParameter = 1;
 
         while (m.find()) {
@@ -143,7 +143,7 @@
     /**
      * Method to parse the integer of the argument index.
      *
-     * @param s
+     * @param s string to parse
      * @return -1 if parsing failed, 0 if string is null, > 0 integer
      */
     private static int index(final String s) {
@@ -166,7 +166,7 @@
      * Method to check if a string contains '&lt;'. This is used to find out if
      * previous parameter is used.
      *
-     * @param s
+     * @param s string to check
      * @return true if '&lt;' is in the string, else false
      */
     private static boolean isPreviousArgument(final String s) {
--- a/src/jdk/nashorn/api/scripting/NashornScriptEngine.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/api/scripting/NashornScriptEngine.java	Mon Apr 01 21:42:31 2013 -0700
@@ -32,6 +32,7 @@
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.Reader;
+import java.lang.reflect.Method;
 import java.net.URL;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
@@ -179,14 +180,14 @@
     }
 
     private <T> T getInterfaceInner(final Object self, final Class<T> clazz) {
-        final Object realSelf;
+        final ScriptObject realSelf;
         final ScriptObject ctxtGlobal = getNashornGlobalFrom(context);
         if(self == null) {
             realSelf = ctxtGlobal;
         } else if (!(self instanceof ScriptObject)) {
-            realSelf = ScriptObjectMirror.unwrap(self, ctxtGlobal);
+            realSelf = (ScriptObject)ScriptObjectMirror.unwrap(self, ctxtGlobal);
         } else {
-            realSelf = self;
+            realSelf = (ScriptObject)self;
         }
         try {
             final ScriptObject oldGlobal = getNashornGlobal();
@@ -194,6 +195,10 @@
                 if(oldGlobal != ctxtGlobal) {
                     setNashornGlobal(ctxtGlobal);
                 }
+
+                if (! isInterfaceImplemented(clazz, realSelf)) {
+                    return null;
+                }
                 return clazz.cast(JavaAdapterFactory.getConstructor(realSelf.getClass(), clazz).invoke(realSelf));
             } finally {
                 if(oldGlobal != ctxtGlobal) {
@@ -394,14 +399,6 @@
             setContextVariables(ctxt);
             final Object val = ctxt.getAttribute(ScriptEngine.FILENAME);
             final String fileName = (val != null) ? val.toString() : "<eval>";
-
-            // NOTE: FIXME: If this is jrunscript's init.js, we want to run the replacement.
-            // This should go away once we fix jrunscript's copy of init.js.
-            if ("<system-init>".equals(fileName)) {
-                evalSupportScript("resources/init.js", "nashorn:engine/resources/init.js");
-                return null;
-            }
-
             Object res = ScriptRuntime.apply(script, ctxtGlobal);
             return ScriptObjectMirror.translateUndefined(ScriptObjectMirror.wrap(res, ctxtGlobal));
         } catch (final Exception e) {
@@ -471,6 +468,21 @@
         }
     }
 
+    private static boolean isInterfaceImplemented(final Class<?> iface, final ScriptObject sobj) {
+        for (final Method method : iface.getMethods()) {
+            // ignore methods of java.lang.Object class
+            if (method.getDeclaringClass() == Object.class) {
+                continue;
+            }
+
+            Object obj = sobj.get(method.getName());
+            if (! (obj instanceof ScriptFunction)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     // don't make this public!!
     static ScriptObject getNashornGlobal() {
         return Context.getGlobal();
--- a/src/jdk/nashorn/api/scripting/NashornScriptEngineFactory.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/api/scripting/NashornScriptEngineFactory.java	Mon Apr 01 21:42:31 2013 -0700
@@ -147,6 +147,7 @@
      * @return newly created script engine.
      */
     public ScriptEngine getScriptEngine(final ClassLoader appLoader) {
+        checkConfigPermission();
         return new NashornScriptEngine(this, appLoader);
     }
 
@@ -157,6 +158,7 @@
      * @return newly created script engine.
      */
     public ScriptEngine getScriptEngine(final String[] args) {
+        checkConfigPermission();
         return new NashornScriptEngine(this, args, getAppClassLoader());
     }
 
@@ -168,11 +170,19 @@
      * @return newly created script engine.
      */
     public ScriptEngine getScriptEngine(final String[] args, final ClassLoader appLoader) {
+        checkConfigPermission();
         return new NashornScriptEngine(this, args, appLoader);
     }
 
     // -- Internals only below this point
 
+    private static void checkConfigPermission() {
+        final SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            sm.checkPermission(new RuntimePermission("nashorn.setConfig"));
+        }
+    }
+
     private static final List<String> names;
     private static final List<String> mimeTypes;
     private static final List<String> extensions;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/api/scripting/ScriptUtils.java	Mon Apr 01 21:42:31 2013 -0700
@@ -0,0 +1,60 @@
+/*
+ * 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;
+
+import jdk.nashorn.internal.runtime.ScriptRuntime;
+
+/**
+ * Utilities that are to be called from script code
+ */
+public final class ScriptUtils {
+    private ScriptUtils() {}
+
+    /**
+     * Returns AST as JSON compatible string. This is used to
+     * implement "parse" function in resources/parse.js script.
+     *
+     * @param code code to be parsed
+     * @param name name of the code source (used for location)
+     * @param includeLoc tells whether to include location information for nodes or not
+     * @return JSON string representation of AST of the supplied code
+     */
+    public static String parse(final String code, final String name, final boolean includeLoc) {
+        return ScriptRuntime.parse(code, name, includeLoc);
+    }
+
+    /**
+     * Method which converts javascript types to java types for the
+     * String.format method (jrunscript function sprintf).
+     *
+     * @param format a format string
+     * @param args arguments referenced by the format specifiers in format
+     * @return a formatted string
+     */
+    public static String format(final String format, final Object[] args) {
+        return Formatter.format(format, args);
+    }
+}
--- a/src/jdk/nashorn/api/scripting/resources/engine.js	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/api/scripting/resources/engine.js	Mon Apr 01 21:42:31 2013 -0700
@@ -46,3 +46,49 @@
     }
     writer.println(String(str));
 }
+
+/**
+ * This is C-like printf
+ *
+ * @param format string to format the rest of the print items
+ * @param args variadic argument list
+ */
+Object.defineProperty(this, "printf", {
+    configurable: true,
+    enumerable: false,
+    writable: true,
+    value: function (format, args/*, more args*/) {
+        print(sprintf.apply(this, arguments));
+    }
+});
+
+/**
+ * This is C-like sprintf
+ *
+ * @param format string to format the rest of the print items
+ * @param args variadic argument list
+ */
+Object.defineProperty(this, "sprintf", {
+    configurable: true,
+    enumerable: false,
+    writable: true,
+    value: function (format, args/*, more args*/) {
+        var len = arguments.length - 1;
+        var array = [];
+
+        if (len < 0) {
+            return "";
+        }
+
+        for (var i = 0; i < len; i++) {
+            if (arguments[i+1] instanceof Date) {
+                array[i] = arguments[i+1].getTime();
+            } else {
+                array[i] = arguments[i+1];
+            }
+        }
+
+        array = Java.toJavaArray(array);
+        return Packages.jdk.nashorn.api.scripting.ScriptUtils.format(format, array);
+    }
+});
--- a/src/jdk/nashorn/api/scripting/resources/init.js	Thu Mar 28 10:55:17 2013 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,939 +0,0 @@
-/*
- * Copyright (c) 2005, 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.
- */
-
-/**
- * jrunscript JavaScript built-in functions and objects.
- */
-
-/**
- * Creates an object that delegates all method calls on
- * it to the 'invoke' method on the given delegate object.<br>
- *
- * Example:
- * <pre>
- * <code>
- *     var x  = { invoke: function(name, args) { //code...}
- *     var y = new JSInvoker(x);
- *     y.func(3, 3); // calls x.invoke('func', args); where args is array of arguments
- * </code>
- * </pre>
- * @param obj object to be wrapped by JSInvoker
- * @constructor
- */
-function JSInvoker(obj) {
-    return new JSAdapter({
-        __get__ : function(name) {
-            return function() {
-                return obj.invoke(name, arguments);
-            }
-        }
-    });
-}
-
-/**
- * This variable represents OS environment. Environment
- * variables can be accessed as fields of this object. For
- * example, env.PATH will return PATH value configured.
- */
-var env = new JSAdapter({
-    __get__ : function (name) {
-        return java.lang.System.getenv(name);
-    },
-    __has__ : function (name) {
-        return java.lang.System.getenv().containsKey(name);
-    },
-    __getIds__ : function() {
-        return java.lang.System.getenv().keySet().toArray();
-    },
-    __delete__ : function(name) {
-        println("can't delete env item");
-    },
-    __put__ : function (name, value) {
-        println("can't change env item");
-    },
-    toString: function() {
-        return java.lang.System.getenv().toString();
-    }
-});
-
-/**
- * Creates a convenient script object to deal with java.util.Map instances.
- * The result script object's field names are keys of the Map. For example,
- * scriptObj.keyName can be used to access value associated with given key.<br>
- * Example:
- * <pre>
- * <code>
- *     var x = java.lang.SystemProperties();
- *     var y = jmap(x);
- *     println(y['java.class.path']); // prints java.class.path System property
- *     delete y['java.class.path']; // remove java.class.path System property
- * </code>
- * </pre>
- *
- * @param map java.util.Map instance that will be wrapped
- * @constructor
- */
-function jmap(map) {
-    return new JSAdapter({
-        __get__ : function(name) {
-            if (map.containsKey(name)) {
-                return map.get(name);
-            } else {
-                return undefined;
-            }
-        },
-        __has__ :  function(name) {
-            return map.containsKey(name);
-        },
-
-        __delete__ : function (name) {
-            return map.remove(name);
-        },
-        __put__ : function(name, value) {
-            map.put(name, value);
-        },
-        __getIds__ : function() {
-            return map.keySet().toArray();
-        },
-        toString: function() {
-            return map.toString();
-        }
-    });
-}
-
-/**
- * Creates a convenient script object to deal with java.util.List instances.
- * The result script object behaves like an array. For example,
- * scriptObj[index] syntax can be used to access values in the List instance.
- * 'length' field gives size of the List. <br>
- *
- * Example:
- * <pre>
- * <code>
- *    var x = new java.util.ArrayList(4);
- *    x.add('Java');
- *    x.add('JavaScript');
- *    x.add('SQL');
- *    x.add('XML');
- *
- *    var y = jlist(x);
- *    println(y[2]); // prints third element of list
- *    println(y.length); // prints size of the list
- *
- * @param map java.util.List instance that will be wrapped
- * @constructor
- */
-function jlist(list) {
-    function isValid(index) {
-        return typeof(index) == 'number' &&
-            index > -1 && index < list.size();
-    }
-    return new JSAdapter({
-        __get__ :  function(name) {
-            if (isValid(name)) {
-                return list.get(name);
-            } else if (name == 'length') {
-                return list.size();
-            } else {
-                return undefined;
-            }
-        },
-        __has__ : function (name) {
-            return isValid(name) || name == 'length';
-        },
-        __delete__ : function(name) {
-            if (isValid(name)) {
-                list.remove(name);
-            }
-        },
-        __put__ : function(name, value) {
-            if (isValid(name)) {
-                list.set(name, value);
-            }
-        },
-        __getIds__: function() {
-            var res = new Array(list.size());
-            for (var i = 0; i < res.length; i++) {
-                res[i] = i;
-            }
-            return res;
-        },
-        toString: function() {
-            return list.toString();
-        }
-    });
-}
-
-/**
- * This is java.lang.System properties wrapped by JSAdapter.
- * For eg. to access java.class.path property, you can use
- * the syntax sysProps["java.class.path"]
- */
-var sysProps = new JSAdapter({
-    __get__ : function (name) {
-        return java.lang.System.getProperty(name);
-    },
-    __has__ : function (name) {
-        return java.lang.System.getProperty(name) != null;
-    },
-    __getIds__ : function() {
-        return java.lang.System.getProperties().keySet().toArray();
-    },
-    __delete__ : function(name) {
-        java.lang.System.clearProperty(name);
-        return true;
-    },
-    __put__ : function (name, value) {
-        java.lang.System.setProperty(name, value);
-    },
-    toString: function() {
-        return "<system properties>";
-    }
-});
-
-// stdout, stderr & stdin
-var out = java.lang.System.out;
-var err = java.lang.System.err;
-// can't use 'in' because it is a JavaScript keyword :-(
-var inp = java.lang.System["in"];
-
-var BufferedInputStream = java.io.BufferedInputStream;
-var BufferedOutputStream = java.io.BufferedOutputStream;
-var BufferedReader = java.io.BufferedReader;
-var DataInputStream = java.io.DataInputStream;
-var File = java.io.File;
-var FileInputStream = java.io.FileInputStream;
-var FileOutputStream = java.io.FileOutputStream;
-var InputStream = java.io.InputStream;
-var InputStreamReader = java.io.InputStreamReader;
-var OutputStream = java.io.OutputStream;
-var Reader = java.io.Reader;
-var URL = java.net.URL;
-
-/**
- * Generic any object to input stream mapper
- * @param str input file name, URL or InputStream
- * @return InputStream object
- * @private
- */
-function inStream(str) {
-    if (typeof(str) == "string") {
-        // '-' means standard input
-        if (str == '-') {
-            return java.lang.System["in"];
-        }
-        // try file first
-        var file = null;
-        try {
-            file = pathToFile(str);
-        } catch (e) {
-        }
-        if (file && file.exists()) {
-            return new FileInputStream(file);
-        } else {
-            try {
-                // treat the string as URL
-                return new URL(str).openStream();
-            } catch (e) {
-                throw 'file or URL ' + str + ' not found';
-            }
-        }
-    } else {
-        if (str instanceof InputStream) {
-            return str;
-        } else if (str instanceof URL) {
-            return str.openStream();
-        } else if (str instanceof File) {
-            return new FileInputStream(str);
-        }
-    }
-    // everything failed, just give input stream
-    return java.lang.System["in"];
-}
-
-/**
- * Generic any object to output stream mapper
- *
- * @param out output file name or stream
- * @return OutputStream object
- * @private
- */
-function outStream(out) {
-    if (typeof(out) == "string") {
-        if (out == '>') {
-            return java.lang.System.out;
-        } else {
-            // treat it as file
-            return new FileOutputStream(pathToFile(out));
-        }
-    } else {
-        if (out instanceof OutputStream) {
-            return out;
-        } else if (out instanceof File) {
-            return new FileOutputStream(out);
-        }
-    }
-
-    // everything failed, just return System.out
-    return java.lang.System.out;
-}
-
-/**
- * stream close takes care not to close stdin, out & err.
- * @private
- */
-function streamClose(stream) {
-    if (stream) {
-        if (stream != java.lang.System["in"] &&
-            stream != java.lang.System.out &&
-            stream != java.lang.System.err) {
-            try {
-                stream.close();
-            } catch (e) {
-                println(e);
-            }
-        }
-    }
-}
-
-/**
- * Loads and evaluates JavaScript code from a stream or file or URL<br>
- *
- * Examples:
- * <pre>
- * <code>
- *    load('test.js'); // load script file 'test.js'
- *    load('http://java.sun.com/foo.js'); // load from a URL
- * </code>
- * </pre>
- *
- * @param str input from which script is loaded and evaluated
- */
-if (typeof(load) == 'undefined') {
-    var load = function(str) {
-        var stream = inStream(str);
-        var bstream = new BufferedInputStream(stream);
-        var reader = new BufferedReader(new InputStreamReader(bstream));
-        var oldFilename = engine.get(engine.FILENAME);
-        engine.put(engine.FILENAME, str);
-        try {
-            engine.eval(reader);
-        } finally {
-            engine.put(engine.FILENAME, oldFilename);
-            streamClose(stream);
-        }
-    }
-}
-
-// file system utilities
-
-/**
- * Creates a Java byte[] of given length
- * @param len size of the array to create
- * @private
- */
-function javaByteArray(len) {
-    return java.lang.reflect.Array.newInstance(java.lang.Byte.TYPE, len);
-}
-
-var curDir = new File('.');
-
-/**
- * Print present working directory
- */
-function pwd() {
-    println(curDir.getAbsolutePath());
-}
-
-/**
- * Changes present working directory to given directory
- * @param target directory to change to. optional, defaults to user's HOME
- */
-function cd(target) {
-    if (target == undefined) {
-        target = sysProps["user.home"];
-    }
-    if (!(target instanceof File)) {
-        target = pathToFile(target);
-    }
-    if (target.exists() && target.isDirectory()) {
-        curDir = target;
-    } else {
-        println(target + " is not a directory");
-    }
-}
-
-/**
- * Converts path to java.io.File taking care of shell present working dir
- *
- * @param pathname file path to be converted
- * @private
- */
-function pathToFile(pathname) {
-    var tmp = pathname;
-    if (!(tmp instanceof File)) {
-        tmp = new File(tmp);
-    }
-    if (!tmp.isAbsolute()) {
-        return new File(curDir, pathname);
-    } else {
-        return tmp;
-    }
-}
-
-/**
- * Copies a file or URL or stream to another file or stream
- *
- * @param from input file or URL or stream
- * @param to output stream or file
- */
-function cp(from, to) {
-    if (from == to) {
-        println("file " + from + " cannot be copied onto itself!");
-        return;
-    }
-    var inp = inStream(from);
-    var out = outStream(to);
-    var binp = new BufferedInputStream(inp);
-    var bout = new BufferedOutputStream(out);
-    var buff = javaByteArray(1024);
-    var len;
-    while ((len = binp.read(buff)) > 0 )
-        bout.write(buff, 0, len);
-
-    bout.flush();
-    streamClose(inp);
-    streamClose(out);
-}
-
-/**
- * Shows the content of a file or URL or any InputStream<br>
- * Examples:
- * <pre>
- * <code>
- *    cat('test.txt'); // show test.txt file contents
- *    cat('http://java.net'); // show the contents from the URL http://java.net
- * </code>
- * </pre>
- * @param obj input to show
- * @param pattern optional. show only the lines matching the pattern
- */
-function cat(obj, pattern) {
-    if (obj instanceof File && obj.isDirectory()) {
-        ls(obj);
-        return;
-    }
-
-    var inp = null;
-    if (!(obj instanceof Reader)) {
-        inp = inStream(obj);
-        obj = new BufferedReader(new InputStreamReader(inp));
-    }
-    var line;
-    if (pattern) {
-        var count = 1;
-        while ((line=obj.readLine()) != null) {
-            if (line.match(pattern)) {
-                println(count + "\t: " + line);
-            }
-            count++;
-        }
-    } else {
-        while ((line=obj.readLine()) != null) {
-            println(line);
-        }
-    }
-}
-
-/**
- * Returns directory part of a filename
- *
- * @param pathname input path name
- * @return directory part of the given file name
- */
-function dirname(pathname) {
-    var dirName = ".";
-    // Normalize '/' to local file separator before work.
-    var i = pathname.replace('/', File.separatorChar ).lastIndexOf(
-        File.separator );
-    if ( i != -1 )
-        dirName = pathname.substring(0, i);
-    return dirName;
-}
-
-/**
- * Creates a new dir of given name
- *
- * @param dir name of the new directory
- */
-function mkdir(dir) {
-    dir = pathToFile(dir);
-    println(dir.mkdir()? "created" : "can not create dir");
-}
-
-/**
- * Creates the directory named by given pathname, including
- * any necessary but nonexistent parent directories.
- *
- * @param dir input path name
- */
-function mkdirs(dir) {
-    dir = pathToFile(dir);
-    println(dir.mkdirs()? "created" : "can not create dirs");
-}
-
-/**
- * Removes a given file
- *
- * @param pathname name of the file
- */
-function rm(pathname) {
-    var file = pathToFile(pathname);
-    if (!file.exists()) {
-        println("file not found: " + pathname);
-        return false;
-    }
-    // note that delete is a keyword in JavaScript!
-    println(file["delete"]()? "deleted" : "can not delete");
-}
-
-/**
- * Removes a given directory
- *
- * @param pathname name of the directory
- */
-function rmdir(pathname) {
-    rm(pathname);
-}
-
-/**
- * Synonym for 'rm'
- */
-function del(pathname) {
-    rm(pathname);
-}
-
-/**
- * Moves a file to another
- *
- * @param from original name of the file
- * @param to new name for the file
- */
-function mv(from, to) {
-    println(pathToFile(from).renameTo(pathToFile(to))?
-        "moved" : "can not move");
-}
-
-/**
- * Synonym for 'mv'.
- */
-function ren(from, to) {
-    mv(from, to);
-}
-
-var months = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
-        "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ];
-
-/**
- * Helper function called by ls
- * @private
- */
-function printFile(f) {
-    var sb = new java.lang.StringBuffer();
-    sb.append(f.isDirectory()? "d" : "-");
-    sb.append(f.canRead() ? "r": "-" );
-    sb.append(f.canWrite() ? "w": "-" );
-    sb.append(" ");
-
-    var d = new java.util.Date(f.lastModified());
-    var c = new java.util.GregorianCalendar();
-    c.setTime(d);
-    var day    = c.get(java.util.Calendar.DAY_OF_MONTH);
-    sb.append(months[c.get(java.util.Calendar.MONTH)]
-         + " " + day );
-    if (day < 10) {
-        sb.append(" ");
-    }
-
-    // to get fixed length 'length' field
-    var fieldlen = 8;
-    var len = new java.lang.StringBuffer();
-    for(var j=0; j<fieldlen; j++)
-        len.append(" ");
-    len.insert(0, java.lang.Long.toString(f.length()));
-    len.setLength(fieldlen);
-    // move the spaces to the front
-    var si = len.toString().indexOf(" ");
-    if ( si != -1 ) {
-        var pad = len.toString().substring(si);
-        len.setLength(si);
-        len.insert(0, pad);
-    }
-    sb.append(len.toString());
-    sb.append(" ");
-    sb.append(f.getName());
-    if (f.isDirectory()) {
-        sb.append('/');
-    }
-    println(sb.toString());
-}
-
-/**
- * Lists the files in a directory
- *
- * @param dir directory from which to list the files. optional, default to pwd
- * @param filter pattern to filter the files listed. optional, default is '.'.
- */
-function ls(dir, filter) {
-    if (dir) {
-        dir = pathToFile(dir);
-    } else {
-        dir = curDir;
-    }
-    if (dir.isDirectory()) {
-        var files = dir.listFiles();
-        for (var i in files) {
-            var f = files[i];
-            if (filter) {
-                if(!f.getName().match(filter)) {
-                    continue;
-                }
-            }
-            printFile(f);
-        }
-    } else {
-        printFile(dir);
-    }
-}
-
-/**
- * Synonym for 'ls'.
- */
-function dir(d, filter) {
-    ls(d, filter);
-}
-
-/**
- * Unix-like grep, but accepts JavaScript regex patterns
- *
- * @param pattern to search in files
- * @param files one or more files
- */
-function grep(pattern, files /*, one or more files */) {
-    if (arguments.length < 2) return;
-    for (var i = 1; i < arguments.length; i++) {
-        println(arguments[i] + ":");
-        cat(arguments[i], pattern);
-    }
-}
-
-/**
- * Find in files. Calls arbitrary callback function
- * for each matching file.<br>
- *
- * Examples:
- * <pre>
- * <code>
- *    find('.')
- *    find('.', '.*\.class', rm);  // remove all .class files
- *    find('.', '.*\.java');       // print fullpath of each .java file
- *    find('.', '.*\.java', cat);  // print all .java files
- * </code>
- * </pre>
- *
- * @param dir directory to search files
- * @param pattern to search in the files
- * @param callback function to call for matching files
- */
-function find(dir, pattern, callback) {
-    dir = pathToFile(dir);
-    if (!callback) callback = print;
-    var files = dir.listFiles();
-    for (var f in files) {
-        var file = files[f];
-        if (file.isDirectory()) {
-            find(file, pattern, callback);
-        } else {
-            if (pattern) {
-                if (file.getName().match(pattern)) {
-                    callback(file);
-                }
-            } else {
-                callback(file);
-            }
-        }
-    }
-}
-
-// process utilities
-
-/**
- * Exec's a child process, waits for completion &amp; returns exit code
- *
- * @param cmd command to execute in child process
- */
-function exec(cmd) {
-    var process = java.lang.Runtime.getRuntime().exec(cmd);
-    var inp = new DataInputStream(process.getInputStream());
-    var line = null;
-    while ((line = inp.readLine()) != null) {
-        println(line);
-    }
-    process.waitFor();
-    $exit = process.exitValue();
-}
-
-// XML utilities
-
-/**
- * Converts input to DOM Document object
- *
- * @param inp file or reader. optional, without this param,
- * this function returns a new DOM Document.
- * @return returns a DOM Document object
- */
-function XMLDocument(inp) {
-    var factory = javax.xml.parsers.DocumentBuilderFactory.newInstance();
-    var builder = factory.newDocumentBuilder();
-    if (inp) {
-        if (typeof(inp) == "string") {
-            return builder.parse(pathToFile(inp));
-        } else {
-            return builder.parse(inp);
-        }
-    } else {
-        return builder.newDocument();
-    }
-}
-
-/**
- * Converts arbitrary stream, file, URL to XMLSource
- *
- * @param inp input stream or file or URL
- * @return XMLSource object
- */
-function XMLSource(inp) {
-    if (inp instanceof javax.xml.transform.Source) {
-        return inp;
-    } else if (inp instanceof Packages.org.w3c.dom.Document) {
-        return new javax.xml.transform.dom.DOMSource(inp);
-    } else {
-        inp = new BufferedInputStream(inStream(inp));
-        return new javax.xml.transform.stream.StreamSource(inp);
-    }
-}
-
-/**
- * Converts arbitrary stream, file to XMLResult
- *
- * @param inp output stream or file
- * @return XMLResult object
- */
-function XMLResult(out) {
-    if (out instanceof javax.xml.transform.Result) {
-        return out;
-    } else if (out instanceof Packages.org.w3c.dom.Document) {
-        return new javax.xml.transform.dom.DOMResult(out);
-    } else {
-        out = new BufferedOutputStream(outStream(out));
-        return new javax.xml.transform.stream.StreamResult(out);
-    }
-}
-
-/**
- * Perform XSLT transform
- *
- * @param inp Input XML to transform (URL, File or InputStream)
- * @param style XSL Stylesheet to be used (URL, File or InputStream). optional.
- * @param out Output XML (File or OutputStream
- */
-function XSLTransform(inp, style, out) {
-    switch (arguments.length) {
-    case 2:
-        inp = arguments[0];
-        out = arguments[1];
-        break;
-    case 3:
-        inp = arguments[0];
-        style = arguments[1];
-        out = arguments[2];
-        break;
-    default:
-        println("XSL tranform requires 2 or 3 arguments");
-        return;
-    }
-
-    var factory = javax.xml.transform.TransformerFactory.newInstance();
-    var transformer;
-    if (style) {
-        transformer = factory.newTransformer(XMLSource(style));
-    } else {
-        transformer = factory.newTransformer();
-    }
-    var source = XMLSource(inp);
-    var result = XMLResult(out);
-    transformer.transform(source, result);
-    if (source.getInputStream) {
-        streamClose(source.getInputStream());
-    }
-    if (result.getOutputStream) {
-        streamClose(result.getOutputStream());
-    }
-}
-
-// miscellaneous utilities
-
-/**
- * Prints which command is selected from PATH
- *
- * @param cmd name of the command searched from PATH
- */
-function which(cmd) {
-    var st = new java.util.StringTokenizer(env.PATH, File.pathSeparator);
-    while (st.hasMoreTokens()) {
-        var file = new File(st.nextToken(), cmd);
-        if (file.exists()) {
-            println(file.getAbsolutePath());
-            return;
-        }
-    }
-}
-
-/**
- * Prints IP addresses of given domain name
- *
- * @param name domain name
- */
-function ip(name) {
-    var addrs = InetAddress.getAllByName(name);
-    for (var i in addrs) {
-        println(addrs[i]);
-    }
-}
-
-/**
- * Prints current date in current locale
- */
-function date() {
-    println(new Date().toLocaleString());
-}
-
-/**
- * Echoes the given string arguments
- */
-function echo(x) {
-    for (var i = 0; i < arguments.length; i++) {
-        println(arguments[i]);
-    }
-}
-
-/**
- * Reads one or more lines from stdin after printing prompt
- *
- * @param prompt optional, default is '>'
- * @param multiline to tell whether to read single line or multiple lines
- */
-function read(prompt, multiline) {
-    if (!prompt) {
-        prompt = '>';
-    }
-    var inp = java.lang.System["in"];
-    var reader = new BufferedReader(new InputStreamReader(inp));
-    if (multiline) {
-        var line = '';
-        while (true) {
-            java.lang.System.err.print(prompt);
-            java.lang.System.err.flush();
-            var tmp = reader.readLine();
-            if (tmp == '' || tmp == null) break;
-            line += tmp + '\n';
-        }
-        return line;
-    } else {
-        java.lang.System.err.print(prompt);
-        java.lang.System.err.flush();
-        return reader.readLine();
-    }
-}
-
-if (typeof(println) == 'undefined') {
-    var print = function(str, newline) {
-        if (typeof(str) == 'undefined') {
-            str = 'undefined';
-        } else if (str == null) {
-            str = 'null';
-        }
-
-        if (!(out instanceof java.io.PrintWriter)) {
-            out = new java.io.PrintWriter(out);
-        }
-
-        out.print(String(str));
-        if (newline) {
-            out.print('\n');
-        }
-        out.flush();
-    }
-
-    var println = function(str) {
-        print(str, true);
-    };
-}
-
-/**
- * This is C-like printf
- *
- * @param format string to format the rest of the print items
- * @param args variadic argument list
- */
-function printf(format, args/*, more args*/) {
-    print(sprintf.apply(this, arguments));
-}
-
-/**
- * This is C-like sprintf
- *
- * @param format string to format the rest of the print items
- * @param args variadic argument list
- */
-function sprintf(format, args/*, more args*/) {
-    var len = arguments.length - 1;
-    var array = [];
-
-    if (len < 0) {
-        return "";
-    }
-
-    for (var i = 0; i < len; i++) {
-        if (arguments[i+1] instanceof Date) {
-            array[i] = arguments[i+1].getTime();
-        } else {
-            array[i] = arguments[i+1];
-        }
-    }
-
-    array = Java.toJavaArray(array);
-    return Packages.jdk.nashorn.api.scripting.Formatter.format(format, array);
-}
--- a/src/jdk/nashorn/internal/codegen/Attr.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/codegen/Attr.java	Mon Apr 01 21:42:31 2013 -0700
@@ -37,13 +37,16 @@
 import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL;
 import static jdk.nashorn.internal.ir.Symbol.IS_LET;
 import static jdk.nashorn.internal.ir.Symbol.IS_PARAM;
+import static jdk.nashorn.internal.ir.Symbol.IS_SCOPE;
 import static jdk.nashorn.internal.ir.Symbol.IS_THIS;
 import static jdk.nashorn.internal.ir.Symbol.IS_VAR;
+import static jdk.nashorn.internal.ir.Symbol.KINDMASK;
 
 import java.util.ArrayList;
 import java.util.HashSet;
-import java.util.LinkedList;
+import java.util.Iterator;
 import java.util.List;
+import java.util.ListIterator;
 import java.util.Set;
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.AccessNode;
@@ -55,14 +58,15 @@
 import jdk.nashorn.internal.ir.CatchNode;
 import jdk.nashorn.internal.ir.ForNode;
 import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
 import jdk.nashorn.internal.ir.IdentNode;
 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.Node;
 import jdk.nashorn.internal.ir.ObjectNode;
 import jdk.nashorn.internal.ir.PropertyNode;
-import jdk.nashorn.internal.ir.ReferenceNode;
 import jdk.nashorn.internal.ir.ReturnNode;
 import jdk.nashorn.internal.ir.RuntimeNode;
 import jdk.nashorn.internal.ir.RuntimeNode.Request;
@@ -115,6 +119,8 @@
      */
     private Set<String> localUses;
 
+    private final LexicalContext lexicalContext = new LexicalContext();
+
     private static final DebugLogger LOG   = new DebugLogger("attr");
     private static final boolean     DEBUG = LOG.isEnabled();
 
@@ -135,14 +141,15 @@
     }
 
     @Override
-    public Node leave(final AccessNode accessNode) {
+    public Node leaveAccessNode(final AccessNode accessNode) {
         newTemporary(Type.OBJECT, accessNode);  //While Object type is assigned here, Access Specialization in FinalizeTypes may narrow this
         end(accessNode);
         return accessNode;
     }
 
     @Override
-    public Node enter(final Block block) {
+    public Node enterBlock(final Block block) {
+        lexicalContext.push(block);
         start(block);
 
         final Set<String> savedLocalDefs = localDefs;
@@ -158,9 +165,7 @@
             localDefs = new HashSet<>(savedLocalDefs);
             localUses = new HashSet<>(savedLocalUses);
 
-            for (final Node statement : block.getStatements()) {
-                statement.accept(this);
-            }
+            block.visitStatements(this);
         } finally {
             localDefs = savedLocalDefs;
             localUses = savedLocalUses;
@@ -170,11 +175,12 @@
 
         end(block);
 
+        lexicalContext.pop(block);
         return null;
     }
 
     @Override
-    public Node enter(final CallNode callNode) {
+    public Node enterCallNode(final CallNode callNode) {
         start(callNode);
 
         callNode.getFunction().accept(this);
@@ -195,8 +201,7 @@
             evalArgs.setThis(thisNode);
         }
 
-        newTemporary(Type.OBJECT, callNode); // object type here, access specialization in FinalizeTypes may narrow it later
-        newType(callNode.getFunction().getSymbol(), Type.OBJECT);
+        newTemporary(callNode.getType(), callNode); // access specialization in FinalizeTypes may narrow it further later
 
         end(callNode);
 
@@ -204,29 +209,106 @@
     }
 
     @Override
-    public Node enter(final CatchNode catchNode) {
+    public Node enterCatchNode(final CatchNode catchNode) {
         final IdentNode exception = catchNode.getException();
         final Block     block     = getCurrentBlock();
 
         start(catchNode);
 
         // define block-local exception variable
-        final Symbol def = block.defineSymbol(exception.getName(), IS_VAR | IS_LET, exception);
+        final Symbol def = defineSymbol(block, exception.getName(), IS_VAR | IS_LET, exception);
         newType(def, Type.OBJECT);
         addLocalDef(exception.getName());
 
         return catchNode;
     }
 
+    /**
+     * Declare the definition of a new symbol.
+     *
+     * @param name         Name of symbol.
+     * @param symbolFlags  Symbol flags.
+     * @param node         Defining Node.
+     *
+     * @return Symbol for given name or null for redefinition.
+     */
+    private Symbol defineSymbol(final Block block, final String name, final int symbolFlags, final Node node) {
+        int    flags  = symbolFlags;
+        Symbol symbol = findSymbol(block, name); // Locate symbol.
+
+        if ((flags & KINDMASK) == IS_GLOBAL) {
+            flags |= IS_SCOPE;
+        }
+
+        final FunctionNode function = lexicalContext.getFunction(block);
+        if (symbol != null) {
+            // Symbol was already defined. Check if it needs to be redefined.
+            if ((flags & KINDMASK) == IS_PARAM) {
+                if (!isLocal(function, symbol)) {
+                    // Not defined in this function. Create a new definition.
+                    symbol = null;
+                } else if (symbol.isParam()) {
+                    // Duplicate parameter. Null return will force an error.
+                    assert false : "duplicate parameter";
+                    return null;
+                }
+            } else if ((flags & KINDMASK) == IS_VAR) {
+                if ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET) {
+                    assert !((flags & IS_LET) == IS_LET && symbol.getBlock() == block) : "duplicate let variable in block";
+                    // Always create a new definition.
+                    symbol = null;
+                } else {
+                    // Not defined in this function. Create a new definition.
+                    if (!isLocal(function, symbol) || symbol.less(IS_VAR)) {
+                        symbol = null;
+                    }
+                }
+            }
+        }
+
+        if (symbol == null) {
+            // If not found, then create a new one.
+            Block symbolBlock;
+
+            // Determine where to create it.
+            if ((flags & Symbol.KINDMASK) == IS_VAR && ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET)) {
+                symbolBlock = block;
+            } else {
+                symbolBlock = function;
+            }
+
+            // Create and add to appropriate block.
+            symbol = new Symbol(name, flags, node, symbolBlock);
+            symbolBlock.putSymbol(name, symbol);
+
+            if ((flags & Symbol.KINDMASK) != IS_GLOBAL) {
+                symbolBlock.getFrame().addSymbol(symbol);
+                symbol.setNeedsSlot(true);
+            }
+        } else if (symbol.less(flags)) {
+            symbol.setFlags(flags);
+        }
+
+        if (node != null) {
+            node.setSymbol(symbol);
+        }
+
+        return symbol;
+    }
+
     @Override
-    public Node enter(final FunctionNode functionNode) {
+    public Node enterFunctionNode(final FunctionNode functionNode) {
         start(functionNode, false);
         if (functionNode.isLazy()) {
-            LOG.info("LAZY: " + functionNode.getName());
+            LOG.info("LAZY: " + functionNode.getName() + " => Promoting to OBJECT");
+            newTemporary(lexicalContext.getCurrentFunction(), Type.OBJECT, functionNode);
+            functionNode.setReturnType(Type.OBJECT);
             end(functionNode);
             return null;
         }
 
+        lexicalContext.push(functionNode);
+
         clearLocalDefs();
         clearLocalUses();
 
@@ -242,24 +324,36 @@
         initScope(functionNode);
         initReturn(functionNode);
 
-        // Add all nested functions as symbols in this function
-        for (final FunctionNode nestedFunction : functionNode.getFunctions()) {
+        // Add all nested declared functions as symbols in this function
+        for (final FunctionNode nestedFunction : functionNode.getDeclaredFunctions()) {
             final IdentNode ident = nestedFunction.getIdent();
-            if (ident != null && nestedFunction.isStatement()) {
-                final Symbol functionSymbol = functionNode.defineSymbol(ident.getName(), IS_VAR, nestedFunction);
+            if (ident != null) {
+                assert nestedFunction.isDeclared();
+                final Symbol functionSymbol = defineSymbol(functionNode, ident.getName(), IS_VAR, nestedFunction);
                 newType(functionSymbol, Type.typeFor(ScriptFunction.class));
             }
         }
 
-        if (functionNode.isScript()) {
+        if (functionNode.isProgram()) {
             initFromPropertyMap(functionNode);
         }
 
         // Add function name as local symbol
-        if (!functionNode.isStatement() && !functionNode.isAnonymous() && !functionNode.isScript()) {
-            final Symbol selfSymbol = functionNode.defineSymbol(functionNode.getIdent().getName(), IS_VAR, functionNode);
-            newType(selfSymbol, Type.OBJECT);
-            selfSymbol.setNode(functionNode);
+        if (!functionNode.isDeclared() && !functionNode.isProgram()) {
+            if(functionNode.getSymbol() != null) {
+                // a temporary left over from an earlier pass when the function was lazy
+                assert functionNode.getSymbol().isTemp();
+                // remove it
+                functionNode.setSymbol(null);
+            }
+            final Symbol selfSymbol;
+            if(functionNode.isAnonymous()) {
+                selfSymbol = newTemporary(functionNode, Type.OBJECT, functionNode);
+            } else {
+                selfSymbol = defineSymbol(functionNode, functionNode.getIdent().getName(), IS_VAR, functionNode);
+                newType(selfSymbol, Type.OBJECT);
+                selfSymbol.setNode(functionNode);
+            }
         }
 
         /*
@@ -280,32 +374,26 @@
          */
 
         final List<Symbol> declaredSymbols = new ArrayList<>();
-        for (final VarNode decl : functionNode.getDeclarations()) {
-            final IdentNode ident = decl.getName();
-            // any declared symbols that aren't visited need to be typed as well, hence the list
-            declaredSymbols.add(functionNode.defineSymbol(ident.getName(), IS_VAR, new IdentNode(ident)));
-        }
+        // 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.
+        functionNode.accept(new NodeOperatorVisitor() {
+            @Override
+            public Node enterFunctionNode(FunctionNode nestedFn) {
+                // Don't descend into nested functions
+                return nestedFn == functionNode ? nestedFn : null;
+            }
+            @Override
+            public Node enterVarNode(VarNode varNode) {
+                if(varNode.isStatement() && !varNode.isFunctionDeclaration()) {
+                    final IdentNode ident = varNode.getName();
+                    // any declared symbols that aren't visited need to be typed as well, hence the list
+                    declaredSymbols.add(defineSymbol(functionNode, ident.getName(), IS_VAR, new IdentNode(ident)));
+                }
+                return null;
+            }
+        });
 
-        // Every nested function needs a definition in the outer function with its name. Add these.
-        for (final FunctionNode nestedFunction : functionNode.getFunctions()) {
-            final VarNode varNode = nestedFunction.getFunctionVarNode();
-            if (varNode != null) {
-                varNode.accept(this);
-                assert varNode.isFunctionVarNode() : varNode + " should be function var node";
-            }
-        }
-
-        for (final Node statement : functionNode.getStatements()) {
-            if (statement instanceof VarNode && ((VarNode)statement).isFunctionVarNode()) {
-                continue; //var nodes have already been processed, skip or they will generate additional defs/uses and false "can be undefined"
-            }
-            statement.accept(this);
-        }
-
-        for (final FunctionNode nestedFunction : functionNode.getFunctions()) {
-            LOG.info("Going into nested function " + functionNode.getName() + " -> " + nestedFunction.getName());
-            nestedFunction.accept(this);
-        }
+        visitFunctionStatements(functionNode);
 
         //unknown parameters are promoted to object type.
         finalizeParameters(functionNode);
@@ -332,13 +420,28 @@
             functionNode.setNeedsSelfSymbol(functionNode.getSelfSymbolInit().accept(this));
         }
 
+        if (functionNode.hasLazyChildren()) {
+            objectifySymbols(functionNode);
+        }
+
         functionNode.popFrame();
 
+        functionNode.setState(CompilationState.ATTR);
+
         end(functionNode, false);
+        lexicalContext.pop(functionNode);
 
         return null;
     }
 
+    private void visitFunctionStatements(final FunctionNode functionNode) {
+        final List<Node> newStatements = new ArrayList<>(functionNode.getStatements());
+        for(ListIterator<Node> stmts = newStatements.listIterator(); stmts.hasNext();) {
+            stmts.set(stmts.next().accept(this));
+        }
+        functionNode.setStatements(newStatements);
+    }
+
     @Override
     public Node leaveCONVERT(final UnaryNode unaryNode) {
         assert false : "There should be no convert operators in IR during Attribution";
@@ -347,7 +450,7 @@
     }
 
     @Override
-    public Node enter(final IdentNode identNode) {
+    public Node enterIdentNode(final IdentNode identNode) {
         final String name = identNode.getName();
 
         start(identNode);
@@ -364,7 +467,7 @@
         final Block  block     = getCurrentBlock();
         final Symbol oldSymbol = identNode.getSymbol();
 
-        Symbol symbol = block.findSymbol(name);
+        Symbol symbol = findSymbol(block, name);
 
         //If an existing symbol with the name is found, use that otherwise, declare a new one
         if (symbol != null) {
@@ -388,22 +491,13 @@
             }
 
             identNode.setSymbol(symbol);
-            if (!getCurrentFunctionNode().isLocal(symbol)) {
-                // non-local: we need to put symbol in scope (if it isn't already)
-                if (!symbol.isScope()) {
-                    final List<Block> lookupBlocks = findLookupBlocksHelper(getCurrentFunctionNode(), symbol.findFunction());
-                    for (final Block lookupBlock : lookupBlocks) {
-                        final Symbol refSymbol = lookupBlock.findSymbol(name);
-                        if (refSymbol != null) { // See NASHORN-837, function declaration in lexical scope: try {} catch (x){ function f() { use(x) } } f()
-                            LOG.finest("Found a ref symbol that must be scope " + refSymbol);
-                            refSymbol.setIsScope();
-                        }
-                    }
-                }
+            // non-local: we need to put symbol in scope (if it isn't already)
+            if (!isLocal(getCurrentFunctionNode(), symbol) && !symbol.isScope()) {
+                symbol.setIsScope();
             }
         } else {
             LOG.info("No symbol exists. Declare undefined: " + symbol);
-            symbol = block.useSymbol(name, identNode);
+            symbol = useSymbol(block, name, identNode);
             // we have never seen this before, it can be undefined
             newType(symbol, Type.OBJECT); // TODO unknown -we have explicit casts anyway?
             symbol.setCanBeUndefined();
@@ -412,9 +506,10 @@
 
         assert symbol != null;
         if(symbol.isGlobal()) {
-            getCurrentFunctionNode().setUsesGlobalSymbol();
+            setUsesGlobalSymbol();
         } else if(symbol.isScope()) {
-            getCurrentFunctionNode().setUsesScopeSymbol(symbol);
+            final Iterator<Block> blocks = lexicalContext.getBlocks();
+            blocks.next().setUsesScopeSymbol(symbol, blocks);
         }
 
         if (symbol != oldSymbol && !identNode.isInitializedHere()) {
@@ -427,15 +522,68 @@
         return null;
     }
 
+    /**
+     * Marks the current function as one using any global symbol. The function and all its parent functions will all be
+     * marked as needing parent scope.
+     * @see #needsParentScope()
+     */
+    private void setUsesGlobalSymbol() {
+        for(final Iterator<FunctionNode> fns = lexicalContext.getFunctions(); fns.hasNext();) {
+            fns.next().setUsesAncestorScope();
+        }
+    }
+
+    /**
+     * Declare the use of a symbol in a block.
+     *
+     * @param block block in which the symbol is used
+     * @param name Name of symbol.
+     * @param node Using node
+     *
+     * @return Symbol for given name.
+     */
+    private Symbol useSymbol(final Block block, final String name, final Node node) {
+        Symbol symbol = findSymbol(block, name);
+
+        if (symbol == null) {
+            // If not found, declare as a free var.
+            symbol = defineSymbol(block, name, IS_GLOBAL, node);
+        } else {
+            node.setSymbol(symbol);
+        }
+
+        return symbol;
+    }
+
+
+    /**
+     * Search for symbol in the lexical context starting from the given block.
+     * @param name Symbol name.
+     * @return Found symbol or null if not found.
+     */
+    private Symbol findSymbol(final Block block, final String name) {
+        // Search up block chain to locate symbol.
+
+        for(final Iterator<Block> blocks = lexicalContext.getBlocks(block); blocks.hasNext();) {
+            // Find name.
+            final Symbol symbol = blocks.next().getExistingSymbol(name);
+            // If found then we are good.
+            if(symbol != null) {
+                return symbol;
+            }
+        }
+        return null;
+    }
+
     @Override
-    public Node leave(final IndexNode indexNode) {
-        newTemporary(Type.OBJECT, indexNode); //TORO
+    public Node leaveIndexNode(final IndexNode indexNode) {
+        newTemporary(Type.OBJECT, indexNode); //TODO
         return indexNode;
     }
 
     @SuppressWarnings("rawtypes")
     @Override
-    public Node enter(final LiteralNode literalNode) {
+    public Node enterLiteralNode(final LiteralNode literalNode) {
         try {
             start(literalNode);
             assert !literalNode.isTokenType(TokenType.THIS) : "tokentype for " + literalNode + " is this"; //guard against old dead code case. literal nodes should never inherit tokens
@@ -464,14 +612,14 @@
     }
 
     @Override
-    public Node leave(final ObjectNode objectNode) {
+    public Node leaveObjectNode(final ObjectNode objectNode) {
         newTemporary(Type.OBJECT, objectNode);
         end(objectNode);
         return objectNode;
     }
 
     @Override
-    public Node enter(final PropertyNode propertyNode) {
+    public Node enterPropertyNode(final PropertyNode propertyNode) {
         // assign a pseudo symbol to property name, see NASHORN-710
         propertyNode.setSymbol(new Symbol(propertyNode.getKeyName(), 0, Type.OBJECT));
         end(propertyNode);
@@ -479,31 +627,7 @@
     }
 
     @Override
-    public Node enter(final ReferenceNode referenceNode) {
-        final FunctionNode functionNode = referenceNode.getReference();
-        if (functionNode != null) {
-            functionNode.addReferencingParentBlock(getCurrentBlock());
-        }
-        return referenceNode;
-    }
-
-    @Override
-    public Node leave(final ReferenceNode referenceNode) {
-        newTemporary(Type.OBJECT, referenceNode); //reference node type is always an object, i.e. the scriptFunction. the function return type varies though
-
-        final FunctionNode functionNode = referenceNode.getReference();
-        //assert !functionNode.getType().isUnknown() || functionNode.isLazy() : functionNode.getType();
-        if (functionNode.isLazy()) {
-            LOG.info("Lazy function node call reference: " + functionNode.getName() + " => Promoting to OBJECT");
-            functionNode.setReturnType(Type.OBJECT);
-        }
-        end(referenceNode);
-
-        return referenceNode;
-    }
-
-    @Override
-    public Node leave(final ReturnNode returnNode) {
+    public Node leaveReturnNode(final ReturnNode returnNode) {
         final Node expr = returnNode.getExpression();
 
         if (expr != null) {
@@ -522,7 +646,7 @@
     }
 
     @Override
-    public Node leave(final SwitchNode switchNode) {
+    public Node leaveSwitchNode(final SwitchNode switchNode) {
         Type type = Type.UNKNOWN;
 
         for (final CaseNode caseNode : switchNode.getCases()) {
@@ -559,7 +683,7 @@
     }
 
     @Override
-    public Node leave(final TryNode tryNode) {
+    public Node leaveTryNode(final TryNode tryNode) {
         tryNode.setException(exceptionSymbol());
 
         if (tryNode.getFinallyBody() != null) {
@@ -572,13 +696,13 @@
     }
 
     @Override
-    public Node enter(final VarNode varNode) {
+    public Node enterVarNode(final VarNode varNode) {
         start(varNode);
 
         final IdentNode ident = varNode.getName();
         final String    name  = ident.getName();
 
-        final Symbol symbol = getCurrentBlock().defineSymbol(name, IS_VAR, ident);
+        final Symbol symbol = defineSymbol(getCurrentBlock(), name, IS_VAR, ident);
         assert symbol != null;
 
         LOG.info("VarNode " + varNode + " set symbol " + symbol);
@@ -590,23 +714,15 @@
             symbol.setCanBeUndefined();
         }
 
-        if (varNode.getInit() != null) {
-            varNode.getInit().accept(this);
-        }
-
         return varNode;
     }
 
     @Override
-    public Node leave(final VarNode varNode) {
+    public Node leaveVarNode(final VarNode varNode) {
         final Node      init  = varNode.getInit();
         final IdentNode ident = varNode.getName();
         final String    name  = ident.getName();
 
-        if (init != null) {
-            addLocalDef(name);
-        }
-
         if (init == null) {
             // var x; with no init will be treated like a use of x by
             // visit(IdentNode) unless we remove the name
@@ -615,8 +731,10 @@
             return varNode;
         }
 
+        addLocalDef(name);
+
         final Symbol  symbol   = varNode.getSymbol();
-        final boolean isScript = symbol.getBlock().getFunction().isScript(); //see NASHORN-56
+        final boolean isScript = lexicalContext.getFunction(symbol.getBlock()).isProgram(); //see NASHORN-56
         if ((init.getType().isNumeric() || init.getType().isBoolean()) && !isScript) {
             // Forbid integers as local vars for now as we have no way to treat them as undefined
             newType(symbol, init.getType());
@@ -710,11 +828,9 @@
         runtimeNode = new RuntimeNode(unaryNode, request, args);
         assert runtimeNode.getSymbol() == unaryNode.getSymbol(); //clone constructor should do this
 
-        runtimeNode.accept(this);
-        return runtimeNode;
+        return leaveRuntimeNode(runtimeNode);
     }
 
-
     @Override
     public Node leaveNEW(final UnaryNode unaryNode) {
         newTemporary(Type.OBJECT, unaryNode);
@@ -747,7 +863,7 @@
         runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args);
         assert runtimeNode.getSymbol() == unaryNode.getSymbol();
 
-        runtimeNode.accept(this);
+        runtimeNode = (RuntimeNode)leaveRuntimeNode(runtimeNode);
 
         end(unaryNode);
 
@@ -755,7 +871,7 @@
     }
 
     @Override
-    public Node leave(final RuntimeNode runtimeNode) {
+    public Node leaveRuntimeNode(final RuntimeNode runtimeNode) {
         newTemporary(runtimeNode.getRequest().getReturnType(), runtimeNode);
         return runtimeNode;
     }
@@ -815,12 +931,12 @@
             final IdentNode ident = (IdentNode)lhs;
             final String    name  = ident.getName();
 
-            Symbol symbol = getCurrentBlock().findSymbol(name);
+            Symbol symbol = findSymbol(getCurrentBlock(), name);
 
             if (symbol == null) {
-                symbol = block.defineSymbol(name, IS_GLOBAL, ident);
+                symbol = defineSymbol(block, name, IS_GLOBAL, ident);
                 binaryNode.setSymbol(symbol);
-            } else if (!getCurrentFunctionNode().isLocal(symbol)) {
+            } else if (!isLocal(getCurrentFunctionNode(), symbol)) {
                 symbol.setIsScope();
             }
 
@@ -830,6 +946,12 @@
         return binaryNode;
     }
 
+    private boolean isLocal(FunctionNode function, Symbol symbol) {
+        final Block block = symbol.getBlock();
+        // some temp symbols have no block, so can be assumed local
+        return block == null || lexicalContext.getFunction(block) == function;
+    }
+
     @Override
     public Node enterASSIGN(final BinaryNode binaryNode) {
         return enterAssignmentNode(binaryNode);
@@ -957,20 +1079,17 @@
 
     @Override
     public Node leaveBIT_AND(final BinaryNode binaryNode) {
-        newTemporary(Type.INT, binaryNode);
-        return binaryNode;
+        return end(coerce(binaryNode, Type.INT));
     }
 
     @Override
     public Node leaveBIT_OR(final BinaryNode binaryNode) {
-        newTemporary(Type.INT, binaryNode);
-        return binaryNode;
+        return end(coerce(binaryNode, Type.INT));
     }
 
     @Override
     public Node leaveBIT_XOR(final BinaryNode binaryNode) {
-        newTemporary(Type.INT, binaryNode);
-        return binaryNode;
+        return end(coerce(binaryNode, Type.INT));
     }
 
     @Override
@@ -990,7 +1109,7 @@
         return leaveBinaryArithmetic(binaryNode);
     }
 
-    private Node leaveCmp(final BinaryNode binaryNode, final RuntimeNode.Request request) {
+    private Node leaveCmp(final BinaryNode binaryNode) {
         final Node lhs = binaryNode.lhs();
         final Node rhs = binaryNode.rhs();
 
@@ -1002,49 +1121,64 @@
         return binaryNode;
     }
 
+    private Node coerce(final BinaryNode binaryNode, final Type operandType, final Type destType) {
+        // TODO we currently don't support changing inferred type based on uses, only on
+        // definitions. we would need some additional logic. We probably want to do that
+        // in the future, if e.g. a specialized method gets parameter that is only used
+        // as, say, an int : function(x) { return x & 4711 }, and x is not defined in
+        // the function. to make this work, uncomment the following two type inferences
+        // and debug.
+
+        //newType(binaryNode.lhs().getSymbol(), operandType);
+        //newType(binaryNode.rhs().getSymbol(), operandType);
+        newTemporary(destType, binaryNode);
+        return binaryNode;
+    }
+
+    private Node coerce(final BinaryNode binaryNode, final Type type) {
+        return coerce(binaryNode, type, type);
+    }
+
     //leave a binary node and inherit the widest type of lhs , rhs
     private Node leaveBinaryArithmetic(final BinaryNode binaryNode) {
-        if (!Compiler.shouldUseIntegerArithmetic()) {
-            newTemporary(Type.NUMBER, binaryNode);
-            return binaryNode;
-        }
-        newTemporary(Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType(), Type.NUMBER), binaryNode);
-        return binaryNode;
+        assert !Compiler.shouldUseIntegerArithmetic();
+        return end(coerce(binaryNode, Type.NUMBER));
     }
 
     @Override
     public Node leaveEQ(final BinaryNode binaryNode) {
-        return leaveCmp(binaryNode, Request.EQ);
+        return leaveCmp(binaryNode);
     }
 
     @Override
     public Node leaveEQ_STRICT(final BinaryNode binaryNode) {
-        return leaveCmp(binaryNode, Request.EQ_STRICT);
+        return leaveCmp(binaryNode);
     }
 
     @Override
     public Node leaveGE(final BinaryNode binaryNode) {
-        return leaveCmp(binaryNode, Request.GE);
+        return leaveCmp(binaryNode);
     }
 
     @Override
     public Node leaveGT(final BinaryNode binaryNode) {
-        return leaveCmp(binaryNode, Request.GT);
+        return leaveCmp(binaryNode);
     }
 
     @Override
     public Node leaveIN(final BinaryNode binaryNode) {
-        try {
-            return new RuntimeNode(binaryNode, Request.IN).accept(this);
-        } finally {
-            end(binaryNode);
-        }
+        return leaveBinaryRuntimeOperator(binaryNode, Request.IN);
     }
 
     @Override
     public Node leaveINSTANCEOF(final BinaryNode binaryNode) {
+        return leaveBinaryRuntimeOperator(binaryNode, Request.INSTANCEOF);
+    }
+
+    private Node leaveBinaryRuntimeOperator(final BinaryNode binaryNode, final Request request) {
         try {
-            return new RuntimeNode(binaryNode, Request.INSTANCEOF).accept(this);
+            // Don't do a full RuntimeNode.accept, as we don't want to double-visit the binary node operands
+            return leaveRuntimeNode(new RuntimeNode(binaryNode, request));
         } finally {
             end(binaryNode);
         }
@@ -1052,12 +1186,12 @@
 
     @Override
     public Node leaveLE(final BinaryNode binaryNode) {
-        return leaveCmp(binaryNode, Request.LE);
+        return leaveCmp(binaryNode);
     }
 
     @Override
     public Node leaveLT(final BinaryNode binaryNode) {
-        return leaveCmp(binaryNode, Request.LT);
+        return leaveCmp(binaryNode);
     }
 
     @Override
@@ -1072,12 +1206,12 @@
 
     @Override
     public Node leaveNE(final BinaryNode binaryNode) {
-        return leaveCmp(binaryNode, Request.NE);
+        return leaveCmp(binaryNode);
     }
 
     @Override
     public Node leaveNE_STRICT(final BinaryNode binaryNode) {
-        return leaveCmp(binaryNode, Request.NE_STRICT);
+        return leaveCmp(binaryNode);
     }
 
     @Override
@@ -1089,23 +1223,17 @@
 
     @Override
     public Node leaveSAR(final BinaryNode binaryNode) {
-        newTemporary(Type.INT, binaryNode);
-        end(binaryNode);
-        return binaryNode;
+        return end(coerce(binaryNode, Type.INT));
     }
 
     @Override
     public Node leaveSHL(final BinaryNode binaryNode) {
-        newTemporary(Type.INT, binaryNode);
-        end(binaryNode);
-        return binaryNode;
+        return end(coerce(binaryNode, Type.INT));
     }
 
     @Override
     public Node leaveSHR(final BinaryNode binaryNode) {
-        newTemporary(Type.LONG, binaryNode);
-        end(binaryNode);
-        return binaryNode;
+        return end(coerce(binaryNode, Type.LONG));
     }
 
     @Override
@@ -1114,9 +1242,9 @@
     }
 
     @Override
-    public Node leave(final ForNode forNode) {
+    public Node leaveForNode(final ForNode forNode) {
         if (forNode.isForIn()) {
-            forNode.setIterator(newInternal(getCurrentFunctionNode(), getCurrentFunctionNode().uniqueName(ITERATOR_PREFIX.tag()), Type.OBJECT)); //NASHORN-73
+            forNode.setIterator(newInternal(getCurrentFunctionNode().uniqueName(ITERATOR_PREFIX.tag()), 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
@@ -1131,7 +1259,7 @@
     }
 
     @Override
-    public Node leave(final TernaryNode ternaryNode) {
+    public Node leaveTernaryNode(final TernaryNode ternaryNode) {
         final Node lhs  = ternaryNode.rhs();
         final Node rhs  = ternaryNode.third();
 
@@ -1146,24 +1274,24 @@
         return ternaryNode;
     }
 
-    private static void initThis(final FunctionNode functionNode) {
-        final Symbol thisSymbol = functionNode.defineSymbol(THIS.tag(), IS_PARAM | IS_THIS, null);
+    private void initThis(final FunctionNode functionNode) {
+        final Symbol thisSymbol = defineSymbol(functionNode, THIS.tag(), IS_PARAM | IS_THIS, null);
         newType(thisSymbol, Type.OBJECT);
         thisSymbol.setNeedsSlot(true);
         functionNode.getThisNode().setSymbol(thisSymbol);
         LOG.info("Initialized scope symbol: " + thisSymbol);
     }
 
-    private static void initScope(final FunctionNode functionNode) {
-        final Symbol scopeSymbol = functionNode.defineSymbol(SCOPE.tag(), IS_VAR | IS_INTERNAL, null);
+    private void initScope(final FunctionNode functionNode) {
+        final Symbol scopeSymbol = defineSymbol(functionNode, SCOPE.tag(), IS_VAR | IS_INTERNAL, null);
         newType(scopeSymbol, Type.typeFor(ScriptObject.class));
         scopeSymbol.setNeedsSlot(true);
         functionNode.getScopeNode().setSymbol(scopeSymbol);
         LOG.info("Initialized scope symbol: " + scopeSymbol);
     }
 
-    private static void initReturn(final FunctionNode functionNode) {
-        final Symbol returnSymbol = functionNode.defineSymbol(SCRIPT_RETURN.tag(), IS_VAR | IS_INTERNAL, null);
+    private void initReturn(final FunctionNode functionNode) {
+        final Symbol returnSymbol = defineSymbol(functionNode, SCRIPT_RETURN.tag(), IS_VAR | IS_INTERNAL, null);
         newType(returnSymbol, Type.OBJECT);
         returnSymbol.setNeedsSlot(true);
         functionNode.getResultNode().setSymbol(returnSymbol);
@@ -1173,7 +1301,7 @@
 
     private void initVarArg(final FunctionNode functionNode) {
         if (functionNode.isVarArg()) {
-            final Symbol varArgsSymbol = functionNode.defineSymbol(VARARGS.tag(), IS_PARAM | IS_INTERNAL, null);
+            final Symbol varArgsSymbol = defineSymbol(functionNode, VARARGS.tag(), IS_PARAM | IS_INTERNAL, null);
             varArgsSymbol.setTypeOverride(Type.OBJECT_ARRAY);
             varArgsSymbol.setNeedsSlot(true);
             functionNode.getVarArgsNode().setSymbol(varArgsSymbol);
@@ -1181,7 +1309,7 @@
 
             if (functionNode.needsArguments()) {
                 final String    argumentsName   = functionNode.getArgumentsNode().getName();
-                final Symbol    argumentsSymbol = functionNode.defineSymbol(argumentsName, IS_VAR | IS_INTERNAL, null);
+                final Symbol    argumentsSymbol = defineSymbol(functionNode, argumentsName, IS_VAR | IS_INTERNAL, null);
                 newType(argumentsSymbol, Type.typeFor(ScriptObject.class));
                 argumentsSymbol.setNeedsSlot(true);
                 functionNode.getArgumentsNode().setSymbol(argumentsSymbol);
@@ -1191,9 +1319,9 @@
         }
     }
 
-    private static void initCallee(final FunctionNode functionNode) {
+    private void initCallee(final FunctionNode functionNode) {
         assert functionNode.getCalleeNode() != null : functionNode + " has no callee";
-        final Symbol calleeSymbol = functionNode.defineSymbol(CALLEE.tag(), IS_PARAM | IS_INTERNAL, null);
+        final Symbol calleeSymbol = defineSymbol(functionNode, CALLEE.tag(), IS_PARAM | IS_INTERNAL, null);
         newType(calleeSymbol, Type.typeFor(ScriptFunction.class));
         calleeSymbol.setNeedsSlot(true);
         functionNode.getCalleeNode().setSymbol(calleeSymbol);
@@ -1211,11 +1339,17 @@
         // type or its parameters with the widest (OBJECT) type for safety.
         functionNode.setReturnType(Type.UNKNOWN);
 
-        for (final IdentNode ident : functionNode.getParameters()) {
-            addLocalDef(ident.getName());
-            final Symbol paramSymbol = functionNode.defineSymbol(ident.getName(), IS_PARAM, ident);
+        for (final IdentNode param : functionNode.getParameters()) {
+            addLocalDef(param.getName());
+            final Symbol paramSymbol = defineSymbol(functionNode, param.getName(), IS_PARAM, param);
             if (paramSymbol != null) {
-                newType(paramSymbol, Type.UNKNOWN);
+                final Type callSiteParamType = functionNode.getSpecializedType(param);
+                if (callSiteParamType != null) {
+                    LOG.info("Param " + paramSymbol + " has a callsite type " + callSiteParamType + ". Using that.");
+
+                    System.err.println("Param " + param + " has a callsite type " + callSiteParamType + ". Using that.");
+                }
+                newType(paramSymbol, callSiteParamType == null ? Type.UNKNOWN : callSiteParamType);
             }
 
             LOG.info("Initialized param " + paramSymbol);
@@ -1229,36 +1363,29 @@
      * @param functionNode functionNode
      */
     private static void finalizeParameters(final FunctionNode functionNode) {
-        boolean nonObjectParams = false;
-        List<Type> paramSpecializations = new ArrayList<>();
+        final boolean isVarArg = functionNode.isVarArg();
 
         for (final IdentNode ident : functionNode.getParameters()) {
             final Symbol paramSymbol = ident.getSymbol();
-            if (paramSymbol != null) {
-                Type type = paramSymbol.getSymbolType();
-                if (type.isUnknown()) {
-                    type = Type.OBJECT;
-                }
-                paramSpecializations.add(type);
-                if (!type.isObject()) {
-                    nonObjectParams = true;
-                }
-                newType(paramSymbol, Type.OBJECT);
+
+            assert paramSymbol != null;
+            Type type = functionNode.getSpecializedType(ident);
+            if (type == null) {
+                type = Type.OBJECT;
             }
-        }
 
-        if (!nonObjectParams) {
-            paramSpecializations = null;
-            // Later, when resolving a call to this method, the linker can say "I have a double, an int and an object" as parameters
-            // here. If the callee has parameter specializations, we can regenerate it with those particular types for speed.
-        } else {
-            LOG.info("parameter specialization possible: " + functionNode.getName() + " " + paramSpecializations);
-        }
+            // if we know that a parameter is only used as a certain type throughout
+            // this function, we can tell the runtime system that no matter what the
+            // call site is, use this information. TODO
+            if (!paramSymbol.getSymbolType().isObject()) {
+                LOG.finest("Parameter " + ident + " could profit from specialization to " + paramSymbol.getSymbolType());
+            }
 
-        // parameters should not be slots for a function that uses variable arity signature
-        if (functionNode.isVarArg()) {
-            for (final IdentNode param : functionNode.getParameters()) {
-                param.getSymbol().setNeedsSlot(false);
+            newType(paramSymbol, Type.widest(type, paramSymbol.getSymbolType()));
+
+            // parameters should not be slots for a function that uses variable arity signature
+            if (isVarArg) {
+                paramSymbol.setNeedsSlot(false);
             }
         }
     }
@@ -1267,15 +1394,15 @@
      * Move any properties from a global map into the scope of this method
      * @param functionNode the function node for which to init scope vars
      */
-    private static void initFromPropertyMap(final FunctionNode functionNode) {
+    private void initFromPropertyMap(final FunctionNode functionNode) {
         // For a script, add scope symbols as defined in the property map
-        assert functionNode.isScript();
+        assert functionNode.isProgram();
 
         final PropertyMap map = Context.getGlobalMap();
 
         for (final Property property : map.getProperties()) {
             final String key    = property.getKey();
-            final Symbol symbol = functionNode.defineSymbol(key, IS_GLOBAL, null);
+            final Symbol symbol = defineSymbol(functionNode, key, IS_GLOBAL, null);
             newType(symbol, Type.OBJECT);
             LOG.info("Added global symbol from property map " + symbol);
         }
@@ -1342,9 +1469,14 @@
     private static void ensureAssignmentSlots(final FunctionNode functionNode, final Node assignmentDest) {
         assignmentDest.accept(new NodeVisitor() {
             @Override
-            public Node leave(final IndexNode indexNode) {
+            public Node leaveIndexNode(final IndexNode indexNode) {
+                assert indexNode.getSymbol().isTemp();
                 final Node index = indexNode.getIndex();
-                index.getSymbol().setNeedsSlot(!index.getSymbol().isConstant());
+                //only temps can be set as needing slots. the others will self resolve
+                //it is illegal to take a scope var and force it to be a slot, that breaks
+                if (index.getSymbol().isTemp() && !index.getSymbol().isConstant()) {
+                     index.getSymbol().setNeedsSlot(true);
+                }
                 return indexNode;
             }
         });
@@ -1387,7 +1519,7 @@
                 }
 
                 @Override
-                public Node enter(final FunctionNode node) {
+                public Node enterFunctionNode(final FunctionNode node) {
                     return node.isLazy() ? null : node;
                 }
 
@@ -1407,7 +1539,7 @@
                  */
                 @SuppressWarnings("fallthrough")
                 @Override
-                public Node leave(final BinaryNode binaryNode) {
+                public Node leaveBinaryNode(final BinaryNode binaryNode) {
                     final Type widest = Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType());
                     switch (binaryNode.tokenType()) {
                     default:
@@ -1465,22 +1597,6 @@
         return binaryNode;
     }
 
-    private static List<Block> findLookupBlocksHelper(final FunctionNode currentFunction, final FunctionNode topFunction) {
-        if (currentFunction.findParentFunction() == topFunction) {
-            final List<Block> blocks = new LinkedList<>();
-
-            blocks.add(currentFunction.getParent());
-            blocks.addAll(currentFunction.getReferencingParentBlocks());
-            return blocks;
-        }
-        /*
-         * assumption: all parent blocks of an inner function will always be in the same outer function;
-         * therefore we can simply skip through intermediate functions.
-         * @see FunctionNode#addReferencingParentBlock(Block)
-         */
-        return findLookupBlocksHelper(currentFunction.findParentFunction(), topFunction);
-    }
-
     private static boolean isFunctionExpressionSelfReference(final Symbol symbol) {
         if (symbol.isVar() && symbol.getNode() == symbol.getBlock() && symbol.getNode() instanceof FunctionNode) {
             return ((FunctionNode)symbol.getNode()).getIdent().getName().equals(symbol.getName());
@@ -1497,16 +1613,12 @@
         return newTemporary(getCurrentFunctionNode(), type, node);
     }
 
-    private Symbol newInternal(final FunctionNode functionNode, final String name, final Type type) {
-        final Symbol iter = getCurrentFunctionNode().defineSymbol(name, IS_VAR | IS_INTERNAL, null);
+    private Symbol newInternal(final String name, final Type type) {
+        final Symbol iter = defineSymbol(getCurrentFunctionNode(), name, IS_VAR | IS_INTERNAL, null);
         iter.setType(type); // NASHORN-73
         return iter;
     }
 
-    private Symbol newInternal(final String name, final Type type) {
-        return newInternal(getCurrentFunctionNode(), name, type);
-    }
-
     private static void newType(final Symbol symbol, final Type type) {
         final Type oldType = symbol.getSymbolType();
         symbol.setType(type);
@@ -1548,6 +1660,39 @@
         localUses.add(name);
     }
 
+    /**
+     * Pessimistically promote all symbols in current function node to Object types
+     * This is done when the function contains unevaluated black boxes such as
+     * lazy sub-function nodes that have not been compiled.
+     *
+     * @param functionNode function node in whose scope symbols should conservatively be made objects
+     */
+    private static void objectifySymbols(final FunctionNode functionNode) {
+        functionNode.accept(new NodeVisitor() {
+            private void toObject(final Block block) {
+                for (final Iterator<Symbol> iter = block.symbolIterator(); iter.hasNext();) {
+                    final Symbol symbol = iter.next();
+                    newType(symbol, Type.OBJECT);
+                }
+            }
+
+            @Override
+            public Node enterBlock(final Block block) {
+                toObject(block);
+                return block;
+            }
+
+            @Override
+            public Node enterFunctionNode(final FunctionNode node) {
+                toObject(node);
+                if (node.isLazy()) {
+                    return null;
+                }
+                return node;
+            }
+        });
+    }
+
     private static String name(final Node node) {
         final String cn = node.getClass().getName();
         int lastDot = cn.lastIndexOf('.');
--- a/src/jdk/nashorn/internal/codegen/BranchOptimizer.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/codegen/BranchOptimizer.java	Mon Apr 01 21:42:31 2013 -0700
@@ -32,7 +32,6 @@
 import static jdk.nashorn.internal.codegen.Condition.LT;
 import static jdk.nashorn.internal.codegen.Condition.NE;
 
-import jdk.nashorn.internal.codegen.Label;
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.BinaryNode;
 import jdk.nashorn.internal.ir.Node;
--- a/src/jdk/nashorn/internal/codegen/ClassEmitter.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/codegen/ClassEmitter.java	Mon Apr 01 21:42:31 2013 -0700
@@ -195,6 +195,14 @@
     }
 
     /**
+     * Returns the name of the compile unit class name.
+     * @return the name of the compile unit class name.
+     */
+    String getUnitClassName() {
+        return unitClassName;
+    }
+
+    /**
      * Convert a binary name to a package/class name.
      *
      * @param name Binary name.
@@ -244,7 +252,7 @@
             // $getMap - get the ith entry from the constants table and cast to PropertyMap.
             final MethodEmitter getMapMethod = method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), GET_MAP.tag(), PropertyMap.class, int.class);
             getMapMethod.begin();
-            getMapMethod.loadConstants(unitClassName)
+            getMapMethod.loadConstants()
                         .load(Type.INT, 0)
                         .arrayload()
                         .checkcast(PropertyMap.class)
@@ -254,7 +262,7 @@
             // $setMap - overwrite an existing map.
             final MethodEmitter setMapMethod = method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), SET_MAP.tag(), void.class, int.class, PropertyMap.class);
             setMapMethod.begin();
-            setMapMethod.loadConstants(unitClassName)
+            setMapMethod.loadConstants()
                         .load(Type.INT, 0)
                         .load(Type.OBJECT, 1)
                         .arraystore();
--- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Mon Apr 01 21:42:31 2013 -0700
@@ -25,10 +25,8 @@
 
 package jdk.nashorn.internal.codegen;
 
-import static jdk.nashorn.internal.codegen.ClassEmitter.Flag.HANDLE_STATIC;
 import static jdk.nashorn.internal.codegen.ClassEmitter.Flag.PRIVATE;
 import static jdk.nashorn.internal.codegen.ClassEmitter.Flag.STATIC;
-import static jdk.nashorn.internal.codegen.CompilerConstants.ALLOCATE;
 import static jdk.nashorn.internal.codegen.CompilerConstants.GET_MAP;
 import static jdk.nashorn.internal.codegen.CompilerConstants.GET_STRING;
 import static jdk.nashorn.internal.codegen.CompilerConstants.LEAF;
@@ -50,7 +48,6 @@
 import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_STRICT;
 
 import java.io.PrintWriter;
-import java.lang.invoke.MethodHandle;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.EnumSet;
@@ -79,9 +76,11 @@
 import jdk.nashorn.internal.ir.ExecuteNode;
 import jdk.nashorn.internal.ir.ForNode;
 import jdk.nashorn.internal.ir.FunctionNode;
+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.LineNumberNode;
 import jdk.nashorn.internal.ir.LiteralNode;
 import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
@@ -89,7 +88,6 @@
 import jdk.nashorn.internal.ir.Node;
 import jdk.nashorn.internal.ir.ObjectNode;
 import jdk.nashorn.internal.ir.PropertyNode;
-import jdk.nashorn.internal.ir.ReferenceNode;
 import jdk.nashorn.internal.ir.ReturnNode;
 import jdk.nashorn.internal.ir.RuntimeNode;
 import jdk.nashorn.internal.ir.RuntimeNode.Request;
@@ -108,14 +106,14 @@
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
 import jdk.nashorn.internal.parser.Lexer.RegexToken;
 import jdk.nashorn.internal.parser.TokenType;
-import jdk.nashorn.internal.runtime.CodeInstaller;
 import jdk.nashorn.internal.runtime.Context;
+import jdk.nashorn.internal.runtime.DebugLogger;
 import jdk.nashorn.internal.runtime.ECMAException;
 import jdk.nashorn.internal.runtime.Property;
 import jdk.nashorn.internal.runtime.PropertyMap;
+import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
 import jdk.nashorn.internal.runtime.Scope;
 import jdk.nashorn.internal.runtime.ScriptFunction;
-import jdk.nashorn.internal.runtime.ScriptFunctionData;
 import jdk.nashorn.internal.runtime.ScriptObject;
 import jdk.nashorn.internal.runtime.ScriptRuntime;
 import jdk.nashorn.internal.runtime.Source;
@@ -149,8 +147,6 @@
     /** Name of the ScriptFunctionImpl, cannot be referred to as .class @see FunctionObjectCreator */
     private static final String SCRIPTFUNCTION_IMPL_OBJECT = Compiler.OBJECTS_PACKAGE + '/' + "ScriptFunctionImpl";
 
-    private static final String SCRIPTFUNCTION_TRAMPOLINE_OBJECT = Compiler.OBJECTS_PACKAGE + '/' + "ScriptFunctionTrampolineImpl";
-
     /** Constant data & installation. The only reason the compiler keeps this is because it is assigned
      *  by reflection in class installation */
     private final Compiler compiler;
@@ -161,12 +157,20 @@
     /** How many regexp fields have been emitted */
     private int regexFieldCount;
 
+    /** Used for temporary signaling between enterCallNode and enterFunctionNode to handle the special case of calling
+     * a just-defined anonymous function expression. */
+    private boolean functionNodeIsCallee;
+
     /** Map of shared scope call sites */
     private final Map<SharedScopeCall, SharedScopeCall> scopeCalls = new HashMap<>();
 
+    private final LexicalContext lexicalContext = new LexicalContext();
+
     /** 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.
      *
@@ -215,7 +219,7 @@
             final int flags = CALLSITE_SCOPE | getCallSiteFlags();
             method.loadScope();
 
-            if (symbol.isFastScope(getCurrentFunctionNode())) {
+            if (isFastScope(symbol)) {
                 // Only generate shared scope getter for fast-scope symbols so we know we can dial in correct scope.
                 if (symbol.getUseCount() > SharedScopeCall.FAST_SCOPE_GET_THRESHOLD) {
                     return loadSharedScopeVar(identNode.getType(), symbol, flags);
@@ -226,8 +230,28 @@
         }
     }
 
+    /**
+     * 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() || !symbol.getBlock().needsScope()) {
+            return false;
+        }
+        // Allow fast scope access if no function contains with or eval
+        for(final Iterator<FunctionNode> it = lexicalContext.getFunctions(getCurrentFunctionNode()); it.hasNext();) {
+            final FunctionNode func = it.next();
+            if (func.hasWith() || func.hasEval()) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     private MethodEmitter loadSharedScopeVar(final Type valueType, final Symbol symbol, final int flags) {
-        method.load(symbol.isFastScope(getCurrentFunctionNode()) ? getScopeProtoDepth(getCurrentBlock(), symbol) : -1);
+        method.load(isFastScope(symbol) ? getScopeProtoDepth(getCurrentBlock(), symbol) : -1);
         final SharedScopeCall scopeCall = getScopeGet(valueType, symbol, flags | CALLSITE_FAST_SCOPE);
         scopeCall.generateInvoke(method);
         return method;
@@ -245,30 +269,18 @@
         return method;
     }
 
-    private static int getScopeProtoDepth(final Block currentBlock, final Symbol symbol) {
-        if (currentBlock == symbol.getBlock()) {
-            return 0;
-        }
-
-        final int   delta       = currentBlock.needsScope() ? 1 : 0;
-        final Block parentBlock = currentBlock.getParent();
-
-        if (parentBlock != null) {
-            final int result = getScopeProtoDepth(parentBlock, symbol);
-            if (result != -1) {
-                return delta + result;
+    private int getScopeProtoDepth(final Block startingBlock, final Symbol symbol) {
+        int depth = 0;
+        final Block definingBlock = symbol.getBlock();
+        for(final Iterator<Block> blocks = lexicalContext.getBlocks(startingBlock); blocks.hasNext();) {
+            final Block currentBlock = blocks.next();
+            if (currentBlock == definingBlock) {
+                return depth;
+            }
+            if (currentBlock.needsScope()) {
+                ++depth;
             }
         }
-
-        if (currentBlock instanceof FunctionNode) {
-            for (final Block lookupBlock : ((FunctionNode)currentBlock).getReferencingParentBlocks()) {
-                final int result = getScopeProtoDepth(lookupBlock, symbol);
-                if (result != -1) {
-                    return delta + result;
-                }
-            }
-        }
-
         return -1;
     }
 
@@ -318,13 +330,13 @@
 
         node.accept(new NodeVisitor(getCurrentCompileUnit(), method) {
             @Override
-            public Node enter(final IdentNode identNode) {
+            public Node enterIdentNode(final IdentNode identNode) {
                 loadIdent(identNode);
                 return null;
             }
 
             @Override
-            public Node enter(final AccessNode accessNode) {
+            public Node enterAccessNode(final AccessNode accessNode) {
                 if (!baseAlreadyOnStack) {
                     load(accessNode.getBase()).convert(Type.OBJECT);
                 }
@@ -334,7 +346,7 @@
             }
 
             @Override
-            public Node enter(final IndexNode indexNode) {
+            public Node enterIndexNode(final IndexNode indexNode) {
                 if (!baseAlreadyOnStack) {
                     load(indexNode.getBase()).convert(Type.OBJECT);
                     load(indexNode.getIndex());
@@ -344,6 +356,14 @@
             }
 
             @Override
+            public Node enterFunctionNode(FunctionNode functionNode) {
+                // function nodes will always leave a constructed function object on stack, no need to load the symbol
+                // separately as in enterDefault()
+                functionNode.accept(codegen);
+                return null;
+            }
+
+            @Override
             public Node enterDefault(final Node otherNode) {
                 otherNode.accept(codegen); // generate code for whatever we are looking at.
                 method.load(symbol); // load the final symbol to the stack (or nop if no slot, then result is already there)
@@ -355,7 +375,7 @@
     }
 
     @Override
-    public Node enter(final AccessNode accessNode) {
+    public Node enterAccessNode(final AccessNode accessNode) {
         if (accessNode.testResolved()) {
             return null;
         }
@@ -427,10 +447,11 @@
     }
 
     @Override
-    public Node enter(final Block block) {
+    public Node enterBlock(final Block block) {
         if (block.testResolved()) {
             return null;
         }
+        lexicalContext.push(block);
 
         method.label(block.getEntryLabel());
         initLocals(block);
@@ -439,14 +460,14 @@
     }
 
     @Override
-    public Node leave(final Block block) {
+    public Node leaveBlock(final Block block) {
         method.label(block.getBreakLabel());
         symbolInfo(block);
 
         if (block.needsScope()) {
             popBlockScope(block);
         }
-
+        lexicalContext.pop(block);
         return block;
     }
 
@@ -472,7 +493,7 @@
     }
 
     @Override
-    public Node enter(final BreakNode breakNode) {
+    public Node enterBreakNode(final BreakNode breakNode) {
         if (breakNode.testResolved()) {
             return null;
         }
@@ -520,14 +541,13 @@
     }
 
     @Override
-    public Node enter(final CallNode callNode) {
+    public Node enterCallNode(final CallNode callNode) {
         if (callNode.testResolved()) {
             return null;
         }
 
         final List<Node>   args            = callNode.getArgs();
         final Node         function        = callNode.getFunction();
-        final FunctionNode currentFunction = getCurrentFunctionNode();
         final Block        currentBlock    = getCurrentBlock();
 
         function.accept(new NodeVisitor(getCurrentCompileUnit(), method) {
@@ -536,7 +556,7 @@
                 final Symbol symbol = identNode.getSymbol();
                 int    scopeCallFlags = flags;
                 method.loadScope();
-                if (symbol.isFastScope(currentFunction)) {
+                if (isFastScope(symbol)) {
                     method.load(getScopeProtoDepth(currentBlock, symbol));
                     scopeCallFlags |= CALLSITE_FAST_SCOPE;
                 } else {
@@ -598,7 +618,7 @@
             }
 
             @Override
-            public Node enter(final IdentNode node) {
+            public Node enterIdentNode(final IdentNode node) {
                 final Symbol symbol = node.getSymbol();
 
                 if (symbol.isScope()) {
@@ -611,7 +631,7 @@
                     if (callNode.isEval()) {
                         evalCall(node, flags);
                     } else if (useCount <= SharedScopeCall.FAST_SCOPE_CALL_THRESHOLD
-                            || (!symbol.isFastScope(currentFunction) && useCount <= SharedScopeCall.SLOW_SCOPE_CALL_THRESHOLD)
+                            || (!isFastScope(symbol) && useCount <= SharedScopeCall.SLOW_SCOPE_CALL_THRESHOLD)
                             || callNode.inWithBlock()) {
                         scopeCall(node, flags);
                     } else {
@@ -626,7 +646,7 @@
             }
 
             @Override
-            public Node enter(final AccessNode node) {
+            public Node enterAccessNode(final AccessNode node) {
                 load(node.getBase());
                 method.convert(Type.OBJECT);
                 method.dup();
@@ -639,8 +659,7 @@
             }
 
             @Override
-            public Node enter(final ReferenceNode node) {
-                final FunctionNode callee   = node.getReference();
+            public Node enterFunctionNode(final FunctionNode callee) {
                 final boolean      isVarArg = callee.isVarArg();
                 final int          argCount = isVarArg ? -1 : callee.getParameters().size();
 
@@ -658,12 +677,13 @@
                 loadArgs(args, signature, isVarArg, argCount);
                 method.invokestatic(callee.getCompileUnit().getUnitClassName(), callee.getName(), signature);
                 assert method.peekType().equals(callee.getReturnType()) : method.peekType() + " != " + callee.getReturnType();
-
+                functionNodeIsCallee = true;
+                callee.accept(CodeGenerator.this);
                 return null;
             }
 
             @Override
-            public Node enter(final IndexNode node) {
+            public Node enterIndexNode(final IndexNode node) {
                 load(node.getBase());
                 method.convert(Type.OBJECT);
                 method.dup();
@@ -699,7 +719,7 @@
     }
 
     @Override
-    public Node enter(final ContinueNode continueNode) {
+    public Node enterContinueNode(final ContinueNode continueNode) {
         if (continueNode.testResolved()) {
             return null;
         }
@@ -714,17 +734,17 @@
     }
 
     @Override
-    public Node enter(final DoWhileNode doWhileNode) {
-        return enter((WhileNode)doWhileNode);
+    public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
+        return enterWhileNode(doWhileNode);
     }
 
     @Override
-    public Node enter(final EmptyNode emptyNode) {
+    public Node enterEmptyNode(final EmptyNode emptyNode) {
         return null;
     }
 
     @Override
-    public Node enter(final ExecuteNode executeNode) {
+    public Node enterExecuteNode(final ExecuteNode executeNode) {
         if (executeNode.testResolved()) {
             return null;
         }
@@ -736,7 +756,7 @@
     }
 
     @Override
-    public Node enter(final ForNode forNode) {
+    public Node enterForNode(final ForNode forNode) {
         if (forNode.testResolved()) {
             return null;
         }
@@ -818,7 +838,7 @@
      * @param block block with local vars.
      */
     private void initLocals(final Block block) {
-        final FunctionNode function       = block.getFunction();
+        final FunctionNode function       = lexicalContext.getFunction(block);
         final boolean      isFunctionNode = block == function;
 
         /*
@@ -920,7 +940,7 @@
             foc.makeObject(method);
 
             // runScript(): merge scope into global
-            if (isFunctionNode && function.isScript()) {
+            if (isFunctionNode && function.isProgram()) {
                 method.invoke(ScriptRuntime.MERGE_SCOPE);
             }
 
@@ -963,31 +983,42 @@
     }
 
     @Override
-    public Node enter(final FunctionNode functionNode) {
+    public Node enterFunctionNode(final FunctionNode functionNode) {
+        final boolean isCallee = functionNodeIsCallee;
+        functionNodeIsCallee = false;
+
+        if (functionNode.testResolved()) {
+            return null;
+        }
+
+        if(!(isCallee || functionNode == compiler.getFunctionNode())) {
+            newFunctionObject(functionNode);
+        }
+
         if (functionNode.isLazy()) {
             return null;
         }
 
-        if (functionNode.testResolved()) {
-            return null;
-        }
+        LOG.info("=== BEGIN " + functionNode.getName());
+        lexicalContext.push(functionNode);
 
         setCurrentCompileUnit(functionNode.getCompileUnit());
         assert getCurrentCompileUnit() != null;
 
-        method = getCurrentCompileUnit().getClassEmitter().method(functionNode);
+        setCurrentMethodEmitter(getCurrentCompileUnit().getClassEmitter().method(functionNode));
         functionNode.setMethodEmitter(method);
         // Mark end for variable tables.
         method.begin();
         method.label(functionNode.getEntryLabel());
 
         initLocals(functionNode);
+        functionNode.setState(CompilationState.EMITTED);
 
         return functionNode;
     }
 
     @Override
-    public Node leave(final FunctionNode functionNode) {
+    public Node leaveFunctionNode(final FunctionNode functionNode) {
         // Mark end for variable tables.
         method.label(functionNode.getBreakLabel());
 
@@ -1005,16 +1036,18 @@
             throw e;
         }
 
+        lexicalContext.pop(functionNode);
+        LOG.info("=== END " + functionNode.getName());
         return functionNode;
     }
 
     @Override
-    public Node enter(final IdentNode identNode) {
+    public Node enterIdentNode(final IdentNode identNode) {
         return null;
     }
 
     @Override
-    public Node enter(final IfNode ifNode) {
+    public Node enterIfNode(final IfNode ifNode) {
         if (ifNode.testResolved()) {
             return null;
         }
@@ -1053,7 +1086,7 @@
     }
 
     @Override
-    public Node enter(final IndexNode indexNode) {
+    public Node enterIndexNode(final IndexNode indexNode) {
         if (indexNode.testResolved()) {
             return null;
         }
@@ -1064,7 +1097,7 @@
     }
 
     @Override
-    public Node enter(final LineNumberNode lineNumberNode) {
+    public Node enterLineNumberNode(final LineNumberNode lineNumberNode) {
         if (lineNumberNode.testResolved()) {
             return null;
         }
@@ -1072,7 +1105,6 @@
         final Label label = new Label("line:" + lineNumberNode.getLineNumber() + " (" + getCurrentFunctionNode().getName() + ")");
         method.label(label);
         method.lineNumber(lineNumberNode.getLineNumber(), label);
-
         return null;
     }
 
@@ -1110,7 +1142,7 @@
                     final String name      = getCurrentFunctionNode().uniqueName(SPLIT_PREFIX.tag());
                     final String signature = methodDescriptor(type, Object.class, ScriptFunction.class, ScriptObject.class, type);
 
-                    method = getCurrentCompileUnit().getClassEmitter().method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), name, signature);
+                    setCurrentMethodEmitter(getCurrentCompileUnit().getClassEmitter().method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), name, signature));
                     method.setFunctionNode(getCurrentFunctionNode());
                     method.begin();
 
@@ -1216,7 +1248,7 @@
             method.invokestatic(unitClassName, methodName, methodDescriptor(cls, int.class));
             classEmitter.needGetConstantMethod(cls);
         } else {
-            method.loadConstants(unitClassName).load(index).arrayload();
+            method.loadConstants().load(index).arrayload();
             if (cls != Object.class) {
                 method.checkcast(cls);
             }
@@ -1296,14 +1328,14 @@
 
     @SuppressWarnings("rawtypes")
     @Override
-    public Node enter(final LiteralNode literalNode) {
+    public Node enterLiteralNode(final LiteralNode literalNode) {
         assert literalNode.getSymbol() != null : literalNode + " has no symbol";
         load(literalNode).store(literalNode.getSymbol());
         return null;
     }
 
     @Override
-    public Node enter(final ObjectNode objectNode) {
+    public Node enterObjectNode(final ObjectNode objectNode) {
         if (objectNode.testResolved()) {
             return null;
         }
@@ -1376,10 +1408,10 @@
         }
 
         for (final Node element : elements) {
-            final PropertyNode  propertyNode = (PropertyNode)element;
-            final Object        key          = propertyNode.getKey();
-            final ReferenceNode getter       = (ReferenceNode)propertyNode.getGetter();
-            final ReferenceNode setter       = (ReferenceNode)propertyNode.getSetter();
+            final PropertyNode propertyNode = (PropertyNode)element;
+            final Object       key          = propertyNode.getKey();
+            final FunctionNode getter       = (FunctionNode)propertyNode.getGetter();
+            final FunctionNode setter       = (FunctionNode)propertyNode.getSetter();
 
             if (getter == null && setter == null) {
                 continue;
@@ -1408,18 +1440,7 @@
     }
 
     @Override
-    public Node enter(final ReferenceNode referenceNode) {
-        if (referenceNode.testResolved()) {
-            return null;
-        }
-
-        newFunctionObject(referenceNode.getReference());
-
-        return null;
-    }
-
-    @Override
-    public Node enter(final ReturnNode returnNode) {
+    public Node enterReturnNode(final ReturnNode returnNode) {
         if (returnNode.testResolved()) {
             return null;
         }
@@ -1560,7 +1581,7 @@
     }
 
     @Override
-    public Node enter(final RuntimeNode runtimeNode) {
+    public Node enterRuntimeNode(final RuntimeNode runtimeNode) {
         if (runtimeNode.testResolved()) {
             return null;
         }
@@ -1641,7 +1662,7 @@
     }
 
     @Override
-    public Node enter(final SplitNode splitNode) {
+    public Node enterSplitNode(final SplitNode splitNode) {
         if (splitNode.testResolved()) {
             return null;
         }
@@ -1710,7 +1731,7 @@
     }
 
     @Override
-    public Node leave(final SplitNode splitNode) {
+    public Node leaveSplitNode(final SplitNode splitNode) {
         try {
             // Wrap up this method.
             method.loadResult();
@@ -1767,7 +1788,7 @@
     }
 
     @Override
-    public Node enter(final SwitchNode switchNode) {
+    public Node enterSwitchNode(final SwitchNode switchNode) {
         if (switchNode.testResolved()) {
             return null;
         }
@@ -1899,7 +1920,7 @@
     }
 
     @Override
-    public Node enter(final ThrowNode throwNode) {
+    public Node enterThrowNode(final ThrowNode throwNode) {
         if (throwNode.testResolved()) {
             return null;
         }
@@ -1926,7 +1947,7 @@
     }
 
     @Override
-    public Node enter(final TryNode tryNode) {
+    public Node enterTryNode(final TryNode tryNode) {
         if (tryNode.testResolved()) {
             return null;
         }
@@ -1959,7 +1980,7 @@
             setCurrentBlock(catchBlock);
 
             try {
-                enter(catchBlock);
+                enterBlock(catchBlock);
 
                 final CatchNode catchNode          = (CatchNode)catchBlocks.get(i).getStatements().get(0);
                 final IdentNode exception          = catchNode.getException();
@@ -1970,6 +1991,7 @@
                     // Generate catch body (inlined finally) and rethrow exception
                     catchBody.accept(this);
                     method.load(symbol).athrow();
+                    lexicalContext.pop(catchBlock);
                     continue;
                 }
 
@@ -2016,7 +2038,7 @@
                     }
                 }
 
-                leave(catchBlock);
+                leaveBlock(catchBlock);
             } finally {
                 setCurrentBlock(saveBlock);
             }
@@ -2031,7 +2053,7 @@
     }
 
     @Override
-    public Node enter(final VarNode varNode) {
+    public Node enterVarNode(final VarNode varNode) {
         final Node init = varNode.getInit();
 
         if (varNode.testResolved() || init == null) {
@@ -2053,7 +2075,7 @@
             int flags = CALLSITE_SCOPE | getCallSiteFlags();
             final IdentNode identNode = varNode.getName();
             final Type type = identNode.getType();
-            if (varSymbol.isFastScope(getCurrentFunctionNode())) {
+            if (isFastScope(varSymbol)) {
                 storeFastScopeVar(type, varSymbol, flags);
             } else {
                 method.dynamicSet(type, identNode.getName(), flags);
@@ -2069,7 +2091,7 @@
     }
 
     @Override
-    public Node enter(final WhileNode whileNode) {
+    public Node enterWhileNode(final WhileNode whileNode) {
         if (whileNode.testResolved()) {
             return null;
         }
@@ -2102,7 +2124,7 @@
     }
 
     @Override
-    public Node enter(final WithNode withNode) {
+    public Node enterWithNode(final WithNode withNode) {
         if (withNode.testResolved()) {
             return null;
         }
@@ -2868,7 +2890,7 @@
      * Ternary visits.
      */
     @Override
-    public Node enter(final TernaryNode ternaryNode) {
+    public Node enterTernaryNode(final TernaryNode ternaryNode) {
         if (ternaryNode.testResolved()) {
             return null;
         }
@@ -3064,7 +3086,7 @@
 
             target.accept(new NodeVisitor(getCurrentCompileUnit(), method) {
                 @Override
-                public Node enter(final IdentNode node) {
+                public Node enterIdentNode(final IdentNode node) {
                     if (targetSymbol.isScope()) {
                         method.load(scopeSymbol);
                         depth++;
@@ -3087,13 +3109,13 @@
                 }
 
                 @Override
-                public Node enter(final AccessNode node) {
+                public Node enterAccessNode(final AccessNode node) {
                     enterBaseNode();
                     return null;
                 }
 
                 @Override
-                public Node enter(final IndexNode node) {
+                public Node enterIndexNode(final IndexNode node) {
                     enterBaseNode();
 
                     final Node index = node.getIndex();
@@ -3159,8 +3181,6 @@
         }
 
         private void epilogue() {
-            final FunctionNode currentFunction = getCurrentFunctionNode();
-
             /**
              * Take the original target args from the stack and use them
              * together with the value to be stored to emit the store code
@@ -3178,7 +3198,7 @@
                 }
 
                 @Override
-                public Node enter(final UnaryNode node) {
+                public Node enterUnaryNode(final UnaryNode node) {
                     if(node.tokenType() == TokenType.CONVERT && node.getSymbol() != null) {
                         method.convert(node.rhs().getType());
                     }
@@ -3186,11 +3206,11 @@
                 }
 
                 @Override
-                public Node enter(final IdentNode node) {
+                public Node enterIdentNode(final IdentNode node) {
                     final Symbol symbol = node.getSymbol();
                     assert symbol != null;
                     if (symbol.isScope()) {
-                        if (symbol.isFastScope(currentFunction)) {
+                        if (isFastScope(symbol)) {
                             storeFastScopeVar(node.getType(), symbol, CALLSITE_SCOPE | getCallSiteFlags());
                         } else {
                             method.dynamicSet(node.getType(), node.getName(), CALLSITE_SCOPE | getCallSiteFlags());
@@ -3203,13 +3223,13 @@
                 }
 
                 @Override
-                public Node enter(final AccessNode node) {
+                public Node enterAccessNode(final AccessNode node) {
                     method.dynamicSet(node.getProperty().getType(), node.getProperty().getName(), getCallSiteFlags());
                     return null;
                 }
 
                 @Override
-                public Node enter(final IndexNode node) {
+                public Node enterIndexNode(final IndexNode node) {
                     method.dynamicSetIndex(getCallSiteFlags());
                     return null;
                 }
@@ -3234,42 +3254,22 @@
     }
 
     private void newFunctionObject(final FunctionNode functionNode) {
-        final boolean isLazy = functionNode.isLazy();
-        final Class<?>[] cparams = new Class<?>[] { ScriptFunctionData.class, ScriptObject.class, MethodHandle.class };
+        final boolean isLazy  = functionNode.isLazy();
 
         new ObjectCreator(this, new ArrayList<String>(), new ArrayList<Symbol>(), false, false) {
             @Override
-            protected void makeObject(final MethodEmitter method) {
-                final String className = isLazy ? SCRIPTFUNCTION_TRAMPOLINE_OBJECT : SCRIPTFUNCTION_IMPL_OBJECT;
-
-                method._new(className).dup();
-                if (isLazy) {
-                    loadConstant(compiler.getCodeInstaller());
-                    loadConstant(functionNode);
+            protected void makeObject(final MethodEmitter m) {
+                final String className = SCRIPTFUNCTION_IMPL_OBJECT;
+
+                m._new(className).dup();
+                loadConstant(new RecompilableScriptFunctionData(functionNode, compiler.getCodeInstaller(), Compiler.binaryName(getClassName()), makeMap()));
+
+                if (isLazy || functionNode.needsParentScope()) {
+                    m.loadScope();
                 } else {
-                    final String signature = new FunctionSignature(true, functionNode.needsCallee(), functionNode.getReturnType(), functionNode.isVarArg() ? null : functionNode.getParameters()).toString();
-                    method.loadHandle(functionNode.getCompileUnit().getUnitClassName(), functionNode.getName(), signature, EnumSet.of(HANDLE_STATIC)); // function
+                    m.loadNull();
                 }
-                loadConstant(new ScriptFunctionData(functionNode, makeMap()));
-
-                if (isLazy || functionNode.needsParentScope()) {
-                    method.loadScope();
-                } else {
-                    method.loadNull();
-                }
-
-                method.loadHandle(getClassName(), ALLOCATE.tag(), methodDescriptor(ScriptObject.class, PropertyMap.class), EnumSet.of(HANDLE_STATIC));
-
-                final List<Class<?>> cparamList = new ArrayList<>();
-                if (isLazy) {
-                    cparamList.add(CodeInstaller.class);
-                    cparamList.add(FunctionNode.class);
-                } else {
-                    cparamList.add(MethodHandle.class);
-                }
-                cparamList.addAll(Arrays.asList(cparams));
-
-                method.invoke(constructorNoLookup(className, cparamList.toArray(new Class<?>[cparamList.size()])));
+                m.invoke(constructorNoLookup(className, RecompilableScriptFunctionData.class, ScriptObject.class));
             }
         }.makeObject(method);
     }
--- a/src/jdk/nashorn/internal/codegen/CompilationPhase.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/codegen/CompilationPhase.java	Mon Apr 01 21:42:31 2013 -0700
@@ -2,10 +2,10 @@
 
 import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.ATTR;
 import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.CONSTANT_FOLDED;
-import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.EMITTED;
 import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.FINALIZED;
 import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.INITIALIZED;
 import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.LOWERED;
+import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.PARSED;
 import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SPLIT;
 
 import java.io.File;
@@ -14,16 +14,16 @@
 import java.util.EnumSet;
 import java.util.HashSet;
 import java.util.Set;
-
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.CallNode;
 import jdk.nashorn.internal.ir.FunctionNode;
 import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
+import jdk.nashorn.internal.ir.LexicalContext;
 import jdk.nashorn.internal.ir.Node;
-import jdk.nashorn.internal.ir.ReferenceNode;
-import jdk.nashorn.internal.ir.visitor.NodeVisitor;
 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;
 import jdk.nashorn.internal.runtime.Timing;
@@ -39,7 +39,7 @@
      * default policy. The will get trampolines and only be generated when
      * called
      */
-    LAZY_INITIALIZATION_PHASE(EnumSet.of(FunctionNode.CompilationState.INITIALIZED)) {
+    LAZY_INITIALIZATION_PHASE(EnumSet.of(INITIALIZED, PARSED)) {
         @Override
         void transform(final Compiler compiler, final FunctionNode fn) {
 
@@ -65,23 +65,25 @@
             outermostFunctionNode.accept(new NodeVisitor() {
                 // self references are done with invokestatic and thus cannot have trampolines - never lazy
                 @Override
-                public Node enter(final CallNode node) {
+                public Node enterCallNode(final CallNode node) {
                     final Node callee = node.getFunction();
-                    if (callee instanceof ReferenceNode) {
-                        neverLazy.add(((ReferenceNode)callee).getReference());
+                    if (callee instanceof FunctionNode) {
+                        neverLazy.add(((FunctionNode)callee));
                         return null;
                     }
                     return node;
                 }
 
                 @Override
-                public Node enter(final FunctionNode node) {
+                public Node enterFunctionNode(final FunctionNode node) {
                     if (node == outermostFunctionNode) {
                         return node;
                     }
-                    assert Compiler.LAZY_JIT;
+                    assert compiler.isLazy();
                     lazy.add(node);
 
+                    //also needs scope, potentially needs arguments etc etc
+
                     return node;
                 }
             });
@@ -92,15 +94,24 @@
                 lazy.remove(node);
             }
 
-            for (final FunctionNode node : lazy) {
-                Compiler.LOG.fine("Marking " + node.getName() + " as lazy");
-                node.setIsLazy(true);
-                final FunctionNode parent = node.findParentFunction();
-                if (parent != null) {
-                    Compiler.LOG.fine("Marking " + parent.getName() + " as having lazy children - it needs scope for all variables");
-                    parent.setHasLazyChildren();
+            outermostFunctionNode.accept(new NodeOperatorVisitor() {
+                private final LexicalContext lexicalContext = new LexicalContext();
+                @Override
+                public Node enterFunctionNode(FunctionNode functionNode) {
+                    lexicalContext.push(functionNode);
+                    if(lazy.contains(functionNode)) {
+                        Compiler.LOG.fine("Marking " + functionNode.getName() + " as lazy");
+                        functionNode.setIsLazy(true);
+                        lexicalContext.getParentFunction(functionNode).setHasLazyChildren();
+                    }
+                    return functionNode;
                 }
-            }
+                @Override
+                public Node leaveFunctionNode(FunctionNode functionNode) {
+                    lexicalContext.pop(functionNode);
+                    return functionNode;
+                }
+            });
         }
 
         @Override
@@ -113,7 +124,7 @@
      * Constant folding pass
      *   Simple constant folding that will make elementary constructs go away
      */
-    CONSTANT_FOLDING_PHASE(EnumSet.of(INITIALIZED), CONSTANT_FOLDED) {
+    CONSTANT_FOLDING_PHASE(EnumSet.of(INITIALIZED, PARSED)) {
         @Override
         void transform(final Compiler compiler, final FunctionNode fn) {
             fn.accept(new FoldConstants());
@@ -134,7 +145,7 @@
      *   as runtime nodes where applicable.
      *
      */
-    LOWERING_PHASE(EnumSet.of(INITIALIZED, CONSTANT_FOLDED), LOWERED) {
+    LOWERING_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED)) {
         @Override
         void transform(final Compiler compiler, final FunctionNode fn) {
             fn.accept(new Lower());
@@ -150,19 +161,10 @@
      * Attribution
      *   Assign symbols and types to all nodes.
      */
-    ATTRIBUTION_PHASE(EnumSet.of(INITIALIZED, CONSTANT_FOLDED, LOWERED), ATTR) {
+    ATTRIBUTION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED)) {
         @Override
         void transform(final Compiler compiler, final FunctionNode fn) {
-            final ScriptEnvironment env = compiler.getEnv();
-
             fn.accept(new Attr());
-            if (env._print_lower_ast) {
-                env.getErr().println(new ASTWriter(fn));
-            }
-
-            if (env._print_lower_parse) {
-                env.getErr().println(new PrintVisitor(fn));
-           }
         }
 
         @Override
@@ -178,7 +180,7 @@
      *   a + b a ScriptRuntime.ADD with call overhead or a dadd with much
      *   less). Split IR can lead to scope information being changed.
      */
-    SPLITTING_PHASE(EnumSet.of(INITIALIZED, CONSTANT_FOLDED, LOWERED, ATTR), SPLIT) {
+    SPLITTING_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR)) {
         @Override
         void transform(final Compiler compiler, final FunctionNode fn) {
             final CompileUnit outermostCompileUnit = compiler.addCompileUnit(compiler.firstCompileUnitName());
@@ -212,10 +214,20 @@
      * Contract: all variables must have slot assignments and scope assignments
      * before type finalization.
      */
-    TYPE_FINALIZATION_PHASE(EnumSet.of(INITIALIZED, CONSTANT_FOLDED, LOWERED, ATTR, SPLIT), FINALIZED) {
+    TYPE_FINALIZATION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR, SPLIT)) {
         @Override
         void transform(final Compiler compiler, final FunctionNode fn) {
+            final ScriptEnvironment env = compiler.getEnv();
+
             fn.accept(new FinalizeTypes());
+
+            if (env._print_lower_ast) {
+                env.getErr().println(new ASTWriter(fn));
+            }
+
+            if (env._print_lower_parse) {
+                env.getErr().println(new PrintVisitor(fn));
+           }
         }
 
         @Override
@@ -229,7 +241,7 @@
      *
      *   Generate the byte code class(es) resulting from the compiled FunctionNode
      */
-    BYTECODE_GENERATION_PHASE(EnumSet.of(INITIALIZED, CONSTANT_FOLDED, LOWERED, ATTR, SPLIT, FINALIZED), EMITTED) {
+    BYTECODE_GENERATION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR, SPLIT, FINALIZED)) {
         @Override
         void transform(final Compiler compiler, final FunctionNode fn) {
             final ScriptEnvironment env = compiler.getEnv();
@@ -238,6 +250,16 @@
                 final CodeGenerator codegen = new CodeGenerator(compiler);
                 fn.accept(codegen);
                 codegen.generateScopeCalls();
+                fn.accept(new NodeOperatorVisitor() {
+                    @Override
+                    public Node enterFunctionNode(FunctionNode functionNode) {
+                        if(functionNode.isLazy()) {
+                            functionNode.resetResolved();
+                            return null;
+                        }
+                        return fn;
+                    }
+                });
 
             } catch (final VerifyError e) {
                 if (env._verify_code || env._print_code) {
@@ -306,18 +328,12 @@
     };
 
     private final EnumSet<CompilationState> pre;
-    private final CompilationState post;
     private long startTime;
     private long endTime;
     private boolean isFinished;
 
     private CompilationPhase(final EnumSet<CompilationState> pre) {
-        this(pre, null);
-    }
-
-    private CompilationPhase(final EnumSet<CompilationState> pre, final CompilationState post) {
-        this.pre  = pre;
-        this.post = post;
+        this.pre = pre;
     }
 
     boolean isApplicable(final FunctionNode functionNode) {
@@ -343,10 +359,6 @@
         endTime = System.currentTimeMillis();
         Timing.accumulateTime(toString(), endTime - startTime);
 
-        if (post != null) {
-            functionNode.setState(post);
-        }
-
         isFinished = true;
     }
 
--- a/src/jdk/nashorn/internal/codegen/CompileUnit.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/codegen/CompileUnit.java	Mon Apr 01 21:42:31 2013 -0700
@@ -37,6 +37,8 @@
 
     private long weight;
 
+    private Class<?> clazz;
+
     CompileUnit(final String className, final ClassEmitter classEmitter) {
         this(className, classEmitter, 0L);
     }
@@ -48,6 +50,24 @@
     }
 
     /**
+     * Return the class that contains the code for this unit, null if not
+     * generated yet
+     *
+     * @return class with compile unit code
+     */
+    public Class<?> getCode() {
+        return clazz;
+    }
+
+    /**
+     * Set class when it exists. Only accessible from compiler
+     * @param clazz class with code for this compile unit
+     */
+    void setCode(final Class<?> clazz) {
+        this.clazz = clazz;
+    }
+
+    /**
      * Add weight to this compile unit
      * @param w weight to add
      */
--- a/src/jdk/nashorn/internal/codegen/Compiler.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/codegen/Compiler.java	Mon Apr 01 21:42:31 2013 -0700
@@ -45,11 +45,14 @@
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
+import java.util.logging.Level;
 import jdk.internal.dynalink.support.NameCodec;
 import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.FunctionNode;
 import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
+import jdk.nashorn.internal.ir.Node;
+import jdk.nashorn.internal.ir.visitor.NodeVisitor;
 import jdk.nashorn.internal.runtime.CodeInstaller;
 import jdk.nashorn.internal.runtime.DebugLogger;
 import jdk.nashorn.internal.runtime.ScriptEnvironment;
@@ -71,8 +74,6 @@
     /** Name of the objects package */
     public static final String OBJECTS_PACKAGE = "jdk/nashorn/internal/objects";
 
-    static final boolean LAZY_JIT = Options.getBooleanProperty("nashorn.compiler.lazy");
-
     private final Map<String, byte[]> bytecode;
 
     private final Set<CompileUnit> compileUnits;
@@ -164,7 +165,7 @@
      * and JIT it at once. This can lead to long startup time and fewer type
      * specializations
      */
-    final static CompilationSequence SEQUENCE_NORMAL = new CompilationSequence(
+    final static CompilationSequence SEQUENCE_EAGER = new CompilationSequence(
         CompilationPhase.CONSTANT_FOLDING_PHASE,
         CompilationPhase.LOWERING_PHASE,
         CompilationPhase.ATTRIBUTION_PHASE,
@@ -173,12 +174,15 @@
         CompilationPhase.BYTECODE_GENERATION_PHASE);
 
     final static CompilationSequence SEQUENCE_LAZY =
-        SEQUENCE_NORMAL.insertFirst(CompilationPhase.LAZY_INITIALIZATION_PHASE);
+        SEQUENCE_EAGER.insertFirst(CompilationPhase.LAZY_INITIALIZATION_PHASE);
 
-    final static CompilationSequence SEQUENCE_DEFAULT =
-        LAZY_JIT ?
-            SEQUENCE_LAZY :
-            SEQUENCE_NORMAL;
+    private static CompilationSequence sequence(final boolean lazy) {
+        return lazy ? SEQUENCE_LAZY : SEQUENCE_EAGER;
+    }
+
+    boolean isLazy() {
+        return sequence == SEQUENCE_LAZY;
+    }
 
     private static String lazyTag(final FunctionNode functionNode) {
         if (functionNode.isLazy()) {
@@ -212,11 +216,6 @@
                 append(safeSourceName(functionNode.getSource()));
 
         this.scriptName = sb.toString();
-
-        LOG.info("Initializing compiler for '" + functionNode.getName() + "' scriptName = " + scriptName + ", root function: '" + functionNode.getName() + "'");
-        if (functionNode.isLazy()) {
-            LOG.info(">>> This is a lazy recompilation triggered by a trampoline");
-        }
     }
 
     /**
@@ -227,7 +226,7 @@
      * @param strict       should this compilation use strict mode semantics
      */
     public Compiler(final CodeInstaller<ScriptEnvironment> installer, final FunctionNode functionNode, final boolean strict) {
-        this(installer.getOwner(), installer, functionNode, SEQUENCE_DEFAULT, strict);
+        this(installer.getOwner(), installer, functionNode, sequence(installer.getOwner()._lazy_compilation), strict);
     }
 
     /**
@@ -237,7 +236,7 @@
      * @param functionNode function node (in any available {@link CompilationState}) to compile
      */
     public Compiler(final CodeInstaller<ScriptEnvironment> installer, final FunctionNode functionNode) {
-        this(installer.getOwner(), installer, functionNode, SEQUENCE_DEFAULT, installer.getOwner()._strict);
+        this(installer.getOwner(), installer, functionNode, sequence(installer.getOwner()._lazy_compilation), installer.getOwner()._strict);
     }
 
     /**
@@ -247,28 +246,104 @@
      * @param functionNode functionNode to compile
      */
     public Compiler(final ScriptEnvironment env, final FunctionNode functionNode) {
-        this(env, null, functionNode, SEQUENCE_DEFAULT, env._strict);
+        this(env, null, functionNode, sequence(env._lazy_compilation), env._strict);
     }
 
     /**
      * Execute the compilation this Compiler was created with
+     * @params param types if known, for specialization
      * @throws CompilationException if something goes wrong
+     * @return this compiler, for possible chaining
      */
-    public void compile() throws CompilationException {
+    public Compiler compile() throws CompilationException {
+        return compile(null);
+    }
+
+    /**
+     * Execute the compilation this Compiler was created with
+     * @param paramTypes param types if known, for specialization
+     * @throws CompilationException if something goes wrong
+     * @return this compiler, for possible chaining
+     */
+    public Compiler compile(final Class<?> paramTypes) throws CompilationException {
         for (final String reservedName : RESERVED_NAMES) {
             functionNode.uniqueName(reservedName);
         }
 
+        final boolean fine = !LOG.levelAbove(Level.FINE);
+        final boolean info = !LOG.levelAbove(Level.INFO);
+
+        long time = 0L;
+
         for (final CompilationPhase phase : sequence) {
             phase.apply(this, functionNode);
-            final String end = phase.toString() + " done for function '" + functionNode.getName() + "'";
-            if (Timing.isEnabled()) {
-                final long duration = phase.getEndTime() - phase.getStartTime();
-                LOG.info(end + " in " + duration + " ms");
-            } else {
-                LOG.info(end);
+
+            final long duration = Timing.isEnabled() ? (phase.getEndTime() - phase.getStartTime()) : 0L;
+            time += duration;
+
+            if (fine) {
+                final StringBuilder sb = new StringBuilder();
+
+                sb.append(phase.toString()).
+                    append(" done for function '").
+                    append(functionNode.getName()).
+                    append('\'');
+
+                if (duration > 0L) {
+                    sb.append(" in ").
+                        append(duration).
+                        append(" ms ");
+                }
+
+                LOG.fine(sb.toString());
             }
         }
+
+        if (info) {
+            final StringBuilder sb = new StringBuilder();
+            sb.append("Compile job for '").
+                append(functionNode.getName()).
+                append("' finished");
+
+            if (time > 0L) {
+                sb.append(" in ").
+                    append(time).
+                    append(" ms");
+            }
+
+            LOG.info(sb.toString());
+        }
+
+        return this;
+    }
+
+    private Class<?> install(final String className, final byte[] code) {
+        LOG.fine("Installing class " + className);
+
+        final Class<?> clazz = installer.install(Compiler.binaryName(className), code);
+
+        try {
+            final Source   source    = getSource();
+            final Object[] constants = getConstantData().toArray();
+            // Need doPrivileged because these fields are private
+            AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
+                @Override
+                public Void run() throws Exception {
+                    //use reflection to write source and constants table to installed classes
+                    final Field sourceField    = clazz.getDeclaredField(SOURCE.tag());
+                    final Field constantsField = clazz.getDeclaredField(CONSTANTS.tag());
+                    sourceField.setAccessible(true);
+                    constantsField.setAccessible(true);
+                    sourceField.set(null, source);
+                    constantsField.set(null, constants);
+                    return null;
+                }
+            });
+        } catch (final PrivilegedActionException e) {
+            throw new RuntimeException(e);
+        }
+
+        return clazz;
     }
 
     /**
@@ -280,46 +355,68 @@
 
         assert functionNode.hasState(CompilationState.EMITTED) : functionNode.getName() + " has no bytecode and cannot be installed";
 
-        Class<?> rootClass = null;
+        final Map<String, Class<?>> installedClasses = new HashMap<>();
+
+        final String   rootClassName = firstCompileUnitName();
+        final byte[]   rootByteCode  = bytecode.get(rootClassName);
+        final Class<?> rootClass     = install(rootClassName, rootByteCode);
+
+        int length = rootByteCode.length;
+
+        installedClasses.put(rootClassName, rootClass);
 
         for (final Entry<String, byte[]> entry : bytecode.entrySet()) {
-            final String     className = entry.getKey();
-            LOG.fine("Installing class " + className);
+            final String className = entry.getKey();
+            if (className.equals(rootClassName)) {
+                continue;
+            }
+            final byte[] code = entry.getValue();
+            length += code.length;
 
-            final byte[]     code  = entry.getValue();
-            final Class<?>   clazz = installer.install(Compiler.binaryName(className), code);
+            installedClasses.put(className, install(className, code));
+        }
 
-            if (rootClass == null && firstCompileUnitName().equals(className)) {
-                rootClass = clazz;
+        for (final CompileUnit unit : compileUnits) {
+            unit.setCode(installedClasses.get(unit.getUnitClassName()));
+        }
+
+        functionNode.accept(new NodeVisitor() {
+            @Override
+            public Node enterFunctionNode(final FunctionNode node) {
+                if (node.isLazy()) {
+                    return null;
+                }
+                node.setState(CompilationState.INSTALLED);
+                return node;
             }
+        });
 
-            try {
-                final Source source = getSource();
-                final Object[] constants = getConstantData().toArray();
-                // Need doPrivileged because these fields are private
-                AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
-                    @Override
-                    public Void run() throws Exception {
-                        //use reflection to write source and constants table to installed classes
-                        final Field sourceField = clazz.getDeclaredField(SOURCE.tag());
-                        final Field constantsField = clazz.getDeclaredField(CONSTANTS.tag());
-                        sourceField.setAccessible(true);
-                        constantsField.setAccessible(true);
-                        sourceField.set(null, source);
-                        constantsField.set(null, constants);
-                        return null;
-                    }
-                });
-            } catch (final PrivilegedActionException e) {
-                throw new RuntimeException(e);
+        final StringBuilder sb;
+        if (LOG.isEnabled()) {
+            sb = new StringBuilder();
+            sb.append("Installed class '").
+                append(rootClass.getSimpleName()).
+                append('\'').
+                append(" bytes=").
+                append(length).
+                append('.');
+            if (bytecode.size() > 1) {
+                sb.append(' ').append(bytecode.size()).append(" compile units.");
+            }
+        } else {
+            sb = null;
+        }
+
+        if (Timing.isEnabled()) {
+            final long duration = System.currentTimeMillis() - t0;
+            Timing.accumulateTime("[Code Installation]", duration);
+            if (sb != null) {
+                sb.append(" Install time: ").append(duration).append(" ms");
             }
         }
 
-        LOG.info("Installed root class: " + rootClass + " and " + bytecode.size() + " compile unit classes");
-        if (Timing.isEnabled()) {
-            final long duration = System.currentTimeMillis() - t0;
-            Timing.accumulateTime("[Code Installation]", duration);
-            LOG.info("Installation time: " + duration + " ms");
+        if (sb != null) {
+            LOG.info(sb.toString());
         }
 
         return rootClass;
@@ -444,8 +541,6 @@
      * TODO: We currently generate no overflow checks so this is
      * disabled
      *
-     * @see #shouldUseIntegers()
-     *
      * @return true if arithmetic operations should not widen integer
      *   operands by default.
      */
@@ -460,4 +555,5 @@
         assert !USE_INT_ARITH : "Integer arithmetic is not enabled";
     }
 
+
 }
--- a/src/jdk/nashorn/internal/codegen/FinalizeTypes.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/codegen/FinalizeTypes.java	Mon Apr 01 21:42:31 2013 -0700
@@ -40,13 +40,14 @@
 import jdk.nashorn.internal.ir.ExecuteNode;
 import jdk.nashorn.internal.ir.ForNode;
 import jdk.nashorn.internal.ir.FunctionNode;
+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.LiteralNode;
 import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
 import jdk.nashorn.internal.ir.Node;
-import jdk.nashorn.internal.ir.ReferenceNode;
 import jdk.nashorn.internal.ir.ReturnNode;
 import jdk.nashorn.internal.ir.RuntimeNode;
 import jdk.nashorn.internal.ir.RuntimeNode.Request;
@@ -84,11 +85,13 @@
 
     private static final DebugLogger LOG = new DebugLogger("finalize");
 
+    private final LexicalContext lexicalContext = new LexicalContext();
+
     FinalizeTypes() {
     }
 
     @Override
-    public Node leave(final CallNode callNode) {
+    public Node leaveCallNode(final CallNode callNode) {
         final EvalArgs evalArgs = callNode.getEvalArgs();
         if (evalArgs != null) {
             evalArgs.setCode(evalArgs.getCode().accept(this));
@@ -96,15 +99,14 @@
 
         // AccessSpecializer - call return type may change the access for this location
         final Node function = callNode.getFunction();
-        if (function instanceof ReferenceNode) {
-            setTypeOverride(callNode, ((ReferenceNode)function).getReference().getType());
+        if (function instanceof FunctionNode) {
+            return setTypeOverride(callNode, ((FunctionNode)function).getReturnType());
         }
         return callNode;
     }
 
     private Node leaveUnary(final UnaryNode unaryNode) {
-        unaryNode.setRHS(convert(unaryNode.rhs(), unaryNode.getType()));
-        return unaryNode;
+        return unaryNode.setRHS(convert(unaryNode.rhs(), unaryNode.getType()));
     }
 
     @Override
@@ -125,8 +127,7 @@
 
     @Override
     public Node leaveDECINC(final UnaryNode unaryNode) {
-        specialize(unaryNode);
-        return unaryNode;
+        return specialize(unaryNode).node;
     }
 
     @Override
@@ -158,9 +159,7 @@
             }
         }
 
-        binaryNode.setLHS(convert(lhs, type));
-        binaryNode.setRHS(convert(rhs, type));
-        return binaryNode;
+        return binaryNode.setLHS(convert(lhs, type)).setRHS(convert(rhs, type));
     }
 
     @Override
@@ -170,12 +169,13 @@
 
     @Override
     public Node leaveASSIGN(final BinaryNode binaryNode) {
-        Type destType = specialize(binaryNode);
+        final SpecializedNode specialized = specialize(binaryNode);
+        final BinaryNode specBinaryNode = (BinaryNode)specialized.node;
+        Type destType = specialized.type;
         if (destType == null) {
-            destType = binaryNode.getType();
+            destType = specBinaryNode.getType();
         }
-        binaryNode.setRHS(convert(binaryNode.rhs(), destType));
-        return binaryNode;
+        return specBinaryNode.setRHS(convert(specBinaryNode.rhs(), destType));
     }
 
     @Override
@@ -235,40 +235,40 @@
 
     @Override
     public Node leaveBIT_AND(BinaryNode binaryNode) {
-        assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isInteger() : binaryNode.getSymbol();
+        assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isInteger() : "int coercion expected: " + binaryNode.getSymbol();
         return leaveBinary(binaryNode, Type.INT, Type.INT);
     }
 
     @Override
     public Node leaveBIT_OR(BinaryNode binaryNode) {
-        assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isInteger();
+        assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isInteger() : "int coercion expected: " + binaryNode.getSymbol();
         return leaveBinary(binaryNode, Type.INT, Type.INT);
     }
 
     @Override
     public Node leaveBIT_XOR(BinaryNode binaryNode) {
-        assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isInteger();
+        assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isInteger() : "int coercion expected: " + binaryNode.getSymbol();
         return leaveBinary(binaryNode, Type.INT, Type.INT);
     }
 
     @Override
     public Node leaveCOMMALEFT(final BinaryNode binaryNode) {
         assert binaryNode.getSymbol() != null;
-        binaryNode.setRHS(discard(binaryNode.rhs()));
-        // AccessSpecializer - the type of rhs, which is the remaining value of this node may have changed
+        final BinaryNode newBinaryNode = (BinaryNode)binaryNode.setRHS(discard(binaryNode.rhs()));
+        // AccessSpecializer - the type of lhs, which is the remaining value of this node may have changed
         // in that case, update the node type as well
-        propagateType(binaryNode, binaryNode.lhs().getType());
-        return binaryNode;
+        propagateType(newBinaryNode, newBinaryNode.lhs().getType());
+        return newBinaryNode;
     }
 
     @Override
     public Node leaveCOMMARIGHT(final BinaryNode binaryNode) {
         assert binaryNode.getSymbol() != null;
-        binaryNode.setLHS(discard(binaryNode.lhs()));
+        final BinaryNode newBinaryNode = binaryNode.setLHS(discard(binaryNode.lhs()));
         // AccessSpecializer - the type of rhs, which is the remaining value of this node may have changed
         // in that case, update the node type as well
-        propagateType(binaryNode, binaryNode.rhs().getType());
-        return binaryNode;
+        propagateType(newBinaryNode, newBinaryNode.rhs().getType());
+        return newBinaryNode;
     }
 
     @Override
@@ -344,7 +344,7 @@
 
     @Override
     public Node leaveSHR(final BinaryNode binaryNode) {
-        assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isLong();
+        assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isLong() : "long coercion expected: " + binaryNode.getSymbol();
         return leaveBinary(binaryNode, Type.INT, Type.INT);
     }
 
@@ -354,13 +354,20 @@
     }
 
     @Override
-    public Node enter(final Block block) {
+    public Node enterBlock(final Block block) {
+        lexicalContext.push(block);
         updateSymbols(block);
         return block;
     }
 
     @Override
-    public Node leave(final CatchNode catchNode) {
+    public Node leaveBlock(Block block) {
+        lexicalContext.pop(block);
+        return super.leaveBlock(block);
+    }
+
+    @Override
+    public Node leaveCatchNode(final CatchNode catchNode) {
         final Node exceptionCondition = catchNode.getExceptionCondition();
         if (exceptionCondition != null) {
             catchNode.setExceptionCondition(convert(exceptionCondition, Type.BOOLEAN));
@@ -369,23 +376,23 @@
     }
 
     @Override
-    public Node enter(final DoWhileNode doWhileNode) {
-        return enter((WhileNode)doWhileNode);
+    public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
+        return enterWhileNode(doWhileNode);
     }
 
     @Override
-    public Node leave(final DoWhileNode doWhileNode) {
-        return leave((WhileNode)doWhileNode);
+    public Node leaveDoWhileNode(final DoWhileNode doWhileNode) {
+        return leaveWhileNode(doWhileNode);
     }
 
     @Override
-    public Node leave(final ExecuteNode executeNode) {
+    public Node leaveExecuteNode(final ExecuteNode executeNode) {
         executeNode.setExpression(discard(executeNode.getExpression()));
         return executeNode;
     }
 
     @Override
-    public Node leave(final ForNode forNode) {
+    public Node leaveForNode(final ForNode forNode) {
         final Node init   = forNode.getInit();
         final Node test   = forNode.getTest();
         final Node modify = forNode.getModify();
@@ -413,11 +420,12 @@
     }
 
     @Override
-    public Node enter(final FunctionNode functionNode) {
+    public Node enterFunctionNode(final FunctionNode functionNode) {
         if (functionNode.isLazy()) {
             return null;
         }
 
+        lexicalContext.push(functionNode);
         // If the function doesn't need a callee, we ensure its __callee__ symbol doesn't get a slot. We can't do
         // this earlier, as access to scoped variables, self symbol, etc. in previous phases can all trigger the
         // need for the callee.
@@ -432,18 +440,26 @@
         }
 
         updateSymbols(functionNode);
+        functionNode.setState(CompilationState.FINALIZED);
+
         return functionNode;
     }
 
     @Override
-    public Node leave(final IfNode ifNode) {
+    public Node leaveFunctionNode(FunctionNode functionNode) {
+        lexicalContext.pop(functionNode);
+        return super.leaveFunctionNode(functionNode);
+    }
+
+    @Override
+    public Node leaveIfNode(final IfNode ifNode) {
         ifNode.setTest(convert(ifNode.getTest(), Type.BOOLEAN));
         return ifNode;
     }
 
     @SuppressWarnings("rawtypes")
     @Override
-    public Node enter(final LiteralNode literalNode) {
+    public Node enterLiteralNode(final LiteralNode literalNode) {
         if (literalNode instanceof ArrayLiteralNode) {
             final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode)literalNode;
             final Node[]           array            = arrayLiteralNode.getValue();
@@ -461,7 +477,7 @@
     }
 
     @Override
-    public Node leave(final ReturnNode returnNode) {
+    public Node leaveReturnNode(final ReturnNode returnNode) {
         final Node expr = returnNode.getExpression();
         if (expr != null) {
             returnNode.setExpression(convert(expr, getCurrentFunctionNode().getReturnType()));
@@ -470,7 +486,7 @@
     }
 
     @Override
-    public Node leave(final RuntimeNode runtimeNode) {
+    public Node leaveRuntimeNode(final RuntimeNode runtimeNode) {
         final List<Node> args = runtimeNode.getArgs();
         for (final Node arg : args) {
             assert !arg.getType().isUnknown();
@@ -479,7 +495,7 @@
     }
 
     @Override
-    public Node leave(final SwitchNode switchNode) {
+    public Node leaveSwitchNode(final SwitchNode switchNode) {
         final Node           expression  = switchNode.getExpression();
         final List<CaseNode> cases       = switchNode.getCases();
         final boolean        allInteger  = switchNode.getTag().getSymbolType().isInteger();
@@ -498,34 +514,34 @@
     }
 
     @Override
-    public Node leave(final TernaryNode ternaryNode) {
-        ternaryNode.setLHS(convert(ternaryNode.lhs(), Type.BOOLEAN));
-        return ternaryNode;
+    public Node leaveTernaryNode(final TernaryNode ternaryNode) {
+        return ternaryNode.setLHS(convert(ternaryNode.lhs(), Type.BOOLEAN));
     }
 
     @Override
-    public Node leave(final ThrowNode throwNode) {
+    public Node leaveThrowNode(final ThrowNode throwNode) {
         throwNode.setExpression(convert(throwNode.getExpression(), Type.OBJECT));
         return throwNode;
     }
 
     @Override
-    public Node leave(final VarNode varNode) {
-
+    public Node leaveVarNode(final VarNode varNode) {
         final Node rhs = varNode.getInit();
         if (rhs != null) {
-            Type destType = specialize(varNode);
+            final SpecializedNode specialized = specialize(varNode);
+            final VarNode specVarNode = (VarNode)specialized.node;
+            Type destType = specialized.type;
             if (destType == null) {
-                destType = varNode.getType();
+                destType = specVarNode.getType();
             }
-            assert varNode.hasType() : varNode + " doesn't have a type";
-            varNode.setInit(convert(rhs, destType));
+            assert specVarNode.hasType() : specVarNode + " doesn't have a type";
+            return specVarNode.setInit(convert(rhs, destType));
         }
         return varNode;
     }
 
     @Override
-    public Node leave(final WhileNode whileNode) {
+    public Node leaveWhileNode(final WhileNode whileNode) {
         final Node test = whileNode.getTest();
         if (test != null) {
             whileNode.setTest(convert(test, Type.BOOLEAN));
@@ -534,7 +550,7 @@
     }
 
     @Override
-    public Node leave(final WithNode withNode) {
+    public Node leaveWithNode(final WithNode withNode) {
         withNode.setExpression(convert(withNode.getExpression(), Type.OBJECT));
         return withNode;
     }
@@ -553,14 +569,14 @@
      * that scope and slot information is correct for every symbol
      * @param block block for which to to finalize type info.
      */
-    private static void updateSymbols(final Block block) {
+    private void updateSymbols(final Block block) {
         if (!block.needsScope()) {
             return; // nothing to do
         }
 
-        assert !(block instanceof FunctionNode) || block.getFunction() == block;
+        final FunctionNode functionNode = lexicalContext.getFunction(block);
+        assert !(block instanceof FunctionNode) || functionNode == block;
 
-        final FunctionNode functionNode   = block.getFunction();
         final List<Symbol> symbols        = block.getFrame().getSymbols();
         final boolean      allVarsInScope = functionNode.allVarsInScope();
         final boolean      isVarArg       = functionNode.isVarArg();
@@ -629,10 +645,7 @@
             break;
         }
 
-        binaryNode.setLHS(convert(lhs, widest));
-        binaryNode.setRHS(convert(rhs, widest));
-
-        return binaryNode;
+        return binaryNode.setLHS(convert(lhs, widest)).setRHS(convert(rhs, widest));
     }
 
     /**
@@ -654,9 +667,7 @@
     }
 
     private Node leaveBinary(final BinaryNode binaryNode, final Type lhsType, final Type rhsType) {
-        binaryNode.setLHS(convert(binaryNode.lhs(), lhsType));
-        binaryNode.setRHS(convert(binaryNode.rhs(), rhsType));
-        return binaryNode;
+        return binaryNode.setLHS(convert(binaryNode.lhs(), lhsType)).setRHS(convert(binaryNode.rhs(), rhsType));
     }
 
     /**
@@ -677,7 +688,7 @@
             }
 
             @Override
-            public Node enter(final IdentNode identNode) {
+            public Node enterIdentNode(final IdentNode identNode) {
                 if (!exclude.contains(identNode)) {
                     setCanBePrimitive(identNode.getSymbol());
                 }
@@ -685,26 +696,36 @@
             }
 
             @Override
-            public Node enter(final AccessNode accessNode) {
+            public Node enterAccessNode(final AccessNode accessNode) {
                 setCanBePrimitive(accessNode.getProperty().getSymbol());
                 return null;
             }
 
             @Override
-            public Node enter(final IndexNode indexNode) {
+            public Node enterIndexNode(final IndexNode indexNode) {
                 exclude.add(indexNode.getBase()); //prevent array base node to be flagged as primitive, but k in a[k++] is fine
                 return indexNode;
             }
         });
     }
 
-    private static Type specialize(final Assignment<?> assignment) {
+    private static class SpecializedNode {
+        final Node node;
+        final Type type;
+
+        SpecializedNode(Node node, Type type) {
+            this.node = node;
+            this.type = type;
+        }
+    }
+
+    private static <T extends Node> SpecializedNode specialize(final Assignment<T> assignment) {
         final Node node = ((Node)assignment);
-        final Node lhs = assignment.getAssignmentDest();
+        final T lhs = assignment.getAssignmentDest();
         final Node rhs = assignment.getAssignmentSource();
 
         if (!canHaveCallSiteType(lhs)) {
-            return null;
+            return new SpecializedNode(node, null);
         }
 
         final Type to;
@@ -716,13 +737,13 @@
 
         if (!isSupportedCallSiteType(to)) {
             //meaningless to specialize to boolean or object
-            return null;
+            return new SpecializedNode(node, null);
         }
 
-        setTypeOverride(lhs, to);
-        propagateType(node, to);
+        final Node newNode = assignment.setAssignmentDest(setTypeOverride(lhs, to));
+        propagateType(newNode, to);
 
-        return to;
+        return new SpecializedNode(newNode, to);
     }
 
 
@@ -734,7 +755,7 @@
      * @return true if node can have a callsite type
      */
     private static boolean canHaveCallSiteType(final Node node) {
-        return node instanceof TypeOverride && ((TypeOverride)node).canHaveCallSiteType();
+        return node instanceof TypeOverride && ((TypeOverride<?>)node).canHaveCallSiteType();
     }
 
     /**
@@ -760,7 +781,8 @@
      * @param node    node for which to change type
      * @param to      new type
      */
-    private static void setTypeOverride(final Node node, final Type to) {
+    @SuppressWarnings("unchecked")
+    private static <T extends Node> T setTypeOverride(final T node, final Type to) {
         final Type from = node.getType();
         if (!node.getType().equals(to)) {
             LOG.info("Changing call override type for '" + node + "' from " + node.getType() + " to " + to);
@@ -769,7 +791,7 @@
             }
         }
         LOG.info("Type override for lhs in '" + node + "' => " + to);
-        ((TypeOverride)node).setType(to);
+        return ((TypeOverride<T>)node).setType(to);
     }
 
     /**
@@ -814,8 +836,8 @@
             }
         } else {
             if (canHaveCallSiteType(node) && isSupportedCallSiteType(to)) {
-                setTypeOverride(node, to);
-                return resultNode;
+                assert node instanceof TypeOverride;
+                return setTypeOverride(node, to);
             }
             resultNode = new UnaryNode(node.getSource(), Token.recast(node.getToken(), TokenType.CONVERT), node);
         }
--- a/src/jdk/nashorn/internal/codegen/FoldConstants.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/codegen/FoldConstants.java	Mon Apr 01 21:42:31 2013 -0700
@@ -30,6 +30,8 @@
 import jdk.nashorn.internal.ir.Block;
 import jdk.nashorn.internal.ir.EmptyNode;
 import jdk.nashorn.internal.ir.ExecuteNode;
+import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
 import jdk.nashorn.internal.ir.IfNode;
 import jdk.nashorn.internal.ir.LiteralNode;
 import jdk.nashorn.internal.ir.Node;
@@ -52,7 +54,7 @@
     }
 
     @Override
-    public Node leave(final UnaryNode unaryNode) {
+    public Node leaveUnaryNode(final UnaryNode unaryNode) {
         final LiteralNode<?> literalNode = new UnaryNodeConstantEvaluator(unaryNode).eval();
         if (literalNode != null) {
             LOG.info("Unary constant folded " + unaryNode + " to " + literalNode);
@@ -62,7 +64,7 @@
     }
 
     @Override
-    public Node leave(final BinaryNode binaryNode) {
+    public Node leaveBinaryNode(final BinaryNode binaryNode) {
         final LiteralNode<?> literalNode = new BinaryNodeConstantEvaluator(binaryNode).eval();
         if (literalNode != null) {
             LOG.info("Binary constant folded " + binaryNode + " to " + literalNode);
@@ -72,7 +74,21 @@
     }
 
     @Override
-    public Node leave(final IfNode ifNode) {
+    public Node enterFunctionNode(final FunctionNode functionNode) {
+        if (functionNode.isLazy()) {
+            return null;
+        }
+        return functionNode;
+    }
+
+    @Override
+    public Node leaveFunctionNode(final FunctionNode functionNode) {
+        functionNode.setState(CompilationState.CONSTANT_FOLDED);
+        return functionNode;
+    }
+
+    @Override
+    public Node leaveIfNode(final IfNode ifNode) {
         final Node test = ifNode.getTest();
         if (test instanceof LiteralNode) {
             final Block shortCut = ((LiteralNode<?>)test).isTrue() ? ifNode.getPass() : ifNode.getFail();
@@ -85,7 +101,7 @@
     }
 
     @Override
-    public Node leave(final TernaryNode ternaryNode) {
+    public Node leaveTernaryNode(final TernaryNode ternaryNode) {
         final Node test = ternaryNode.lhs();
         if (test instanceof LiteralNode) {
             return ((LiteralNode<?>)test).isTrue() ? ternaryNode.rhs() : ternaryNode.third();
--- a/src/jdk/nashorn/internal/codegen/FunctionSignature.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/codegen/FunctionSignature.java	Mon Apr 01 21:42:31 2013 -0700
@@ -146,7 +146,7 @@
 
     /**
      * Create a function signature given a function node, using as much
-     * type information for parameters and return types that is availabe
+     * type information for parameters and return types that is available
      *
      * @param functionNode the function node
      */
@@ -155,7 +155,7 @@
             true,
             functionNode.needsCallee(),
             functionNode.getReturnType(),
-            (functionNode.isVarArg() && !functionNode.isScript()) ?
+            (functionNode.isVarArg() && !functionNode.isProgram()) ?
                 null :
                 functionNode.getParameters());
     }
@@ -202,6 +202,14 @@
         return methodType;
     }
 
+    /**
+     * Return the return type for this function signature
+     * @return the return type
+     */
+    public Type getReturnType() {
+        return returnType;
+    }
+
     private static Type[] objectArgs(final int nArgs) {
         final Type[] array = new Type[nArgs];
         for (int i = 0; i < nArgs; i++) {
--- a/src/jdk/nashorn/internal/codegen/Lower.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/codegen/Lower.java	Mon Apr 01 21:42:31 2013 -0700
@@ -37,8 +37,8 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Deque;
+import java.util.Iterator;
 import java.util.List;
-import jdk.nashorn.internal.ir.AccessNode;
 import jdk.nashorn.internal.ir.BaseNode;
 import jdk.nashorn.internal.ir.BinaryNode;
 import jdk.nashorn.internal.ir.Block;
@@ -52,11 +52,12 @@
 import jdk.nashorn.internal.ir.ExecuteNode;
 import jdk.nashorn.internal.ir.ForNode;
 import jdk.nashorn.internal.ir.FunctionNode;
+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.LabelNode;
 import jdk.nashorn.internal.ir.LabeledNode;
+import jdk.nashorn.internal.ir.LexicalContext;
 import jdk.nashorn.internal.ir.LineNumberNode;
 import jdk.nashorn.internal.ir.LiteralNode;
 import jdk.nashorn.internal.ir.Node;
@@ -102,6 +103,8 @@
 
     private List<Node> statements;
 
+    private LexicalContext lexicalContext = new LexicalContext();
+
     /**
      * Constructor.
      *
@@ -113,14 +116,15 @@
     }
 
     @Override
-    public Node enter(final Block block) {
+    public Node enterBlock(final Block block) {
         final Node       savedLastStatement = lastStatement;
         final List<Node> savedStatements    = statements;
-
+        lexicalContext.push(block);
         try {
             this.statements = new ArrayList<>();
+            NodeVisitor visitor = this;
             for (final Node statement : block.getStatements()) {
-                statement.accept(this);
+                statement.accept(visitor);
                 /*
                  * This is slightly unsound, for example if we have a loop with
                  * a guarded statement like if (x) continue in the body and the
@@ -132,7 +136,7 @@
                  */
                 if (lastStatement != null && lastStatement.isTerminal()) {
                     copyTerminal(block, lastStatement);
-                    break;
+                    visitor = new DeadCodeVarDeclarationVisitor();
                 }
             }
             block.setStatements(statements);
@@ -140,18 +144,19 @@
         } finally {
             this.statements = savedStatements;
             this.lastStatement = savedLastStatement;
+            lexicalContext.pop(block);
         }
 
         return null;
     }
 
     @Override
-    public Node enter(final BreakNode breakNode) {
+    public Node enterBreakNode(final BreakNode breakNode) {
         return enterBreakOrContinue(breakNode);
     }
 
     @Override
-    public Node enter(final CallNode callNode) {
+    public Node enterCallNode(final CallNode callNode) {
         final Node function = markerFunction(callNode.getFunction());
         callNode.setFunction(function);
         checkEval(callNode); //check if this is an eval call and store the information
@@ -159,44 +164,44 @@
     }
 
     @Override
-    public Node leave(final CaseNode caseNode) {
+    public Node leaveCaseNode(final CaseNode caseNode) {
         caseNode.copyTerminalFlags(caseNode.getBody());
         return caseNode;
     }
 
     @Override
-    public Node leave(final CatchNode catchNode) {
+    public Node leaveCatchNode(final CatchNode catchNode) {
         catchNode.copyTerminalFlags(catchNode.getBody());
         addStatement(catchNode);
         return catchNode;
     }
 
     @Override
-    public Node enter(final ContinueNode continueNode) {
+    public Node enterContinueNode(final ContinueNode continueNode) {
         return enterBreakOrContinue(continueNode);
     }
 
     @Override
-    public Node enter(final DoWhileNode doWhileNode) {
-        return enter((WhileNode)doWhileNode);
+    public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
+        return enterWhileNode(doWhileNode);
     }
 
     @Override
-    public Node leave(final DoWhileNode doWhileNode) {
-        return leave((WhileNode)doWhileNode);
+    public Node leaveDoWhileNode(final DoWhileNode doWhileNode) {
+        return leaveWhileNode(doWhileNode);
     }
 
     @Override
-    public Node enter(final EmptyNode emptyNode) {
+    public Node enterEmptyNode(final EmptyNode emptyNode) {
         return null;
     }
 
     @Override
-    public Node leave(final ExecuteNode executeNode) {
+    public Node leaveExecuteNode(final ExecuteNode executeNode) {
         final Node expr = executeNode.getExpression();
 
-        if (getCurrentFunctionNode().isScript()) {
-            if (!(expr instanceof Block)) {
+        if (getCurrentFunctionNode().isProgram()) {
+            if (!(expr instanceof Block) || expr instanceof FunctionNode) { // it's not a block, but can be a function
                 if (!isInternalExpression(expr) && !isEvalResultAssignment(expr)) {
                     executeNode.setExpression(new BinaryNode(executeNode.getSource(), Token.recast(executeNode.getToken(), TokenType.ASSIGN),
                             getCurrentFunctionNode().getResultNode(),
@@ -212,13 +217,13 @@
     }
 
     @Override
-    public Node enter(final ForNode forNode) {
+    public Node enterForNode(final ForNode forNode) {
         nest(forNode);
         return forNode;
     }
 
     @Override
-    public Node leave(final ForNode forNode) {
+    public Node leaveForNode(final ForNode forNode) {
         final Node  test = forNode.getTest();
         final Block body = forNode.getBody();
 
@@ -236,6 +241,7 @@
 
         if (!forNode.isForIn() && conservativeAlwaysTrue(test)) {
             forNode.setTest(null);
+            setHasGoto(forNode);
             setTerminal(forNode, !escapes);
         }
 
@@ -245,18 +251,16 @@
     }
 
     @Override
-    public Node enter(final FunctionNode functionNode) {
+    public Node enterFunctionNode(final FunctionNode functionNode) {
         LOG.info("START FunctionNode: " + functionNode.getName());
 
         if (functionNode.isLazy()) {
             LOG.info("LAZY: " + functionNode.getName());
             return null;
         }
-
+        lexicalContext.push(functionNode);
         initFunctionNode(functionNode);
 
-        Node initialEvalResult = LiteralNode.newInstance(functionNode, ScriptRuntime.UNDEFINED);
-
         nest(functionNode);
 
         /*
@@ -270,60 +274,40 @@
         statements    = new ArrayList<>();
         lastStatement = null;
 
-        // for initial eval result is the last declared function
-        for (final FunctionNode nestedFunction : functionNode.getFunctions()) {
-            final IdentNode ident = nestedFunction.getIdent();
-            if (ident != null && nestedFunction.isStatement()) {
-                initialEvalResult = new IdentNode(ident);
-            }
-        }
-
         if (functionNode.needsSelfSymbol()) {
             //function needs to start with var funcIdent = __callee_;
             statements.add(functionNode.getSelfSymbolInit().accept(this));
         }
 
+        NodeVisitor visitor = this;
         try {
-            // Every nested function needs a definition in the outer function with its name. Add these.
-            for (final FunctionNode nestedFunction : functionNode.getFunctions()) {
-                final VarNode varNode = nestedFunction.getFunctionVarNode();
-                if (varNode != null) {
-                    final LineNumberNode lineNumberNode = nestedFunction.getFunctionVarLineNumberNode();
-                    if (lineNumberNode != null) {
-                        lineNumberNode.accept(this);
-                    }
-                    varNode.accept(this);
-                    varNode.setIsFunctionVarNode();
+            //do the statements - this fills the block with code
+            boolean needsInitialEvalResult = functionNode.isProgram();
+            for (final Node statement : functionNode.getStatements()) {
+                // If this function is a program, then insert an assignment to the initial eval result after all
+                // function declarations.
+                if(needsInitialEvalResult && !(statement instanceof LineNumberNode || (statement instanceof VarNode && ((VarNode)statement).isFunctionDeclaration()))) {
+                    addInitialEvalResult(functionNode);
+                    needsInitialEvalResult = false;
                 }
-            }
-
-            if (functionNode.isScript()) {
-                new ExecuteNode(functionNode.getSource(), functionNode.getFirstToken(), functionNode.getFinish(), initialEvalResult).accept(this);
-            }
-
-            //do the statements - this fills the block with code
-            for (final Node statement : functionNode.getStatements()) {
-                statement.accept(this);
+                statement.accept(visitor);
                 //If there are unused terminated endpoints in the function, we need
                 // to add a "return undefined" in those places for correct semantics
                 LOG.info("Checking lastStatement="+lastStatement+" for terminal flags");
                 if (lastStatement != null && lastStatement.hasTerminalFlags()) {
                     copyTerminal(functionNode, lastStatement);
-                    break;
+                    assert !needsInitialEvalResult;
+                    visitor = new DeadCodeVarDeclarationVisitor();
                 }
             }
-
+            if(needsInitialEvalResult) {
+                addInitialEvalResult(functionNode);
+            }
             functionNode.setStatements(statements);
 
             if (!functionNode.isTerminal()) {
                 guaranteeReturn(functionNode);
             }
-
-            //lower all nested functions
-            for (final FunctionNode nestedFunction : functionNode.getFunctions()) {
-                nestedFunction.accept(this);
-            }
-
         } finally {
             statements    = savedStatements;
             lastStatement = savedLastStatement;
@@ -331,17 +315,67 @@
 
         LOG.info("END FunctionNode: " + functionNode.getName());
         unnest(functionNode);
+        lexicalContext.pop(functionNode);
+
+        functionNode.setState(CompilationState.LOWERED);
 
         return null;
     }
 
+    /**
+     * This visitor is used to go over statements after a terminal statement. Those statements are dead code, but the
+     * var declarations in them still have the effect of declaring a local variable on the function level. Therefore,
+     * they aren't really dead code and must be preserved. Note that they're only preserved as no-op declarations; their
+     * initializers are wiped out as those are, in fact, dead code.
+     */
+    private class DeadCodeVarDeclarationVisitor extends NodeOperatorVisitor {
+        DeadCodeVarDeclarationVisitor() {
+        }
+
+        @Override
+        public Node enterVarNode(VarNode varNode) {
+            // Can't ever see a function declaration, as this visitor is only ever used after a terminal statement was
+            // encountered, and all function declarations precede any terminal statements.
+            assert !varNode.isFunctionDeclaration();
+            if(varNode.getInit() == null) {
+                // No initializer, just pass it to Lower.
+                return varNode.accept(Lower.this);
+            }
+            // Wipe out the initializer and then pass it to Lower.
+            return varNode.setInit(null).accept(Lower.this);
+        }
+    }
+
+    private void addInitialEvalResult(final FunctionNode functionNode) {
+        new ExecuteNode(functionNode.getSource(), functionNode.getFirstToken(), functionNode.getFinish(),
+                getInitialEvalResult(functionNode)).accept(this);
+    }
+
+    /**
+     * Result of initial result of evaluating a particular program, which is either the last function it declares, or
+     * undefined if it doesn't declare any functions.
+     * @param program
+     * @return the initial result of evaluating the program
+     */
+    private static Node getInitialEvalResult(final FunctionNode program) {
+        IdentNode lastFnName = null;
+        for (final FunctionNode fn : program.getDeclaredFunctions()) {
+            assert fn.isDeclared();
+            final IdentNode fnName = fn.getIdent();
+            if(fnName != null) {
+                lastFnName = fnName;
+            }
+        }
+        return lastFnName != null ? new IdentNode(lastFnName) : LiteralNode.newInstance(program, ScriptRuntime.UNDEFINED);
+    }
+
     @Override
-    public Node enter(final IfNode ifNode) {
+    public Node enterIfNode(final IfNode ifNode) {
         return nest(ifNode);
     }
 
     @Override
-    public Node leave(final IfNode ifNode) {
+    public Node leaveIfNode(final IfNode ifNode) {
         final Node pass = ifNode.getPass();
         final Node fail = ifNode.getFail();
 
@@ -356,7 +390,7 @@
     }
 
     @Override
-    public Node enter(LabelNode labelNode) {
+    public Node enterLabelNode(LabelNode labelNode) {
         final Block body = labelNode.getBody();
         body.accept(this);
         copyTerminal(labelNode, body);
@@ -365,13 +399,13 @@
     }
 
     @Override
-    public Node enter(final LineNumberNode lineNumberNode) {
+    public Node enterLineNumberNode(final LineNumberNode lineNumberNode) {
         addStatement(lineNumberNode, false); // don't put it in lastStatement cache
         return null;
     }
 
     @Override
-    public Node enter(final ReturnNode returnNode) {
+    public Node enterReturnNode(final ReturnNode returnNode) {
         final TryNode tryNode = returnNode.getTryChain();
         final Node    expr    = returnNode.getExpression();
 
@@ -409,19 +443,19 @@
     }
 
     @Override
-    public Node leave(final ReturnNode returnNode) {
+    public Node leaveReturnNode(final ReturnNode returnNode) {
         addStatement(returnNode); //ReturnNodes are always terminal, marked as such in constructor
         return returnNode;
     }
 
     @Override
-    public Node enter(final SwitchNode switchNode) {
+    public Node enterSwitchNode(final SwitchNode switchNode) {
         nest(switchNode);
         return switchNode;
     }
 
     @Override
-    public Node leave(final SwitchNode switchNode) {
+    public Node leaveSwitchNode(final SwitchNode switchNode) {
         unnest(switchNode);
 
         final List<CaseNode> cases       = switchNode.getCases();
@@ -442,13 +476,13 @@
     }
 
     @Override
-    public Node leave(final ThrowNode throwNode) {
+    public Node leaveThrowNode(final ThrowNode throwNode) {
         addStatement(throwNode); //ThrowNodes are always terminal, marked as such in constructor
         return throwNode;
     }
 
     @Override
-    public Node enter(final TryNode tryNode) {
+    public Node enterTryNode(final TryNode tryNode) {
         final Block  finallyBody = tryNode.getFinallyBody();
         final long   token       = tryNode.getToken();
         final int    finish      = tryNode.getFinish();
@@ -534,26 +568,19 @@
 
             // set outer tryNode's body to innerTryNode
             final Block outerBody;
-            outerBody = new Block(source, token, finish, tryNode.getBody().getParent(), getCurrentFunctionNode());
+            outerBody = new Block(source, token, finish);
             outerBody.setStatements(new ArrayList<Node>(Arrays.asList(innerTryNode)));
             tryNode.setBody(outerBody);
             tryNode.setCatchBlocks(null);
-
-            // now before we go on, we have to fix the block parents
-            // (we repair the block tree after the insertion so that all references are intact)
-            innerTryNode.getBody().setParent(tryNode.getBody());
-            for (final Block block : innerTryNode.getCatchBlocks()) {
-                block.setParent(tryNode.getBody());
-            }
         }
 
         // create a catch-all that inlines finally and rethrows
 
-        final Block catchBlock      = new Block(source, token, finish, getCurrentBlock(), getCurrentFunctionNode());
+        final Block catchBlock      = new Block(source, token, finish);
         //this catch block should get define symbol
 
-        final Block catchBody       = new Block(source, token, finish, catchBlock, getCurrentFunctionNode());
-        final Node  catchAllFinally = finallyBody.clone();
+        final Block catchBody       = new Block(source, token, finish);
+        final Node  catchAllFinally = finallyBody.copy();
 
         catchBody.addStatement(new ExecuteNode(source, finallyBody.getToken(), finallyBody.getFinish(), catchAllFinally));
         setTerminal(catchBody, true);
@@ -580,7 +607,7 @@
     }
 
     @Override
-    public Node leave(final TryNode tryNode) {
+    public Node leaveTryNode(final TryNode tryNode) {
         final Block finallyBody   = tryNode.getFinallyBody();
 
         boolean allTerminal = tryNode.getBody().isTerminal() && (finallyBody == null || finallyBody.isTerminal());
@@ -604,18 +631,18 @@
     }
 
     @Override
-    public Node leave(final VarNode varNode) {
+    public Node leaveVarNode(final VarNode varNode) {
         addStatement(varNode);
         return varNode;
     }
 
     @Override
-    public Node enter(final WhileNode whileNode) {
+    public Node enterWhileNode(final WhileNode whileNode) {
         return nest(whileNode);
     }
 
     @Override
-    public Node leave(final WhileNode whileNode) {
+    public Node leaveWhileNode(final WhileNode whileNode) {
         final Node test = whileNode.getTest();
 
         if (test == null) {
@@ -636,7 +663,7 @@
             } else if (conservativeAlwaysTrue(test)) {
                 node = new ForNode(whileNode.getSource(), whileNode.getToken(), whileNode.getFinish());
                 ((ForNode)node).setBody(body);
-                ((ForNode)node).accept(this);
+                node.accept(this);
                 setTerminal(node, !escapes);
             }
         }
@@ -649,7 +676,7 @@
     }
 
     @Override
-    public Node leave(final WithNode withNode) {
+    public Node leaveWithNode(final WithNode withNode) {
         if (withNode.getBody().isTerminal()) {
             setTerminal(withNode,  true);
         }
@@ -678,28 +705,10 @@
      */
     private static Node markerFunction(final Node function) {
         if (function instanceof IdentNode) {
-            return new IdentNode((IdentNode)function) {
-                @Override
-                public boolean isFunction() {
-                    return true;
-                }
-            };
-        } else if (function instanceof AccessNode) {
-            return new AccessNode((AccessNode)function) {
-                @Override
-                public boolean isFunction() {
-                    return true;
-                }
-            };
-        } else if (function instanceof IndexNode) {
-            return new IndexNode((IndexNode)function) {
-                @Override
-                public boolean isFunction() {
-                    return true;
-                }
-            };
+            return ((IdentNode)function).setIsFunction();
+        } else if (function instanceof BaseNode) {
+            return ((BaseNode)function).setIsFunction();
         }
-
         return function;
     }
 
@@ -742,7 +751,7 @@
             if (args.size() >= 1 && EVAL.tag().equals(callee.getName())) {
                 final CallNode.EvalArgs evalArgs =
                     new CallNode.EvalArgs(
-                        args.get(0).clone().accept(this), //clone as we use this for the "is eval case". original evaluated separately for "is not eval case"
+                        args.get(0).copy().accept(this), //clone as we use this for the "is eval case". original evaluated separately for "is not eval case"
                         getCurrentFunctionNode().getThisNode(),
                         evalLocation(callee),
                         getCurrentFunctionNode().isStrictMode());
@@ -769,13 +778,13 @@
 
         loopBody.accept(new NodeVisitor() {
             @Override
-            public Node leave(final BreakNode node) {
+            public Node leaveBreakNode(final BreakNode node) {
                 escapes.add(node);
                 return node;
             }
 
             @Override
-            public Node leave(final ContinueNode node) {
+            public Node leaveContinueNode(final ContinueNode node) {
                 // all inner loops have been popped.
                 if (nesting.contains(node.getTargetNode())) {
                     escapes.add(node);
@@ -790,7 +799,7 @@
     private void guaranteeReturn(final FunctionNode functionNode) {
         Node resultNode;
 
-        if (functionNode.isScript()) {
+        if (functionNode.isProgram()) {
             resultNode = functionNode.getResultNode(); // the eval result, symbol assigned in Attr
         } else {
             if (lastStatement != null && lastStatement.isTerminal() || lastStatement instanceof ReturnNode) {
@@ -855,18 +864,15 @@
      * @return true if try block is inside the target, false otherwise.
      */
     private boolean isNestedTry(final TryNode tryNode, final Block target) {
-        for (Block current = getCurrentBlock(); current != target; current = current.getParent()) {
-            if (tryNode.getBody() == current) {
+        for(Iterator<Block> blocks = lexicalContext.getBlocks(getCurrentBlock()); blocks.hasNext();) {
+            final Block block = blocks.next();
+            if(block == target) {
+                return false;
+            }
+            if(tryNode.isChildBlock(block)) {
                 return true;
             }
-
-            for (final Block catchBlock : tryNode.getCatchBlocks()) {
-                if (catchBlock == current) {
-                    return true;
-                }
-            }
         }
-
         return false;
     }
 
@@ -895,7 +901,7 @@
                 continue;
             }
 
-            finallyBody = (Block)finallyBody.clone();
+            finallyBody = (Block)finallyBody.copy();
             final boolean hasTerminalFlags = finallyBody.hasTerminalFlags();
 
             new ExecuteNode(finallyBody.getSource(), finallyBody.getToken(), finallyBody.getFinish(), finallyBody).accept(this);
@@ -970,6 +976,3 @@
     }
 
 }
-
-
-
--- a/src/jdk/nashorn/internal/codegen/MethodEmitter.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/codegen/MethodEmitter.java	Mon Apr 01 21:42:31 2013 -0700
@@ -651,11 +651,10 @@
 
     /**
      * Load the constants array
-     * @param unitClassName name of the compile unit from which to load constants
      * @return this method emitter
      */
-    MethodEmitter loadConstants(final String unitClassName) {
-        getStatic(unitClassName, CONSTANTS.tag(), CONSTANTS.descriptor());
+    MethodEmitter loadConstants() {
+        getStatic(classEmitter.getUnitClassName(), CONSTANTS.tag(), CONSTANTS.descriptor());
         assert peekType().isArray() : peekType();
         return this;
     }
--- a/src/jdk/nashorn/internal/codegen/Splitter.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/codegen/Splitter.java	Mon Apr 01 21:42:31 2013 -0700
@@ -39,7 +39,9 @@
 import jdk.nashorn.internal.ir.DoWhileNode;
 import jdk.nashorn.internal.ir.ForNode;
 import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
 import jdk.nashorn.internal.ir.LabelNode;
+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;
@@ -48,6 +50,7 @@
 import jdk.nashorn.internal.ir.SplitNode;
 import jdk.nashorn.internal.ir.SwitchNode;
 import jdk.nashorn.internal.ir.WhileNode;
+import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
 import jdk.nashorn.internal.runtime.DebugLogger;
 import jdk.nashorn.internal.runtime.Source;
@@ -69,6 +72,8 @@
     /** Cache for calculated block weights. */
     private final Map<Node, Long> weightCache = new HashMap<>();
 
+    private final LexicalContext lexicalContext = new LexicalContext();
+
     /** Weight threshold for when to start a split. */
     public static final long SPLIT_THRESHOLD = Options.getIntProperty("nashorn.compiler.splitter.threshold", 32 * 1024);
 
@@ -92,15 +97,16 @@
      */
     void split() {
         if (functionNode.isLazy()) {
-            LOG.fine("Postponing split of '" + functionNode.getName() + "' as it's lazy");
+            LOG.finest("Postponing split of '" + functionNode.getName() + "' as it's lazy");
             return;
         }
-        LOG.fine("Initiating split of '" + functionNode.getName() + "'");
+
+        LOG.finest("Initiating split of '" + functionNode.getName() + "'");
 
         long weight = WeighNodes.weigh(functionNode);
 
         if (weight >= SPLIT_THRESHOLD) {
-            LOG.fine("Splitting '" + functionNode.getName() + "' as its weight " + weight + " exceeds split threshold " + SPLIT_THRESHOLD);
+            LOG.finest("Splitting '" + functionNode.getName() + "' as its weight " + weight + " exceeds split threshold " + SPLIT_THRESHOLD);
 
             functionNode.accept(this);
 
@@ -110,7 +116,7 @@
             }
 
             if (weight >= SPLIT_THRESHOLD) {
-                weight = splitBlock(functionNode);
+                weight = splitBlock(functionNode, functionNode);
             }
 
             if (functionNode.isSplit()) {
@@ -130,9 +136,22 @@
         }
 
         // Recursively split nested functions
-        for (final FunctionNode function : functionNode.getFunctions()) {
-            new Splitter(compiler, function, outermostCompileUnit).split();
-        }
+        functionNode.accept(new NodeOperatorVisitor() {
+            @Override
+            public Node enterFunctionNode(FunctionNode function) {
+                if(function == functionNode) {
+                    // Don't process outermost function (it was already processed) but descend into it to find nested
+                    // functions.
+                    return function;
+                }
+                // Process a nested function
+                new Splitter(compiler, function, outermostCompileUnit).split();
+                // Don't descend into a a nested function; Splitter.split() has taken care of nested-in-nested functions.
+                return null;
+            }
+        });
+
+        functionNode.setState(CompilationState.SPLIT);
     }
 
     /**
@@ -151,7 +170,7 @@
      *
      * @return new weight for the resulting block.
      */
-    private long splitBlock(final Block block) {
+    private long splitBlock(final Block block, final FunctionNode function) {
         functionNode.setIsSplit();
 
         final List<Node> splits = new ArrayList<>();
@@ -163,7 +182,7 @@
 
             if (statementsWeight + weight >= SPLIT_THRESHOLD || statement.isTerminal()) {
                 if (!statements.isEmpty()) {
-                    splits.add(createBlockSplitNode(block, statements, statementsWeight));
+                    splits.add(createBlockSplitNode(block, function, statements, statementsWeight));
                     statements = new ArrayList<>();
                     statementsWeight = 0;
                 }
@@ -179,7 +198,7 @@
         }
 
         if (!statements.isEmpty()) {
-            splits.add(createBlockSplitNode(block, statements, statementsWeight));
+            splits.add(createBlockSplitNode(block, function, statements, statementsWeight));
         }
 
         block.setStatements(splits);
@@ -195,13 +214,13 @@
      *
      * @return New split node.
      */
-    private SplitNode createBlockSplitNode(final Block parent, final List<Node> statements, final long weight) {
+    private SplitNode createBlockSplitNode(final Block parent, final FunctionNode function, final List<Node> statements, final long weight) {
         final Source source = parent.getSource();
         final long   token  = parent.getToken();
         final int    finish = parent.getFinish();
-        final String name   = parent.getFunction().uniqueName(SPLIT_PREFIX.tag());
+        final String name   = function.uniqueName(SPLIT_PREFIX.tag());
 
-        final Block newBlock = new Block(source, token, finish, parent, functionNode);
+        final Block newBlock = new Block(source, token, finish);
         newBlock.setFrame(new Frame(parent.getFrame()));
         newBlock.setStatements(statements);
 
@@ -213,15 +232,17 @@
     }
 
     @Override
-    public Node enter(final Block block) {
+    public Node enterBlock(final Block block) {
         if (block.isCatchBlock()) {
             return null;
         }
+        lexicalContext.push(block);
 
         final long weight = WeighNodes.weigh(block, weightCache);
 
         if (weight < SPLIT_THRESHOLD) {
             weightCache.put(block, weight);
+            lexicalContext.pop(block);
             return null;
         }
 
@@ -229,23 +250,24 @@
     }
 
     @Override
-    public Node leave(final Block block) {
+    public Node leaveBlock(final Block block) {
         assert !block.isCatchBlock();
 
         // Block was heavier than SLIT_THRESHOLD in enter, but a sub-block may have
         // been split already, so weigh again before splitting.
         long weight = WeighNodes.weigh(block, weightCache);
         if (weight >= SPLIT_THRESHOLD) {
-            weight = splitBlock(block);
+            weight = splitBlock(block, lexicalContext.getFunction(block));
         }
         weightCache.put(block, weight);
 
+        lexicalContext.pop(block);
         return block;
     }
 
     @SuppressWarnings("rawtypes")
     @Override
-    public Node leave(final LiteralNode literal) {
+    public Node leaveLiteralNode(final LiteralNode literal) {
         long weight = WeighNodes.weigh(literal);
 
         if (weight < SPLIT_THRESHOLD) {
@@ -290,17 +312,12 @@
     }
 
     @Override
-    public Node enter(final FunctionNode node) {
-        if (node.isLazy()) {
-            return null;
+    public Node enterFunctionNode(final FunctionNode node) {
+        if(node == functionNode && !node.isLazy()) {
+            lexicalContext.push(node);
+            node.visitStatements(this);
+            lexicalContext.pop(node);
         }
-
-        final List<Node> statements = node.getStatements();
-
-        for (final Node statement : statements) {
-            statement.accept(this);
-        }
-
         return null;
     }
 
@@ -317,38 +334,38 @@
         }
 
         @Override
-        public Node enter(final LabelNode labelNode) {
+        public Node enterLabelNode(final LabelNode labelNode) {
             registerJumpTarget(labelNode.getBreakNode());
             registerJumpTarget(labelNode.getContinueNode());
             return labelNode;
         }
 
         @Override
-        public Node enter(final WhileNode whileNode) {
+        public Node enterWhileNode(final WhileNode whileNode) {
             registerJumpTarget(whileNode);
             return whileNode;
         }
 
         @Override
-        public Node enter(final DoWhileNode doWhileNode) {
+        public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
             registerJumpTarget(doWhileNode);
             return doWhileNode;
         }
 
         @Override
-        public Node enter(final ForNode forNode) {
+        public Node enterForNode(final ForNode forNode) {
             registerJumpTarget(forNode);
             return forNode;
         }
 
         @Override
-        public Node enter(final SwitchNode switchNode) {
+        public Node enterSwitchNode(final SwitchNode switchNode) {
             registerJumpTarget(switchNode);
             return switchNode;
         }
 
         @Override
-        public Node enter(final ReturnNode returnNode) {
+        public Node enterReturnNode(final ReturnNode returnNode) {
             for (final SplitNode split : splitStack) {
                 split.setHasReturn(true);
             }
@@ -356,25 +373,25 @@
         }
 
         @Override
-        public Node enter(final ContinueNode continueNode) {
+        public Node enterContinueNode(final ContinueNode continueNode) {
             searchJumpTarget(continueNode.getTargetNode(), continueNode.getTargetLabel());
             return continueNode;
         }
 
         @Override
-        public Node enter(final BreakNode breakNode) {
+        public Node enterBreakNode(final BreakNode breakNode) {
             searchJumpTarget(breakNode.getTargetNode(), breakNode.getTargetLabel());
             return breakNode;
         }
 
         @Override
-        public Node enter(final SplitNode splitNode) {
+        public Node enterSplitNode(final SplitNode splitNode) {
             splitStack.addFirst(splitNode);
             return splitNode;
         }
 
         @Override
-        public Node leave(final SplitNode splitNode) {
+        public Node leaveSplitNode(final SplitNode splitNode) {
             assert splitNode == splitStack.peekFirst();
             splitStack.removeFirst();
             return splitNode;
--- a/src/jdk/nashorn/internal/codegen/WeighNodes.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/codegen/WeighNodes.java	Mon Apr 01 21:42:31 2013 -0700
@@ -47,7 +47,6 @@
 import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit;
 import jdk.nashorn.internal.ir.Node;
 import jdk.nashorn.internal.ir.PropertyNode;
-import jdk.nashorn.internal.ir.ReferenceNode;
 import jdk.nashorn.internal.ir.ReturnNode;
 import jdk.nashorn.internal.ir.RuntimeNode;
 import jdk.nashorn.internal.ir.SplitNode;
@@ -80,7 +79,7 @@
     private static final long LITERAL_WEIGHT   = 10;
     private static final long LOOP_WEIGHT      = 4;
     private static final long NEW_WEIGHT       = 6;
-    private static final long REFERENCE_WEIGHT = 20;
+    private static final long FUNC_EXPR_WEIGHT = 20;
     private static final long RETURN_WEIGHT    = 2;
     private static final long SPLIT_WEIGHT     = 40;
     private static final long SWITCH_WEIGHT    = 8;
@@ -94,36 +93,37 @@
     /** Optional cache for weight of block nodes. */
     private final Map<Node, Long> weightCache;
 
-    /*
+    private final FunctionNode topFunction;
+
+    /**
      * Constructor
      *
      * @param weightCache cache of already calculated block weights
      */
-    private WeighNodes(final Map<Node, Long> weightCache) {
+    private WeighNodes(FunctionNode topFunction, final Map<Node, Long> weightCache) {
         super(null, null);
+        this.topFunction = topFunction;
         this.weightCache = weightCache;
     }
 
     static long weigh(final Node node) {
-        final WeighNodes weighNodes = new WeighNodes(null);
-        node.accept(weighNodes);
-        return weighNodes.weight;
+        return weigh(node, null);
     }
 
     static long weigh(final Node node, final Map<Node, Long> weightCache) {
-        final WeighNodes weighNodes = new WeighNodes(weightCache);
+        final WeighNodes weighNodes = new WeighNodes(node instanceof FunctionNode ? (FunctionNode)node : null, weightCache);
         node.accept(weighNodes);
         return weighNodes.weight;
     }
 
     @Override
-    public Node leave(final AccessNode accessNode) {
+    public Node leaveAccessNode(final AccessNode accessNode) {
         weight += ACCESS_WEIGHT;
         return accessNode;
     }
 
     @Override
-    public Node enter(final Block block) {
+    public Node enterBlock(final Block block) {
         if (weightCache != null && weightCache.containsKey(block)) {
             weight += weightCache.get(block);
             return null;
@@ -133,78 +133,79 @@
     }
 
     @Override
-    public Node leave(final BreakNode breakNode) {
+    public Node leaveBreakNode(final BreakNode breakNode) {
         weight += BREAK_WEIGHT;
         return breakNode;
     }
 
     @Override
-    public Node leave(final CallNode callNode) {
+    public Node leaveCallNode(final CallNode callNode) {
         weight += CALL_WEIGHT;
         return callNode;
     }
 
     @Override
-    public Node leave(final CatchNode catchNode) {
+    public Node leaveCatchNode(final CatchNode catchNode) {
         weight += CATCH_WEIGHT;
         return catchNode;
     }
 
     @Override
-    public Node leave(final ContinueNode continueNode) {
+    public Node leaveContinueNode(final ContinueNode continueNode) {
         weight += CONTINUE_WEIGHT;
         return continueNode;
     }
 
     @Override
-    public Node leave(final DoWhileNode doWhileNode) {
+    public Node leaveDoWhileNode(final DoWhileNode doWhileNode) {
         weight += LOOP_WEIGHT;
         return doWhileNode;
     }
 
     @Override
-    public Node leave(final ExecuteNode executeNode) {
+    public Node leaveExecuteNode(final ExecuteNode executeNode) {
         return executeNode;
     }
 
     @Override
-    public Node leave(final ForNode forNode) {
+    public Node leaveForNode(final ForNode forNode) {
         weight += LOOP_WEIGHT;
         return forNode;
     }
 
     @Override
-    public Node enter(final FunctionNode functionNode) {
-        final List<Node> statements = functionNode.getStatements();
-
-        for (final Node statement : statements) {
-            statement.accept(this);
+    public Node enterFunctionNode(final FunctionNode functionNode) {
+        if(functionNode == topFunction) {
+            // the function being weighted; descend into its statements
+            functionNode.visitStatements(this);
+        } else {
+            // just a reference to inner function from outer function
+            weight += FUNC_EXPR_WEIGHT;
         }
-
         return null;
     }
 
     @Override
-    public Node leave(final IdentNode identNode) {
+    public Node leaveIdentNode(final IdentNode identNode) {
         weight += ACCESS_WEIGHT + identNode.getName().length() * 2;
         return identNode;
     }
 
     @Override
-    public Node leave(final IfNode ifNode) {
+    public Node leaveIfNode(final IfNode ifNode) {
         weight += IF_WEIGHT;
         return ifNode;
     }
 
     @Override
-    public Node leave(final IndexNode indexNode) {
+    public Node leaveIndexNode(final IndexNode indexNode) {
         weight += ACCESS_WEIGHT;
         return indexNode;
     }
 
     @SuppressWarnings("rawtypes")
     @Override
-    public Node enter(final LiteralNode literalNode) {
+    public Node enterLiteralNode(final LiteralNode literalNode) {
         weight += LITERAL_WEIGHT;
 
         if (literalNode instanceof ArrayLiteralNode) {
@@ -230,67 +231,61 @@
     }
 
     @Override
-    public Node leave(final PropertyNode propertyNode) {
+    public Node leavePropertyNode(final PropertyNode propertyNode) {
         weight += LITERAL_WEIGHT;
         return propertyNode;
     }
 
     @Override
-    public Node leave(final ReferenceNode referenceNode) {
-        weight += REFERENCE_WEIGHT;
-        return referenceNode;
-    }
-
-    @Override
-    public Node leave(final ReturnNode returnNode) {
+    public Node leaveReturnNode(final ReturnNode returnNode) {
         weight += RETURN_WEIGHT;
         return returnNode;
     }
 
     @Override
-    public Node leave(final RuntimeNode runtimeNode) {
+    public Node leaveRuntimeNode(final RuntimeNode runtimeNode) {
         weight += CALL_WEIGHT;
         return runtimeNode;
     }
 
     @Override
-    public Node enter(final SplitNode splitNode) {
+    public Node enterSplitNode(final SplitNode splitNode) {
         weight += SPLIT_WEIGHT;
         return null;
     }
 
     @Override
-    public Node leave(final SwitchNode switchNode) {
+    public Node leaveSwitchNode(final SwitchNode switchNode) {
         weight += SWITCH_WEIGHT;
         return switchNode;
     }
 
     @Override
-    public Node leave(final ThrowNode throwNode) {
+    public Node leaveThrowNode(final ThrowNode throwNode) {
         weight += THROW_WEIGHT;
         return throwNode;
     }
 
     @Override
-    public Node leave(final TryNode tryNode) {
+    public Node leaveTryNode(final TryNode tryNode) {
         weight += THROW_WEIGHT;
         return tryNode;
     }
 
     @Override
-    public Node leave(final VarNode varNode) {
+    public Node leaveVarNode(final VarNode varNode) {
         weight += VAR_WEIGHT;
         return varNode;
     }
 
     @Override
-    public Node leave(final WhileNode whileNode) {
+    public Node leaveWhileNode(final WhileNode whileNode) {
         weight += LOOP_WEIGHT;
         return whileNode;
     }
 
     @Override
-    public Node leave(final WithNode withNode) {
+    public Node leaveWithNode(final WithNode withNode) {
         weight += WITH_WEIGHT;
         return withNode;
     }
--- a/src/jdk/nashorn/internal/codegen/types/Type.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/codegen/types/Type.java	Mon Apr 01 21:42:31 2013 -0700
@@ -647,21 +647,20 @@
     }
 
     private static void swap(final MethodVisitor method, final Type above, final Type below) {
-        final MethodVisitor mv = method;
         if (below.isCategory2()) {
             if (above.isCategory2()) {
-                mv.visitInsn(DUP2_X2);
-                mv.visitInsn(POP2);
+                method.visitInsn(DUP2_X2);
+                method.visitInsn(POP2);
             } else {
-                mv.visitInsn(DUP_X2);
-                mv.visitInsn(POP);
+                method.visitInsn(DUP_X2);
+                method.visitInsn(POP);
             }
         } else {
             if (above.isCategory2()) {
-                mv.visitInsn(DUP2_X1);
-                mv.visitInsn(POP2);
+                method.visitInsn(DUP2_X1);
+                method.visitInsn(POP2);
             } else {
-                mv.visitInsn(SWAP);
+                method.visitInsn(SWAP);
             }
         }
 
--- a/src/jdk/nashorn/internal/ir/AccessNode.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/AccessNode.java	Mon Apr 01 21:42:31 2013 -0700
@@ -36,7 +36,7 @@
  * IR representation of a property access (period operator.)
  *
  */
-public class AccessNode extends BaseNode implements TypeOverride {
+public class AccessNode extends BaseNode implements TypeOverride<AccessNode> {
     /** Property ident. */
     private IdentNode property;
 
@@ -56,9 +56,7 @@
         super(source, token, finish, base);
 
         this.start    = base.getStart();
-        this.property = property;
-
-        this.property.setIsPropertyName();
+        this.property = property.setIsPropertyName();
     }
 
     /**
@@ -106,10 +104,10 @@
      */
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
+        if (visitor.enterAccessNode(this) != null) {
             base = base.accept(visitor);
             property = (IdentNode)property.accept(visitor);
-            return visitor.leave(this);
+            return visitor.leaveAccessNode(this);
         }
 
         return this;
@@ -150,13 +148,14 @@
     }
 
     @Override
-    public void setType(final Type type) {
+    public AccessNode setType(final Type type) {
         if (DEBUG_FIELDS && !Type.areEquivalent(getSymbol().getSymbolType(), type)) {
             ObjectClassGenerator.LOG.info(getClass().getName() + " " + this + " => " + type + " instead of " + getType());
         }
-        property.setType(type);
+        property = property.setType(type);
         getSymbol().setTypeOverride(type); //always a temp so this is fine.
         hasCallSiteType = true;
+        return this;
     }
 
     @Override
--- a/src/jdk/nashorn/internal/ir/Assignment.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/Assignment.java	Mon Apr 01 21:42:31 2013 -0700
@@ -46,4 +46,11 @@
      * @return get the assignment source node
      */
     public Node getAssignmentSource();
+
+    /**
+     * Set assignment destination node.
+     * @param n the assignment destination node.
+     * @return a node equivalent to this one except for the requested change.
+     */
+    public Node setAssignmentDest(D n);
 }
--- a/src/jdk/nashorn/internal/ir/BaseNode.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/BaseNode.java	Mon Apr 01 21:42:31 2013 -0700
@@ -38,6 +38,8 @@
     /** Base Node. */
     protected Node base;
 
+    private boolean function;
+
     /**
      * Constructor
      *
@@ -96,6 +98,15 @@
 
     @Override
     public boolean isFunction() {
-        return false;
+        return function;
+    }
+
+    /**
+     * Mark this node as being the callee operand of a {@link CallNode}.
+     * @return a base node identical to this one in all aspects except with its function flag set.
+     */
+    public BaseNode setIsFunction() {
+        function = true;
+        return this;
     }
 }
--- a/src/jdk/nashorn/internal/ir/BinaryNode.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/BinaryNode.java	Mon Apr 01 21:42:31 2013 -0700
@@ -35,7 +35,7 @@
  */
 public class BinaryNode extends UnaryNode {
     /** Left hand side argument. */
-    protected Node lhs;
+    private Node lhs;
 
     /**
      * Constructor
@@ -140,6 +140,11 @@
     }
 
     @Override
+    public Node setAssignmentDest(Node n) {
+        return setLHS(n);
+    }
+
+    @Override
     public Node getAssignmentSource() {
         return rhs();
     }
@@ -163,10 +168,9 @@
      */
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
-            lhs = lhs.accept(visitor);
-            rhs = rhs.accept(visitor);
-            return visitor.leave(this);
+        if (visitor.enterBinaryNode(this) != null) {
+            // TODO: good cause for a separate visitMembers: we could delegate to UnaryNode.visitMembers
+            return visitor.leaveBinaryNode((BinaryNode)setLHS(lhs.accept(visitor)).setRHS(rhs().accept(visitor)));
         }
 
         return this;
@@ -229,8 +233,12 @@
     /**
      * Set the left hand side expression for this node
      * @param lhs new left hand side expression
+     * @return a node equivalent to this one except for the requested change.
      */
-    public void setLHS(final Node lhs) {
-        this.lhs = lhs;
+    public BinaryNode setLHS(final Node lhs) {
+        if(this.lhs == lhs) return this;
+        final BinaryNode n = (BinaryNode)clone();
+        n.lhs = lhs;
+        return n;
     }
 }
--- a/src/jdk/nashorn/internal/ir/Block.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/Block.java	Mon Apr 01 21:42:31 2013 -0700
@@ -25,41 +25,24 @@
 
 package jdk.nashorn.internal.ir;
 
-import static jdk.nashorn.internal.ir.Symbol.IS_GLOBAL;
-import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL;
-import static jdk.nashorn.internal.ir.Symbol.IS_LET;
-import static jdk.nashorn.internal.ir.Symbol.IS_PARAM;
-import static jdk.nashorn.internal.ir.Symbol.IS_SCOPE;
-import static jdk.nashorn.internal.ir.Symbol.IS_VAR;
-import static jdk.nashorn.internal.ir.Symbol.KINDMASK;
-
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
+import java.util.ListIterator;
 import jdk.nashorn.internal.codegen.Frame;
 import jdk.nashorn.internal.codegen.Label;
-import jdk.nashorn.internal.ir.annotations.Ignore;
-import jdk.nashorn.internal.ir.annotations.ParentNode;
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
 import jdk.nashorn.internal.runtime.Source;
 
 /**
  * IR representation for a list of statements and functions. All provides the
  * basis for script body.
- *
  */
 public class Block extends Node {
-    /** Parent context */
-    @ParentNode @Ignore
-    private Block parent;
-
-    /** Owning function. */
-    @Ignore //don't print it, it is apparent in the tree
-    protected FunctionNode function;
-
     /** List of statements */
     protected List<Node> statements;
 
@@ -84,14 +67,10 @@
      * @param source   source code
      * @param token    token
      * @param finish   finish
-     * @param parent   reference to parent block
-     * @param function function node this block is in
      */
-    public Block(final Source source, final long token, final int finish, final Block parent, final FunctionNode function) {
+    public Block(final Source source, final long token, final int finish) {
         super(source, token, finish);
 
-        this.parent     = parent;
-        this.function   = function;
         this.statements = new ArrayList<>();
         this.symbols    = new HashMap<>();
         this.entryLabel = new Label("block_entry");
@@ -107,8 +86,6 @@
     protected Block(final Block block, final CopyState cs) {
         super(block);
 
-        this.parent     = block.parent;
-        this.function   = block.function;
         this.statements = new ArrayList<>();
         for (final Node statement : block.getStatements()) {
             statements.add(cs.existingOrCopy(statement));
@@ -123,55 +100,7 @@
 
     @Override
     protected Node copy(final CopyState cs) {
-        return fixBlockChain(new Block(this, cs));
-    }
-
-    /**
-     * Whenever a clone that contains a hierarchy of blocks is created,
-     * this function has to be called to ensure that the parents point
-     * to the correct parent blocks or two different ASTs would not
-     * be completely separated.
-     *
-     * @return the argument
-     */
-    static Block fixBlockChain(final Block root) {
-        root.accept(new NodeVisitor() {
-            private Block        parent   = root.getParent();
-            private final FunctionNode function = root.getFunction();
-
-            @Override
-            public Node enter(final Block block) {
-                assert block.getFunction() == function;
-                block.setParent(parent);
-                parent = block;
-
-                return block;
-            }
-
-            @Override
-            public Node leave(final Block block) {
-                parent = block.getParent();
-
-                return block;
-            }
-
-            @Override
-            public Node enter(final FunctionNode functionNode) {
-                assert functionNode.getFunction() == function;
-
-                return enter((Block)functionNode);
-            }
-
-            @Override
-            public Node leave(final FunctionNode functionNode) {
-                assert functionNode.getFunction() == function;
-
-                return leave((Block)functionNode);
-            }
-
-        });
-
-        return root;
+        return new Block(this, cs);
     }
 
     /**
@@ -189,17 +118,12 @@
     }
 
     /**
-     * Prepend a statement to the statement list
+     * Prepend statements to the statement list
      *
-     * @param statement Statement node to add
+     * @param prepended statement to add
      */
-    public void prependStatement(final Node statement) {
-        if (statement != null) {
-            final List<Node> newStatements = new ArrayList<>();
-            newStatements.add(statement);
-            newStatements.addAll(statements);
-            setStatements(newStatements);
-        }
+    public void prependStatements(final List<Node> prepended) {
+        statements.addAll(0, prepended);
     }
 
     /**
@@ -212,39 +136,6 @@
     }
 
     /**
-     * Add a new function to the function list.
-     *
-     * @param functionNode Function node to add.
-     */
-    public void addFunction(final FunctionNode functionNode) {
-        assert parent != null : "Parent context missing.";
-
-        parent.addFunction(functionNode);
-    }
-
-    /**
-     * Add a list of functions to the function list.
-     *
-     * @param functionNodes Function nodes to add.
-     */
-    public void addFunctions(final List<FunctionNode> functionNodes) {
-        assert parent != null : "Parent context missing.";
-
-        parent.addFunctions(functionNodes);
-    }
-
-    /**
-     * Set the function list to a new one
-     *
-     * @param functionNodes the nodes to set
-     */
-    public void setFunctions(final List<FunctionNode> functionNodes) {
-        assert parent != null : "Parent context missing.";
-
-        parent.setFunctions(functionNodes);
-    }
-
-    /**
      * Assist in IR navigation.
      *
      * @param visitor IR navigating visitor.
@@ -258,13 +149,9 @@
         try {
             // Ignore parent to avoid recursion.
 
-            if (visitor.enter(this) != null) {
-                for (int i = 0, count = statements.size(); i < count; i++) {
-                    final Node statement = statements.get(i);
-                    statements.set(i, statement.accept(visitor));
-                }
-
-                return visitor.leave(this);
+            if (visitor.enterBlock(this) != null) {
+                visitStatements(visitor);
+                return visitor.leaveBlock(this);
             }
         } finally {
             visitor.setCurrentBlock(saveBlock);
@@ -274,51 +161,21 @@
     }
 
     /**
-     * Search for symbol.
-     *
-     * @param name Symbol name.
-     *
-     * @return Found symbol or null if not found.
+     * Get an iterator for all the symbols defined in this block
+     * @return symbol iterator
      */
-    public Symbol findSymbol(final String name) {
-        // Search up block chain to locate symbol.
-
-        for (Block block = this; block != null; block = block.getParent()) {
-            // Find name.
-            final Symbol symbol = block.symbols.get(name);
-            // If found then we are good.
-            if (symbol != null) {
-                return symbol;
-            }
-        }
-        return null;
+    public Iterator<Symbol> symbolIterator() {
+        return symbols.values().iterator();
     }
 
     /**
-     * Search for symbol in current function.
-     *
-     * @param name Symbol name.
-     *
-     * @return Found symbol or null if not found.
+     * Retrieves an existing symbol defined in the current block.
+     * @param name the name of the symbol
+     * @return an existing symbol with the specified name defined in the current block, or null if this block doesn't
+     * define a symbol with this name.
      */
-    public Symbol findLocalSymbol(final String name) {
-        // Search up block chain to locate symbol.
-        for (Block block = this; block != null; block = block.getParent()) {
-            // Find name.
-            final Symbol symbol = block.symbols.get(name);
-            // If found then we are good.
-            if (symbol != null) {
-                return symbol;
-            }
-
-            // If searched function then we are done.
-            if (block == block.function) {
-                break;
-            }
-        }
-
-        // Not found.
-        return null;
+    public Symbol getExistingSymbol(final String name) {
+        return symbols.get(name);
     }
 
     /**
@@ -331,122 +188,6 @@
         return statements.size() == 1 && statements.get(0) instanceof CatchNode;
     }
 
-    /**
-     * Test to see if a symbol is local to the function.
-     *
-     * @param symbol Symbol to test.
-     * @return True if a local symbol.
-     */
-    public boolean isLocal(final Symbol symbol) {
-        // some temp symbols have no block, so can be assumed local
-        final Block block = symbol.getBlock();
-        return block == null || block.getFunction() == function;
-    }
-
-    /**
-     * Declare the definition of a new symbol.
-     *
-     * @param name         Name of symbol.
-     * @param symbolFlags  Symbol flags.
-     * @param node         Defining Node.
-     *
-     * @return Symbol for given name or null for redefinition.
-     */
-    public Symbol defineSymbol(final String name, final int symbolFlags, final Node node) {
-        int    flags  = symbolFlags;
-        Symbol symbol = findSymbol(name); // Locate symbol.
-
-        if ((flags & KINDMASK) == IS_GLOBAL) {
-            flags |= IS_SCOPE;
-        }
-
-        if (symbol != null) {
-            // Symbol was already defined. Check if it needs to be redefined.
-            if ((flags & KINDMASK) == IS_PARAM) {
-                if (!function.isLocal(symbol)) {
-                    // Not defined in this function. Create a new definition.
-                    symbol = null;
-                } else if (symbol.isParam()) {
-                    // Duplicate parameter. Null return will force an error.
-                    assert false : "duplicate parameter";
-                    return null;
-                }
-            } else if ((flags & KINDMASK) == IS_VAR) {
-                if ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & Symbol.IS_LET) == Symbol.IS_LET) {
-                    assert !((flags & IS_LET) == IS_LET && symbol.getBlock() == this) : "duplicate let variable in block";
-                    // Always create a new definition.
-                    symbol = null;
-                } else {
-                    // Not defined in this function. Create a new definition.
-                    if (!function.isLocal(symbol) || symbol.less(IS_VAR)) {
-                        symbol = null;
-                    }
-                }
-            }
-        }
-
-        if (symbol == null) {
-            // If not found, then create a new one.
-            Block symbolBlock;
-
-            // Determine where to create it.
-            if ((flags & Symbol.KINDMASK) == IS_VAR && ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET)) {
-                symbolBlock = this;
-            } else {
-                symbolBlock = getFunction();
-            }
-
-            // Create and add to appropriate block.
-            symbol = new Symbol(name, flags, node, symbolBlock);
-            symbolBlock.putSymbol(name, symbol);
-
-            if ((flags & Symbol.KINDMASK) != IS_GLOBAL) {
-                symbolBlock.getFrame().addSymbol(symbol);
-                symbol.setNeedsSlot(true);
-            }
-        } else if (symbol.less(flags)) {
-            symbol.setFlags(flags);
-        }
-
-        if (node != null) {
-            node.setSymbol(symbol);
-        }
-
-        return symbol;
-    }
-
-    /**
-     * Declare the use of a symbol.
-     *
-     * @param name Name of symbol.
-     * @param node Using node
-     *
-     * @return Symbol for given name.
-     */
-    public Symbol useSymbol(final String name, final Node node) {
-        Symbol symbol = findSymbol(name);
-
-        if (symbol == null) {
-            // If not found, declare as a free var.
-            symbol = defineSymbol(name, IS_GLOBAL, node);
-        } else {
-            node.setSymbol(symbol);
-        }
-
-        return symbol;
-    }
-
-    /**
-     * Add parent name to the builder.
-     *
-     * @param sb String bulder.
-     */
-    public void addParentName(final StringBuilder sb) {
-        if (parent != null) {
-            parent.addParentName(sb);
-        }
-    }
-
     @Override
     public void toString(final StringBuilder sb) {
         for (final Node statement : statements) {
@@ -505,16 +246,6 @@
     }
 
     /**
-     * Get the FunctionNode for this block, i.e. the function it
-     * belongs to
-     *
-     * @return the function node
-     */
-    public FunctionNode getFunction() {
-        return function;
-    }
-
-    /**
      * Reset the frame for this block
      *
      * @param frame  the new frame
@@ -524,24 +255,6 @@
     }
 
     /**
-     * Get the parent block
-     *
-     * @return parent block, or null if none exists
-     */
-    public Block getParent() {
-        return parent;
-    }
-
-    /**
-     * Set the parent block
-     *
-     * @param parent the new parent block
-     */
-    public void setParent(final Block parent) {
-        this.parent = parent;
-    }
-
-    /**
      * Get the list of statements in this block
      *
      * @return a list of statements
@@ -551,6 +264,15 @@
     }
 
     /**
+     * Applies the specified visitor to all statements in the block.
+     * @param visitor the visitor.
+     */
+    public void visitStatements(NodeVisitor visitor) {
+        for (ListIterator<Node> stmts = statements.listIterator(); stmts.hasNext();) {
+            stmts.set(stmts.next().accept(visitor));
+        }
+    }
+    /**
      * Reset the statement list for this block
      *
      * @param statements  new statement list
@@ -585,4 +307,29 @@
         needsScope = true;
     }
 
+    /**
+     * Marks this block as using a specified scoped symbol. The block and its parent blocks up to but not
+     * including the block defining the symbol will be marked as needing parent scope. The block defining the symbol
+     * will be marked as one that needs to have its own scope.
+     * @param symbol the symbol being used.
+     * @param ancestors the iterator over block's containing lexical context
+     */
+    public void setUsesScopeSymbol(final Symbol symbol, Iterator<Block> ancestors) {
+        if(symbol.getBlock() == this) {
+            setNeedsScope();
+        } else {
+            setUsesParentScopeSymbol(symbol, ancestors);
+        }
+    }
+
+    /**
+     * Invoked when this block uses a scope symbol defined in one of its ancestors.
+     * @param symbol the scope symbol being used
+     * @param ancestors iterator over ancestor blocks
+     */
+    void setUsesParentScopeSymbol(final Symbol symbol, Iterator<Block> ancestors) {
+        if(ancestors.hasNext()) {
+            ancestors.next().setUsesScopeSymbol(symbol, ancestors);
+        }
+    }
 }
--- a/src/jdk/nashorn/internal/ir/BreakNode.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/BreakNode.java	Mon Apr 01 21:42:31 2013 -0700
@@ -64,8 +64,8 @@
      */
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
-            return visitor.leave(this);
+        if (visitor.enterBreakNode(this) != null) {
+            return visitor.leaveBreakNode(this);
         }
 
         return this;
--- a/src/jdk/nashorn/internal/ir/CallNode.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/CallNode.java	Mon Apr 01 21:42:31 2013 -0700
@@ -37,7 +37,7 @@
  * IR representation for a function call.
  *
  */
-public class CallNode extends Node implements TypeOverride {
+public class CallNode extends Node implements TypeOverride<CallNode> {
 
     private Type type;
 
@@ -176,13 +176,13 @@
         if (hasCallSiteType()) {
             return type;
         }
-        assert !function.getType().isUnknown();
-        return function.getType();
+        return function instanceof FunctionNode ? ((FunctionNode)function).getReturnType() : Type.OBJECT;
     }
 
     @Override
-    public void setType(final Type type) {
+    public CallNode setType(final Type type) {
         this.type = type;
+        return this;
     }
 
     private boolean hasCallSiteType() {
@@ -208,14 +208,14 @@
      */
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
+        if (visitor.enterCallNode(this) != null) {
             function = function.accept(visitor);
 
             for (int i = 0, count = args.size(); i < count; i++) {
                 args.set(i, args.get(i).accept(visitor));
             }
 
-            return visitor.leave(this);
+            return visitor.leaveCallNode(this);
         }
 
         return this;
--- a/src/jdk/nashorn/internal/ir/CaseNode.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/CaseNode.java	Mon Apr 01 21:42:31 2013 -0700
@@ -79,7 +79,7 @@
      */
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
+        if (visitor.enterCaseNode(this) != null) {
             if (test != null) {
                 test = test.accept(visitor);
             }
@@ -87,7 +87,7 @@
                 body = (Block)body.accept(visitor);
             }
 
-            return visitor.leave(this);
+            return visitor.leaveCaseNode(this);
         }
 
         return this;
--- a/src/jdk/nashorn/internal/ir/CatchNode.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/CatchNode.java	Mon Apr 01 21:42:31 2013 -0700
@@ -84,7 +84,7 @@
      */
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
+        if (visitor.enterCatchNode(this) != null) {
             exception = (IdentNode)exception.accept(visitor);
 
             if (exceptionCondition != null) {
@@ -92,7 +92,7 @@
             }
 
             body = (Block)body.accept(visitor);
-            return visitor.leave(this);
+            return visitor.leaveCatchNode(this);
         }
 
         return this;
--- a/src/jdk/nashorn/internal/ir/ContinueNode.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/ContinueNode.java	Mon Apr 01 21:42:31 2013 -0700
@@ -61,8 +61,8 @@
 
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
-            return visitor.leave(this);
+        if (visitor.enterContinueNode(this) != null) {
+            return visitor.leaveContinueNode(this);
         }
 
         return this;
--- a/src/jdk/nashorn/internal/ir/DoWhileNode.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/DoWhileNode.java	Mon Apr 01 21:42:31 2013 -0700
@@ -63,11 +63,11 @@
 
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
+        if (visitor.enterDoWhileNode(this) != null) {
             body = (Block)body.accept(visitor);
             test = test.accept(visitor);
 
-            return visitor.leave(this);
+            return visitor.leaveDoWhileNode(this);
         }
 
         return this;
--- a/src/jdk/nashorn/internal/ir/EmptyNode.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/EmptyNode.java	Mon Apr 01 21:42:31 2013 -0700
@@ -57,8 +57,8 @@
 
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
-            return visitor.leave(this);
+        if (visitor.enterEmptyNode(this) != null) {
+            return visitor.leaveEmptyNode(this);
         }
         return this;
     }
--- a/src/jdk/nashorn/internal/ir/ExecuteNode.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/ExecuteNode.java	Mon Apr 01 21:42:31 2013 -0700
@@ -85,9 +85,9 @@
 
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
+        if (visitor.enterExecuteNode(this) != null) {
             setExpression(expression.accept(visitor));
-            return visitor.leave(this);
+            return visitor.leaveExecuteNode(this);
         }
 
         return this;
--- a/src/jdk/nashorn/internal/ir/ForNode.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/ForNode.java	Mon Apr 01 21:42:31 2013 -0700
@@ -76,7 +76,7 @@
 
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
+        if (visitor.enterForNode(this) != null) {
             if (init != null) {
                 init = init.accept(visitor);
             }
@@ -91,7 +91,7 @@
 
             body = (Block)body.accept(visitor);
 
-            return visitor.leave(this);
+            return visitor.leaveForNode(this);
         }
 
         return this;
--- a/src/jdk/nashorn/internal/ir/FunctionNode.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/FunctionNode.java	Mon Apr 01 21:42:31 2013 -0700
@@ -33,8 +33,10 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.EnumSet;
-import java.util.LinkedList;
+import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.Stack;
 import jdk.nashorn.internal.codegen.CompileUnit;
 import jdk.nashorn.internal.codegen.Compiler;
@@ -45,16 +47,18 @@
 import jdk.nashorn.internal.ir.annotations.Ignore;
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
 import jdk.nashorn.internal.parser.Parser;
+import jdk.nashorn.internal.runtime.ScriptFunction;
 import jdk.nashorn.internal.runtime.Source;
 import jdk.nashorn.internal.runtime.UserAccessorProperty;
 import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
 
 /**
  * IR representation for function (or script.)
- *
  */
 public class FunctionNode extends Block {
 
+    private static final Type FUNCTION_TYPE = Type.typeFor(ScriptFunction.class);
+
     /** Function kinds */
     public enum Kind {
         /** a normal function - nothing special */
@@ -86,7 +90,9 @@
         /** method has had its types finalized */
         FINALIZED,
         /** method has been emitted to bytecode */
-        EMITTED
+        EMITTED,
+        /** code installed in a class loader */
+        INSTALLED
     }
 
     /** External function identifier. */
@@ -108,9 +114,6 @@
     /** List of parameters. */
     private List<IdentNode> parameters;
 
-    /** List of nested functions. */
-    private List<FunctionNode> functions;
-
     /** First token of function. **/
     private long firstToken;
 
@@ -153,10 +156,6 @@
     /** Pending control list. */
     private final Stack<Node> controlStack;
 
-    /** Variable declarations in the function's scope */
-    @Ignore
-    private final List<VarNode> declarations;
-
     /** VarNode for this function statement */
     @Ignore //this is explicit code anyway and should not be traversed after lower
     private VarNode funcVarNode;
@@ -173,37 +172,42 @@
     @Ignore
     private final EnumSet<CompilationState> compilationState;
 
+    /** Type hints, e.g based on parameters at call site */
+    private final Map<IdentNode, Type> specializedTypes;
+
     /** Function flags. */
     private int flags;
 
     /** Is anonymous function flag. */
-    private static final int IS_ANONYMOUS                = 0b0000_0000_0000_0001;
-    /** Is statement flag */
-    private static final int IS_STATEMENT                = 0b0000_0000_0000_0010;
+    private static final int IS_ANONYMOUS                = 1 << 0;
+    /** Is the function created in a function declaration (as opposed to a function expression) */
+    private static final int IS_DECLARED                 = 1 << 1;
     /** is this a strict mode function? */
-    private static final int IS_STRICT_MODE              = 0b0000_0000_0000_0100;
+    private static final int IS_STRICT_MODE              = 1 << 2;
     /** Does the function use the "arguments" identifier ? */
-    private static final int USES_ARGUMENTS              = 0b0000_0000_0000_1000;
+    private static final int USES_ARGUMENTS              = 1 << 3;
     /** Are we lowered ? */
-    private static final int IS_LOWERED                  = 0b0000_0000_0001_0000;
+    private static final int IS_LOWERED                  = 1 << 4;
     /** Has this node been split because it was too large? */
-    private static final int IS_SPLIT                    = 0b0000_0000_0010_0000;
+    private static final int IS_SPLIT                    = 1 << 5;
     /** Does the function call eval? */
-    private static final int HAS_EVAL                    = 0b0000_0000_0100_0000;
+    private static final int HAS_EVAL                    = 1 << 6;
     /** Does the function contain a with block ? */
-    private static final int HAS_WITH                    = 0b0000_0000_1000_0000;
+    private static final int HAS_WITH                    = 1 << 7;
     /** Does a descendant function contain a with or eval? */
-    private static final int HAS_DESCENDANT_WITH_OR_EVAL = 0b0000_0001_0000_0000;
+    private static final int HAS_DESCENDANT_WITH_OR_EVAL = 1 << 8;
     /** Does the function define "arguments" identifier as a parameter of nested function name? */
-    private static final int DEFINES_ARGUMENTS           = 0b0000_0010_0000_0000;
+    private static final int DEFINES_ARGUMENTS           = 1 << 9;
     /** Does the function need a self symbol? */
-    private static final int NEEDS_SELF_SYMBOL           = 0b0000_0100_0000_0000;
+    private static final int NEEDS_SELF_SYMBOL           = 1 << 10;
     /** Does this function or any of its descendants use variables from an ancestor function's scope (incl. globals)? */
-    private static final int USES_ANCESTOR_SCOPE         = 0b0000_1000_0000_0000;
+    private static final int USES_ANCESTOR_SCOPE         = 1 << 11;
     /** Is this function lazily compiled? */
-    private static final int IS_LAZY                     = 0b0001_0000_0000_0000;
+    private static final int IS_LAZY                     = 1 << 12;
     /** Does this function have lazy, yet uncompiled children */
-    private static final int HAS_LAZY_CHILDREN           = 0b0010_0000_0000_0000;
+    private static final int HAS_LAZY_CHILDREN           = 1 << 13;
+    /** Does this function have lazy, yet uncompiled children */
+    private static final int IS_PROGRAM                   = 1 << 14;
 
     /** Does this function or any nested functions contain a with or an eval? */
     private static final int HAS_DEEP_WITH_OR_EVAL = HAS_EVAL | HAS_WITH | HAS_DESCENDANT_WITH_OR_EVAL;
@@ -211,54 +215,39 @@
     private static final int HAS_ALL_VARS_IN_SCOPE = HAS_DEEP_WITH_OR_EVAL | IS_SPLIT | HAS_LAZY_CHILDREN;
     /** Does this function potentially need "arguments"? Note that this is not a full test, as further negative check of REDEFINES_ARGS is needed. */
     private static final int MAYBE_NEEDS_ARGUMENTS = USES_ARGUMENTS | HAS_EVAL;
-    /** Does this function need the parent scope? It needs it if either it or its descendants use variables from it, or have a deep with or eval. */
-    private static final int NEEDS_PARENT_SCOPE = USES_ANCESTOR_SCOPE | HAS_DEEP_WITH_OR_EVAL;
+    /** Does this function need the parent scope? It needs it if either it or its descendants use variables from it, or have a deep with or eval.
+     *  We also pessimistically need a parent scope if we have lazy children that have not yet been compiled */
+    private static final int NEEDS_PARENT_SCOPE = USES_ANCESTOR_SCOPE | HAS_DEEP_WITH_OR_EVAL | HAS_LAZY_CHILDREN;
 
     /** What is the return type of this function? */
     private Type returnType = Type.UNKNOWN;
 
     /**
-     * Used to keep track of a function's parent blocks.
-     * This is needed when a (finally body) block is cloned than contains inner functions.
-     * Does not include function.getParent().
-     */
-    @Ignore
-    private List<Block> referencingParentBlocks;
-
-    /**
      * Constructor
      *
      * @param source    the source
      * @param token     token
      * @param finish    finish
      * @param namespace the namespace
-     * @param parent    the parent block
      * @param ident     the identifier
      * @param name      the name of the function
      */
-    @SuppressWarnings("LeakingThisInConstructor")
-    public FunctionNode(final Source source, final long token, final int finish, final Namespace namespace, final Block parent, final IdentNode ident, final String name) {
-        super(source, token, finish, parent, null);
+    public FunctionNode(final Source source, final long token, final int finish, final Namespace namespace, final IdentNode ident, final String name) {
+        super(source, token, finish);
 
         this.ident             = ident;
         this.name              = name;
         this.kind              = Kind.NORMAL;
         this.parameters        = new ArrayList<>();
-        this.functions         = new ArrayList<>();
         this.firstToken        = token;
         this.lastToken         = token;
         this.namespace         = namespace;
         this.labelStack        = new Stack<>();
         this.controlStack      = new Stack<>();
-        this.declarations      = new ArrayList<>();
-        // my block -> function is this. We added @SuppressWarnings("LeakingThisInConstructor") as NetBeans identifies
-        // it as such a leak - this is a false positive as we're setting this into a field of the object being
-        // constructed, so it can't be seen from other threads.
-        this.function          = this;
         this.compilationState  = EnumSet.of(CompilationState.INITIALIZED);
+        this.specializedTypes  = new HashMap<>();
     }
 
-    @SuppressWarnings("LeakingThisInConstructor")
     private FunctionNode(final FunctionNode functionNode, final CopyState cs) {
         super(functionNode, cs);
 
@@ -268,10 +257,9 @@
 
         this.parameters = new ArrayList<>();
         for (final IdentNode param : functionNode.getParameters()) {
-            this.parameters.add((IdentNode) cs.existingOrCopy(param));
+            this.parameters.add((IdentNode)cs.existingOrCopy(param));
         }
 
-        this.functions         = new ArrayList<>();
         this.firstToken        = functionNode.firstToken;
         this.lastToken         = functionNode.lastToken;
         this.namespace         = functionNode.getNamespace();
@@ -283,43 +271,34 @@
         this.calleeNode        = (IdentNode)cs.existingOrCopy(functionNode.calleeNode);
         this.labelStack        = new Stack<>();
         this.controlStack      = new Stack<>();
-        this.declarations      = new ArrayList<>();
-
-        for (final VarNode decl : functionNode.getDeclarations()) {
-            declarations.add((VarNode) cs.existingOrCopy(decl)); //TODO same?
-        }
 
         this.flags = functionNode.flags;
 
         this.funcVarNode = (VarNode)cs.existingOrCopy(functionNode.funcVarNode);
         /** VarNode for this function statement */
 
-        // my block -> function is this. We added @SuppressWarnings("LeakingThisInConstructor") as NetBeans identifies
-        // it as such a leak - this is a false positive as we're setting this into a field of the object being
-        // constructed, so it can't be seen from other threads.
-        this.function = this;
-
         this.compilationState = EnumSet.copyOf(functionNode.compilationState);
+        this.specializedTypes = new HashMap<>();
     }
 
     @Override
     protected Node copy(final CopyState cs) {
         // deep clone all parent blocks
-        return fixBlockChain(new FunctionNode(this, cs));
+        return new FunctionNode(this, cs);
     }
 
     @Override
     public Node accept(final NodeVisitor visitor) {
-        final FunctionNode saveFunctionNode = visitor.getCurrentFunctionNode();
-        final Block        saveBlock        = visitor.getCurrentBlock();
+        final FunctionNode  saveFunctionNode  = visitor.getCurrentFunctionNode();
+        final Block         saveBlock         = visitor.getCurrentBlock();
+        final MethodEmitter saveMethodEmitter = visitor.getCurrentMethodEmitter();
+        final CompileUnit   saveCompileUnit   = visitor.getCurrentCompileUnit();
 
         visitor.setCurrentFunctionNode(this);
-        visitor.setCurrentCompileUnit(getCompileUnit());
-        visitor.setCurrentMethodEmitter(getMethodEmitter());
         visitor.setCurrentBlock(this);
 
         try {
-            if (visitor.enter(this) != null) {
+            if (visitor.enterFunctionNode(this) != null) {
                 if (ident != null) {
                     ident = (IdentNode)ident.accept(visitor);
                 }
@@ -328,51 +307,25 @@
                     parameters.set(i, (IdentNode)parameters.get(i).accept(visitor));
                 }
 
-                for (int i = 0, count = functions.size(); i < count; i++) {
-                    functions.set(i, (FunctionNode)functions.get(i).accept(visitor));
-                }
-
                 for (int i = 0, count = statements.size(); i < count; i++) {
                     statements.set(i, statements.get(i).accept(visitor));
                 }
 
-                return visitor.leave(this);
+                return visitor.leaveFunctionNode(this);
             }
         } finally {
             visitor.setCurrentBlock(saveBlock);
             visitor.setCurrentFunctionNode(saveFunctionNode);
-            visitor.setCurrentCompileUnit(saveFunctionNode != null ? saveFunctionNode.getCompileUnit() : null);
-            visitor.setCurrentMethodEmitter(saveFunctionNode != null ? saveFunctionNode.getMethodEmitter() : null);
+            visitor.setCurrentCompileUnit(saveCompileUnit);
+            visitor.setCurrentMethodEmitter(saveMethodEmitter);
         }
 
         return this;
     }
 
-    /**
-     * Locate the parent function.
-     *
-     * @return Parent function.
-     */
-    public FunctionNode findParentFunction() {
-        return getParent() != null ? getParent().getFunction() : null;
-    }
-
-    /**
-     * Add parent name to the builder.
-     *
-     * @param sb String builder.
-     */
-    @Override
-    public void addParentName(final StringBuilder sb) {
-        if (!isScript()) {
-            sb.append(getName());
-            sb.append("$");
-        }
-    }
-
     @Override
     public boolean needsScope() {
-        return super.needsScope() || isScript();
+        return super.needsScope() || isProgram();
     }
 
     /**
@@ -530,12 +483,18 @@
     }
 
     /**
-     * Determine if script function.
-     *
-     * @return True if script function.
+     * Returns true if the function is the top-level program.
+     * @return True if this function node represents the top-level program.
      */
-    public boolean isScript() {
-        return getParent() == null;
+    public boolean isProgram() {
+        return (flags & IS_PROGRAM) != 0;
+    }
+
+    /**
+     * Marks the function as representing the top-level program.
+     */
+    public void setProgram() {
+        flags |= IS_PROGRAM;
     }
 
     /**
@@ -575,31 +534,31 @@
 
     /**
      * Flag this function as using the {@code with} keyword
+     * @param ancestors the iterator over functions in this functions's containing lexical context
      */
-    public void setHasWith() {
+    public void setHasWith(final Iterator<FunctionNode> ancestors) {
         if(!hasWith()) {
             this.flags |= HAS_WITH;
             // with requires scope in parents.
             // TODO: refine this. with should not force all variables in parents to be in scope, only those that are
             // actually referenced as identifiers by name
-            markParentForWithOrEval();
+            markParentForWithOrEval(ancestors);
         }
     }
 
-    private void markParentForWithOrEval() {
+    private void markParentForWithOrEval(final Iterator<FunctionNode> ancestors) {
         // If this is invoked, then either us or a descendant uses with or eval, meaning we must have our own scope.
         setNeedsScope();
 
-        final FunctionNode parentFunction = findParentFunction();
-        if(parentFunction != null) {
-            parentFunction.setDescendantHasWithOrEval();
+        if(ancestors.hasNext()) {
+            ancestors.next().setDescendantHasWithOrEval(ancestors);
         }
     }
 
-    private void setDescendantHasWithOrEval() {
+    private void setDescendantHasWithOrEval(final Iterator<FunctionNode> ancestors) {
         if((flags & HAS_DESCENDANT_WITH_OR_EVAL) == 0) {
             flags |= HAS_DESCENDANT_WITH_OR_EVAL;
-            markParentForWithOrEval();
+            markParentForWithOrEval(ancestors);
         }
     }
 
@@ -614,11 +573,12 @@
 
     /**
      * Flag this function as calling the {@code eval} function
+     * @param ancestors the iterator over functions in this functions's containing lexical context
      */
-    public void setHasEval() {
+    public void setHasEval(final Iterator<FunctionNode> ancestors) {
         if(!hasEval()) {
             this.flags |= HAS_EVAL;
-            markParentForWithOrEval();
+            markParentForWithOrEval(ancestors);
         }
     }
 
@@ -651,11 +611,34 @@
     }
 
     /**
-     * Get all nested functions
-     * @return list of nested functions in this function
+     * Returns a list of functions declared by this function. Only includes declared functions, and does not include any
+     * function expressions that might occur in its body.
+     * @return a list of functions declared by this function.
      */
-    public List<FunctionNode> getFunctions() {
-        return Collections.unmodifiableList(functions);
+    public List<FunctionNode> getDeclaredFunctions() {
+        // Note that the function does not have a dedicated list of declared functions, but rather relies on the
+        // invariant that all function declarations are at the beginning of the statement list as VarNode with a
+        // FunctionNode marked as statement with its variable initializer. Every VarNode is also preceded by a
+        // LineNumberNode. This invariant is established by the parser and has to be preserved in visitors.
+        final List<FunctionNode> fns = new ArrayList<>();
+        for (final Node stmt : statements) {
+            if(stmt instanceof LineNumberNode) {
+                continue;
+            } else if(stmt instanceof VarNode) {
+                final Node init = ((VarNode)stmt).getInit();
+                if(init instanceof FunctionNode) {
+                    final FunctionNode fn = (FunctionNode)init;
+                    if(fn.isDeclared()) {
+                        fns.add(fn);
+                        continue;
+                    }
+                }
+            }
+            // Node is neither a LineNumberNode, nor a function declaration VarNode. Since all function declarations are
+            // at the start of the function, we've reached the end of function declarations.
+            break;
+        }
+        return fns;
     }
 
     /**
@@ -710,6 +693,7 @@
      * Check if this function's generated Java method needs a {@code callee} parameter. Functions that need access to
      * their parent scope, functions that reference themselves, and non-strict functions that need an Arguments object
      * (since it exposes {@code arguments.callee} property) will need to have a callee parameter.
+     *
      * @return true if the function's generated Java method needs a {@code callee} parameter.
      */
     public boolean needsCallee() {
@@ -786,7 +770,7 @@
     public boolean needsArguments() {
         // uses "arguments" or calls eval, but it does not redefine "arguments", and finally, it's not a script, since
         // for top-level script, "arguments" is picked up from Context by Global.init() instead.
-        return (flags & MAYBE_NEEDS_ARGUMENTS) != 0 && (flags & DEFINES_ARGUMENTS) == 0 && !isScript();
+        return (flags & MAYBE_NEEDS_ARGUMENTS) != 0 && (flags & DEFINES_ARGUMENTS) == 0 && !isProgram();
     }
 
     /**
@@ -805,7 +789,7 @@
      * @return true if the function needs parent scope.
      */
     public boolean needsParentScope() {
-        return (flags & NEEDS_PARENT_SCOPE) != 0 || isScript();
+        return (flags & NEEDS_PARENT_SCOPE) != 0 || isProgram();
     }
 
     /**
@@ -865,7 +849,7 @@
      * @return true if all variables should be in scope
      */
     public boolean allVarsInScope() {
-        return isScript() || (flags & HAS_ALL_VARS_IN_SCOPE) != 0;
+        return isProgram() || (flags & HAS_ALL_VARS_IN_SCOPE) != 0;
     }
 
     /**
@@ -919,6 +903,27 @@
     }
 
     /**
+     * Get a specialized type for an identity, if one exists
+     * @param node node to check specialized type for
+     * @return null if no specialization exists, otherwise type
+     */
+    public Type getSpecializedType(final IdentNode node) {
+        return specializedTypes.get(node);
+    }
+
+    /**
+     * Set parameter type hints for specialization.
+     * @param types types array of length equal to parameter list size
+     */
+    public void setParameterTypes(final Class<?>[] types) {
+        assert types.length == parameters.size() : "Type vector length doesn't correspond to parameter types";
+        //diff - skip the callee and this etc, they are not explicit params in the parse tree
+        for (int i = 0; i < types.length ; i++) {
+            specializedTypes.put(parameters.get(i), Type.typeFor(types[i]));
+        }
+    }
+
+    /**
      * Get the identifier for the variable in which the function return value
      * should be stored
      * @return an IdentNode representing the return value
@@ -953,19 +958,19 @@
     }
 
     /**
-     * Check if this function is a statement
-     * @return true if function is a statement
+     * Check if this function is created as a function declaration (as opposed to function expression)
+     * @return true if function is declared.
      */
-    public boolean isStatement() {
-        return (flags & IS_STATEMENT) != 0;
+    public boolean isDeclared() {
+        return (flags & IS_DECLARED) != 0;
     }
 
     /**
-     * Flag this function as a statement
+     * Flag this function as being created as a function declaration (as opposed to a function expression).
      * @see Parser
      */
-    public void setIsStatement() {
-        this.flags |= IS_STATEMENT;
+    public void setIsDeclared() {
+        this.flags |= IS_DECLARED;
     }
 
     /**
@@ -1013,35 +1018,16 @@
     }
 
     /**
-     * Marks this function as one using any global symbol. The function and all its parent functions will all be marked
-     * as needing parent scope.
-     * @see #needsParentScope()
+     * Marks this function as using any of its ancestors' scopes.
      */
-    public void setUsesGlobalSymbol() {
+    public void setUsesAncestorScope() {
         this.flags |= USES_ANCESTOR_SCOPE;
-        final FunctionNode parentFn = findParentFunction();
-        if(parentFn != null) {
-            parentFn.setUsesGlobalSymbol();
-        }
     }
 
-    /**
-     * Marks this function as using a specified scoped symbol. The function and its parent functions up to but not
-     * including the function defining the symbol will be marked as needing parent scope. The function defining the
-     * symbol will be marked as one that needs to have its own scope.
-     * @param symbol the symbol being used.
-     * @see #needsParentScope()
-     */
-    public void setUsesScopeSymbol(final Symbol symbol) {
-        if(symbol.getBlock() == this) {
-            setNeedsScope();
-        } else {
-            this.flags |= USES_ANCESTOR_SCOPE;
-            final FunctionNode parentFn = findParentFunction();
-            if(parentFn != null) {
-                parentFn.setUsesScopeSymbol(symbol);
-            }
-        }
+    @Override
+    void setUsesParentScopeSymbol(Symbol symbol, Iterator<Block> ancestors) {
+        setUsesAncestorScope();
+        super.setUsesParentScopeSymbol(symbol, ancestors);
     }
 
     /**
@@ -1116,7 +1102,7 @@
 
     @Override
     public Type getType() {
-        return getReturnType();
+        return FUNCTION_TYPE;
     }
 
     /**
@@ -1176,56 +1162,6 @@
     }
 
     /**
-     * Add a new function to the function list.
-     *
-     * @param functionNode Function node to add.
-     */
-    @Override
-    public void addFunction(final FunctionNode functionNode) {
-        assert functionNode != null;
-        functions.add(functionNode);
-    }
-
-    /**
-     * Add a list of functions to the function list.
-     *
-     * @param functionNodes  Function nodes to add.
-     */
-    @Override
-    public void addFunctions(final List<FunctionNode> functionNodes) {
-        functions.addAll(functionNodes);
-    }
-
-    /**
-     * Set a function list
-     *
-     * @param functionNodes to set
-     */
-    @Override
-    public void setFunctions(final List<FunctionNode> functionNodes) {
-        this.functions = functionNodes;
-    }
-
-    /**
-     * Add a variable declaration that should be visible to the entire function
-     * scope. Parser does this.
-     *
-     * @param varNode a var node
-     */
-    public void addDeclaration(final VarNode varNode) {
-        declarations.add(varNode);
-    }
-
-    /**
-     * Return all variable declarations from this function scope
-     *
-     * @return all VarNodes in scope
-     */
-    public List<VarNode> getDeclarations() {
-        return Collections.unmodifiableList(declarations);
-    }
-
-    /**
      * Get the compile unit used to compile this function
      * @see Compiler
      * @return the compile unit
@@ -1258,32 +1194,4 @@
     public void setMethodEmitter(final MethodEmitter method) {
         this.method = method;
     }
-
-    /**
-     * Each FunctionNode maintains a list of reference to its parent blocks.
-     * Add a parent block to this function.
-     *
-     * @param parentBlock  a block to remember as parent
-     */
-    public void addReferencingParentBlock(final Block parentBlock) {
-        assert parentBlock.getFunction() == function.findParentFunction(); // all parent blocks must be in the same function
-        if (parentBlock != function.getParent()) {
-            if (referencingParentBlocks == null) {
-                referencingParentBlocks = new LinkedList<>();
-            }
-            referencingParentBlocks.add(parentBlock);
-        }
-    }
-
-    /**
-     * Get the known parent blocks to this function
-     *
-     * @return list of parent blocks
-     */
-    public List<Block> getReferencingParentBlocks() {
-        if (referencingParentBlocks == null) {
-            return Collections.emptyList();
-        }
-        return Collections.unmodifiableList(referencingParentBlocks);
-    }
 }
--- a/src/jdk/nashorn/internal/ir/IdentNode.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/IdentNode.java	Mon Apr 01 21:42:31 2013 -0700
@@ -38,18 +38,18 @@
 /**
  * IR representation for an identifier.
  */
-public class IdentNode extends Node implements PropertyKey, TypeOverride, FunctionCall {
+public class IdentNode extends Node implements PropertyKey, TypeOverride<IdentNode>, FunctionCall {
+    private static final int PROPERTY_NAME    = 1 << 0;
+    private static final int INITIALIZED_HERE = 1 << 1;
+    private static final int FUNCTION         = 1 << 2;
+
     /** Identifier. */
     private final String name;
 
     /** Type for a callsite, e.g. X in a get()X or a set(X)V */
     private Type callSiteType;
 
-    /** flag for an ident that is the property name of an AccessNode. */
-    private boolean isPropertyName;
-
-    /** flag for an ident on the left hand side of <code>var lhs = rhs;</code>. */
-    private boolean isInitializedHere;
+    private byte flags;
 
     /**
      * Constructor
@@ -71,9 +71,8 @@
      */
     public IdentNode(final IdentNode identNode) {
         super(identNode);
-        this.name              = identNode.getName();
-        this.isPropertyName    = identNode.isPropertyName;
-        this.isInitializedHere = identNode.isInitializedHere;
+        this.name  = identNode.getName();
+        this.flags = identNode.flags;
     }
 
     @Override
@@ -92,12 +91,17 @@
     }
 
     @Override
-    public void setType(final Type type) {
+    public IdentNode setType(final Type type) {
         if (DEBUG_FIELDS && getSymbol() != null && !Type.areEquivalent(getSymbol().getSymbolType(), type)) {
             ObjectClassGenerator.LOG.info(getClass().getName() + " " + this + " => " + type + " instead of " + getType());
         }
-        this.callSiteType = type;
         // do NOT, repeat NOT touch the symbol here. it might be a local variable or whatever. This is the override if it isn't
+        if(this.callSiteType == type) {
+            return this;
+        }
+        final IdentNode n = (IdentNode)clone();
+        n.callSiteType = type;
+        return n;
     }
 
     @Override
@@ -131,8 +135,8 @@
      */
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
-            return visitor.leave(this);
+        if (visitor.enterIdentNode(this) != null) {
+            return visitor.leaveIdentNode(this);
         }
 
         return this;
@@ -179,14 +183,18 @@
      * @return true if this is a property name
      */
     public boolean isPropertyName() {
-        return isPropertyName;
+        return (flags & PROPERTY_NAME) != 0;
     }
 
     /**
      * Flag this IdentNode as a property name
+     * @return a node equivalent to this one except for the requested change.
      */
-    public void setIsPropertyName() {
-        isPropertyName = true;
+    public IdentNode setIsPropertyName() {
+        if(isPropertyName()) return this;
+        final IdentNode n = (IdentNode)clone();
+        n.flags |= PROPERTY_NAME;
+        return n;
     }
 
     /**
@@ -194,14 +202,18 @@
      * @return true if IdentNode is initialized on creation
      */
     public boolean isInitializedHere() {
-        return isInitializedHere;
+        return (flags & INITIALIZED_HERE) != 0;
     }
 
     /**
      * Flag IdentNode to be initialized on creation
+     * @return a node equivalent to this one except for the requested change.
      */
-    public void setIsInitializedHere() {
-        isInitializedHere = true;
+    public IdentNode setIsInitializedHere() {
+        if(isInitializedHere()) return this;
+        final IdentNode n = (IdentNode)clone();
+        n.flags |= INITIALIZED_HERE;
+        return n;
     }
 
     /**
@@ -216,6 +228,17 @@
 
     @Override
     public boolean isFunction() {
-        return false;
+        return (flags & FUNCTION) != 0;
+    }
+
+    /**
+     * Mark this node as being the callee operand of a {@link CallNode}.
+     * @return an ident node identical to this one in all aspects except with its function flag set.
+     */
+    public IdentNode setIsFunction() {
+        if(isFunction()) return this;
+        final IdentNode n = (IdentNode)clone();
+        n.flags |= FUNCTION;
+        return n;
     }
 }
--- a/src/jdk/nashorn/internal/ir/IfNode.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/IfNode.java	Mon Apr 01 21:42:31 2013 -0700
@@ -75,7 +75,7 @@
 
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
+        if (visitor.enterIfNode(this) != null) {
             test = test.accept(visitor);
 
             pass = (Block)pass.accept(visitor);
@@ -84,7 +84,7 @@
                 fail = (Block)fail.accept(visitor);
             }
 
-            return visitor.leave(this);
+            return visitor.leaveIfNode(this);
         }
 
         return this;
--- a/src/jdk/nashorn/internal/ir/IndexNode.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/IndexNode.java	Mon Apr 01 21:42:31 2013 -0700
@@ -36,7 +36,7 @@
  * IR representation of an indexed access (brackets operator.)
  *
  */
-public class IndexNode extends BaseNode implements TypeOverride {
+public class IndexNode extends BaseNode implements TypeOverride<IndexNode> {
     /** Property ident. */
     private Node index;
 
@@ -92,10 +92,10 @@
 
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
+        if (visitor.enterIndexNode(this) != null) {
             base = base.accept(visitor);
             index = index.accept(visitor);
-            return visitor.leave(this);
+            return visitor.leaveIndexNode(this);
         }
 
         return this;
@@ -144,12 +144,13 @@
     }
 
     @Override
-    public void setType(final Type type) {
+    public IndexNode setType(final Type type) {
         if (DEBUG_FIELDS && !Type.areEquivalent(getSymbol().getSymbolType(), type)) {
             ObjectClassGenerator.LOG.info(getClass().getName() + " " + this + " => " + type + " instead of " + getType());
         }
         hasCallSiteType = true;
         getSymbol().setTypeOverride(type);
+        return this;
     }
 
     @Override
--- a/src/jdk/nashorn/internal/ir/LabelNode.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/LabelNode.java	Mon Apr 01 21:42:31 2013 -0700
@@ -81,10 +81,10 @@
 
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
+        if (visitor.enterLabelNode(this) != null) {
             label = (IdentNode)label.accept(visitor);
             body  = (Block)body.accept(visitor);
-            return visitor.leave(this);
+            return visitor.leaveLabelNode(this);
         }
 
         return this;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/ir/LexicalContext.java	Mon Apr 01 21:42:31 2013 -0700
@@ -0,0 +1,198 @@
+package jdk.nashorn.internal.ir;
+
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * A class that tracks the current lexical context of node visitation as a stack of {@link Block} nodes. Has special
+ * methods to retrieve useful subsets of the context.
+ */
+public class LexicalContext implements Cloneable {
+    private final Deque<Block> lexicalContext;
+
+    /**
+     * Creates a new empty lexical context.
+     */
+    public LexicalContext() {
+        lexicalContext = new ArrayDeque<>();
+    }
+
+    /**
+     * Pushes a new block on top of the context, making it the innermost open block.
+     * @param block the new block
+     */
+    public void push(Block block) {
+        //new Exception(block.toString()).printStackTrace();
+        lexicalContext.push(block);
+    }
+
+    /**
+     * Pops the innermost block off the context.
+     * @param the block expected to be popped, used to detect unbalanced pushes/pops
+     */
+    public void pop(Block block) {
+        final Block popped = lexicalContext.pop();
+        assert popped == block;
+    }
+
+    /**
+     * Returns an iterator over all blocks in the context, with the top block (innermost lexical context) first.
+     * @return an iterator over all blocks in the context.
+     */
+    public Iterator<Block> getBlocks() {
+        return lexicalContext.iterator();
+    }
+
+    /**
+     * Returns an iterator over all functions in the context, with the top (innermost open) function first.
+     * @return an iterator over all functions in the context.
+     */
+    public Iterator<FunctionNode> getFunctions() {
+        return new FunctionIterator(getBlocks());
+    }
+
+    private static final class FunctionIterator implements Iterator<FunctionNode> {
+        private final Iterator<Block> it;
+        private FunctionNode next;
+
+        FunctionIterator(Iterator<Block> it) {
+            this.it = it;
+            next = findNext();
+        }
+
+        @Override
+        public boolean hasNext() {
+            return next != null;
+        }
+
+        @Override
+        public FunctionNode next() {
+            if(next == null) {
+                throw new NoSuchElementException();
+            }
+            FunctionNode lnext = next;
+            next = findNext();
+            return lnext;
+        }
+
+        private FunctionNode findNext() {
+            while(it.hasNext()) {
+                final Block block = it.next();
+                if(block instanceof FunctionNode) {
+                    return ((FunctionNode)block);
+                }
+            }
+            return null;
+        }
+
+        @Override
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    /**
+     * Returns an iterator over all ancestors block of the given block, with its parent block first.
+     * @param block the block whose ancestors are returned
+     * @return an iterator over all ancestors block of the given block.
+     */
+    public Iterator<Block> getAncestorBlocks(Block block) {
+        final Iterator<Block> it = getBlocks();
+        while(it.hasNext()) {
+            final Block b = it.next();
+            if(block == b) {
+                return it;
+            }
+        }
+        throw new AssertionError("Block is not on the current lexical context stack");
+    }
+
+    /**
+     * Returns an iterator over a block and all its ancestors blocks, with the block first.
+     * @param block the block that is the starting point of the iteration.
+     * @return an iterator over a block and all its ancestors.
+     */
+    public Iterator<Block> getBlocks(final Block block) {
+        final Iterator<Block> it = getAncestorBlocks(block);
+        return new Iterator<Block>() {
+            boolean blockReturned = false;
+            @Override
+            public boolean hasNext() {
+                return it.hasNext() || !blockReturned;
+            }
+            @Override
+            public Block next() {
+                if(blockReturned) {
+                    return it.next();
+                }
+                blockReturned = true;
+                return block;
+            }
+            @Override
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+        };
+    }
+
+    /**
+     * Returns the closest function node to the block. If the block is itself a function, it is returned.
+     * @param block the block
+     * @return the function closest to the block.
+     * @see #getParentFunction(Block)
+     */
+    public FunctionNode getFunction(Block block) {
+        if(block instanceof FunctionNode) {
+            return (FunctionNode)block;
+        }
+        return getParentFunction(block);
+    }
+
+    /**
+     * Returns the closest function node to the block and all its ancestor functions. If the block is itself a function,
+     * it is returned too.
+     * @param block the block
+     * @return the closest function node to the block and all its ancestor functions.
+     */
+    public Iterator<FunctionNode> getFunctions(final Block block) {
+        return new FunctionIterator(getBlocks(block));
+    }
+
+    /**
+     * Returns the containing function of the block. If the block is itself a function, its parent function is returned.
+     * @param block the block
+     * @return the containing function of the block.
+     * @see #getFunction(Block)
+     */
+    public FunctionNode getParentFunction(Block block) {
+        return getFirstFunction(getAncestorBlocks(block));
+    }
+
+    private static FunctionNode getFirstFunction(Iterator<Block> it) {
+        while(it.hasNext()) {
+            final Block ancestor = it.next();
+            if(ancestor instanceof FunctionNode) {
+                return (FunctionNode)ancestor;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns the innermost block in the context.
+     * @return the innermost block in the context.
+     */
+    public Block getCurrentBlock() {
+        return lexicalContext.element();
+    }
+
+    /**
+     * Returns the innermost function in the context.
+     * @return the innermost function in the context.
+     */
+    public FunctionNode getCurrentFunction() {
+        return getFirstFunction(getBlocks());
+    }
+}
--- a/src/jdk/nashorn/internal/ir/LineNumberNode.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/LineNumberNode.java	Mon Apr 01 21:42:31 2013 -0700
@@ -63,8 +63,8 @@
 
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
-            return visitor.leave(this);
+        if (visitor.enterLineNumberNode(this) != null) {
+            return visitor.leaveLineNumberNode(this);
         }
 
         return this;
--- a/src/jdk/nashorn/internal/ir/LiteralNode.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/LiteralNode.java	Mon Apr 01 21:42:31 2013 -0700
@@ -46,7 +46,7 @@
  */
 public abstract class LiteralNode<T> extends Node implements PropertyKey {
     /** Literal value */
-    protected T value;
+    protected final T value;
 
      /**
      * Constructor
@@ -67,8 +67,17 @@
      * @param literalNode source node
      */
     protected LiteralNode(final LiteralNode<T> literalNode) {
+        this(literalNode, literalNode.value);
+    }
+
+    /**
+     * A copy constructor with value change.
+     * @param literalNode the original literal node
+     * @param newValue new value for this node
+     */
+    protected LiteralNode(final LiteralNode<T> literalNode, final T newValue) {
         super(literalNode);
-        this.value = literalNode.value;
+        this.value = newValue;
     }
 
     @Override
@@ -217,8 +226,8 @@
      */
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
-            return visitor.leave(this);
+        if (visitor.enterLiteralNode(this) != null) {
+            return visitor.leaveLiteralNode(this);
         }
 
         return this;
@@ -544,6 +553,10 @@
             super(literalNode);
         }
 
+        private NodeLiteralNode(final LiteralNode<Node> literalNode, final Node value) {
+            super(literalNode, value);
+        }
+
         @Override
         protected Node copy(final CopyState cs) {
             return new NodeLiteralNode(this);
@@ -551,11 +564,14 @@
 
         @Override
         public Node accept(final NodeVisitor visitor) {
-            if (visitor.enter(this) != null) {
+            if (visitor.enterLiteralNode(this) != null) {
                 if (value != null) {
-                    value = value.accept(visitor);
+                    final Node newValue = value.accept(visitor);
+                    if(value != newValue) {
+                        return visitor.leaveLiteralNode(new NodeLiteralNode(this, newValue));
+                    }
                 }
-                return visitor.leave(this);
+                return visitor.leaveLiteralNode(this);
             }
 
             return this;
@@ -878,14 +894,14 @@
 
         @Override
         public Node accept(final NodeVisitor visitor) {
-            if (visitor.enter(this) != null) {
+            if (visitor.enterLiteralNode(this) != null) {
                 for (int i = 0; i < value.length; i++) {
                     final Node element = value[i];
                     if (element != null) {
                         value[i] = element.accept(visitor);
                     }
                 }
-                return visitor.leave(this);
+                return visitor.leaveLiteralNode(this);
             }
             return this;
         }
--- a/src/jdk/nashorn/internal/ir/Location.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/Location.java	Mon Apr 01 21:42:31 2013 -0700
@@ -65,7 +65,11 @@
 
     @Override
     protected Object clone() {
-        return new Location(this);
+        try {
+            return super.clone();
+        } catch(CloneNotSupportedException e) {
+            throw new AssertionError(e);
+        }
     }
 
     @Override
--- a/src/jdk/nashorn/internal/ir/Node.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/Node.java	Mon Apr 01 21:42:31 2013 -0700
@@ -165,12 +165,19 @@
             return true;
         }
 
-        setIsResolved();
+        setIsResolved(true);
 
         return false;
     }
 
     /**
+     * Reset the resolved flag.
+     */
+    public void resetResolved() {
+        setIsResolved(false);
+    }
+
+    /**
      * Is this a debug info node like LineNumberNode etc?
      *
      * @return true if this is a debug node
@@ -234,8 +241,7 @@
      *
      * @return Deep copy of the  Node.
      */
-    @Override
-    public final Node clone() {
+    public final Node copy() {
         return copy(new CopyState());
     }
 
@@ -349,10 +355,10 @@
     }
 
     /**
-     * Flag this node as resolved, i.e. code has been generated for it
+     * Flag this node as resolved or not, i.e. code has been generated for it
      */
-    public void setIsResolved() {
-        this.isResolved = true;
+    private void setIsResolved(boolean isResolved) {
+        this.isResolved = isResolved;
     }
 
     /**
--- a/src/jdk/nashorn/internal/ir/ObjectNode.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/ObjectNode.java	Mon Apr 01 21:42:31 2013 -0700
@@ -28,7 +28,6 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
-import jdk.nashorn.internal.ir.annotations.Ignore;
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
 import jdk.nashorn.internal.runtime.Source;
 
@@ -36,9 +35,6 @@
  * IR representation of an object literal.
  */
 public class ObjectNode extends Node {
-    /** Literal context. */
-    @Ignore
-    private Block context;
 
     /** Literal elements. */
     private final List<Node> elements;
@@ -49,13 +45,11 @@
      * @param source   the source
      * @param token    token
      * @param finish   finish
-     * @param context  the block for this ObjectNode
      * @param elements the elements used to initialize this ObjectNode
      */
-    public ObjectNode(final Source source, final long token, final int finish, final Block context, final List<Node> elements) {
+    public ObjectNode(final Source source, final long token, final int finish, final List<Node> elements) {
         super(source, token, finish);
 
-        this.context  = context;
         this.elements = elements;
     }
 
@@ -68,7 +62,6 @@
             newElements.add(cs.existingOrCopy(element));
         }
 
-        this.context  = (Block)cs.existingOrCopy(objectNode.context);
         this.elements = newElements;
     }
 
@@ -79,16 +72,12 @@
 
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
-            if (context != null) {
-                context = (Block)context.accept(visitor);
-            }
-
+        if (visitor.enterObjectNode(this) != null) {
             for (int i = 0, count = elements.size(); i < count; i++) {
                 elements.set(i, elements.get(i).accept(visitor));
             }
 
-            return visitor.leave(this);
+            return visitor.leaveObjectNode(this);
         }
 
         return this;
@@ -117,14 +106,6 @@
     }
 
     /**
-     * Get the block that is this ObjectNode's literal context
-     * @return the block
-     */
-    public Block getContext() {
-        return context;
-    }
-
-    /**
      * Get the elements of this literal node
      * @return a list of elements
      */
--- a/src/jdk/nashorn/internal/ir/PropertyNode.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/PropertyNode.java	Mon Apr 01 21:42:31 2013 -0700
@@ -88,7 +88,7 @@
 
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
+        if (visitor.enterPropertyNode(this) != null) {
             key = (PropertyKey)((Node)key).accept(visitor);
 
             if (value != null) {
@@ -103,7 +103,7 @@
                 setter = setter.accept(visitor);
             }
 
-            return visitor.leave(this);
+            return visitor.leavePropertyNode(this);
         }
 
         return this;
--- a/src/jdk/nashorn/internal/ir/ReferenceNode.java	Thu Mar 28 10:55:17 2013 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,91 +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.ir;
-
-import jdk.nashorn.internal.ir.annotations.Reference;
-import jdk.nashorn.internal.ir.visitor.NodeVisitor;
-import jdk.nashorn.internal.runtime.Source;
-
-/**
- * IR representation of a reference to another entity (function.)
- */
-public class ReferenceNode extends Node {
-    /** Node referenced. */
-    @Reference
-    private final FunctionNode reference;
-
-    /**
-     * Constructor
-     *
-     * @param source the source
-     * @param token  token
-     * @param finish finish
-     * @param reference the function node to reference
-     */
-    public ReferenceNode(final Source source, final long token, final int finish, final FunctionNode reference) {
-        super(source, token, finish);
-
-        this.reference = reference;
-    }
-
-    private ReferenceNode(final ReferenceNode referenceNode) {
-        super(referenceNode);
-
-        this.reference = referenceNode.reference;
-    }
-
-    @Override
-    protected Node copy(final CopyState cs) {
-        return new ReferenceNode(this);
-    }
-
-    @Override
-    public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
-            return visitor.leave(this);
-        }
-
-        return this;
-    }
-
-    @Override
-    public void toString(final StringBuilder sb) {
-        if (reference == null) {
-            sb.append("null");
-        } else {
-            reference.toString(sb);
-        }
-    }
-
-    /**
-     * Get there function node reference that this node points tp
-     * @return a function node reference
-     */
-    public FunctionNode getReference() {
-        return reference;
-    }
-
-}
--- a/src/jdk/nashorn/internal/ir/ReturnNode.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/ReturnNode.java	Mon Apr 01 21:42:31 2013 -0700
@@ -100,12 +100,12 @@
 
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
+        if (visitor.enterReturnNode(this) != null) {
             if (expression != null) {
                 expression = expression.accept(visitor);
             }
 
-            return visitor.leave(this);
+            return visitor.leaveReturnNode(this);
         }
 
         return this;
--- a/src/jdk/nashorn/internal/ir/RuntimeNode.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/RuntimeNode.java	Mon Apr 01 21:42:31 2013 -0700
@@ -38,7 +38,7 @@
  * IR representation for a runtime call.
  *
  */
-public class RuntimeNode extends Node implements TypeOverride {
+public class RuntimeNode extends Node implements TypeOverride<RuntimeNode> {
 
     /**
      * Request enum used for meta-information about the runtime request
@@ -393,8 +393,9 @@
     }
 
     @Override
-    public void setType(final Type type) {
+    public RuntimeNode setType(final Type type) {
         this.callSiteType = type;
+        return this;
     }
 
     @Override
@@ -408,12 +409,12 @@
 
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
+        if (visitor.enterRuntimeNode(this) != null) {
             for (int i = 0, count = args.size(); i < count; i++) {
                 args.set(i, args.get(i).accept(visitor));
             }
 
-            return visitor.leave(this);
+            return visitor.leaveRuntimeNode(this);
         }
 
         return this;
--- a/src/jdk/nashorn/internal/ir/SplitNode.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/SplitNode.java	Mon Apr 01 21:42:31 2013 -0700
@@ -108,10 +108,10 @@
         visitor.setCurrentMethodEmitter(getMethodEmitter());
 
         try {
-            if (visitor.enter(this) != null) {
+            if (visitor.enterSplitNode(this) != null) {
                 body = body.accept(visitor);
 
-                return visitor.leave(this);
+                return visitor.leaveSplitNode(this);
             }
         } finally {
             visitor.setCurrentCompileUnit(saveCompileUnit);
--- a/src/jdk/nashorn/internal/ir/SwitchNode.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/SwitchNode.java	Mon Apr 01 21:42:31 2013 -0700
@@ -85,7 +85,7 @@
 
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
+        if (visitor.enterSwitchNode(this) != null) {
             expression = expression.accept(visitor);
 
             for (int i = 0, count = cases.size(); i < count; i++) {
@@ -94,7 +94,7 @@
 
             //the default case is in the cases list and should not be explicitly traversed!
 
-            return visitor.leave(this);
+            return visitor.leaveSwitchNode(this);
         }
 
         return this;
--- a/src/jdk/nashorn/internal/ir/Symbol.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/Symbol.java	Mon Apr 01 21:42:31 2013 -0700
@@ -38,31 +38,31 @@
  */
 
 public final class Symbol implements Comparable<Symbol> {
-    /** Symbol flags. Kind ordered by precedence. */
-    public static final int IS_TEMP     = 0b0000_0001;
+    /** Symbol kinds. Kind ordered by precedence. */
+    public static final int IS_TEMP     = 1;
     /** Is this Global */
-    public static final int IS_GLOBAL   = 0b0000_0010;
+    public static final int IS_GLOBAL   = 2;
     /** Is this a variable */
-    public static final int IS_VAR      = 0b0000_0011;
+    public static final int IS_VAR      = 3;
     /** Is this a parameter */
-    public static final int IS_PARAM    = 0b0000_0100;
+    public static final int IS_PARAM    = 4;
     /** Is this a constant */
-    public static final int IS_CONSTANT = 0b0000_0101;
-
-    static final int KINDMASK = 0b0000_1111;
+    public static final int IS_CONSTANT = 5;
+    /** Mask for kind flags */
+    public static final int KINDMASK = (1 << 3) - 1; // Kinds are represented by lower three bits
 
     /** Is this scope */
-    public static final int IS_SCOPE         = 0b0000_0001_0000;
+    public static final int IS_SCOPE         = 1 << 4;
     /** Is this a this symbol */
-    public static final int IS_THIS          = 0b0000_0010_0000;
+    public static final int IS_THIS          = 1 << 5;
     /** Can this symbol ever be undefined */
-    public static final int CAN_BE_UNDEFINED = 0b0000_0100_0000;
+    public static final int CAN_BE_UNDEFINED = 1 << 6;
     /** Can this symbol ever have primitive types */
-    public static final int CAN_BE_PRIMITIVE = 0b0000_1000_0000;
+    public static final int CAN_BE_PRIMITIVE = 1 << 7;
     /** Is this a let */
-    public static final int IS_LET           = 0b0001_0000_0000;
+    public static final int IS_LET           = 1 << 8;
     /** Is this an internal symbol, never represented explicitly in source code */
-    public static final int IS_INTERNAL      = 0b0010_0000_0000;
+    public static final int IS_INTERNAL      = 1 << 9;
 
     /** Null or name identifying symbol. */
     private final String name;
@@ -269,15 +269,6 @@
         return type.isCategory2() ? 2 : 1;
     }
 
-    /**
-     * Return the defining function (scope.)
-     *
-     * @return Defining function.
-     */
-    public FunctionNode findFunction() {
-        return block != null ? block.getFunction() : null;
-    }
-
     @Override
     public boolean equals(final Object other) {
         if (!(other instanceof Symbol)) {
@@ -487,27 +478,6 @@
     }
 
     /**
-     * Check if this symbol can be accessed directly with a putfield or getfield or dynamic load
-     *
-     * @param currentFunction function to check for fast scope
-     * @return true if fast scope
-     */
-    public boolean isFastScope(final FunctionNode currentFunction) {
-        if (!isScope() || !block.needsScope()) {
-            return false;
-        }
-        // Allow fast scope access if no parent function contains with or eval
-        FunctionNode func = currentFunction;
-        while (func != null) {
-            if (func.hasWith() || func.hasEval()) {
-                return false;
-            }
-            func = func.findParentFunction();
-        }
-        return true;
-    }
-
-    /**
      * Get the block in which the symbol is defined
      * @return a block
      */
@@ -651,7 +621,7 @@
      * @return true if this this is a global scope symbol
      */
     public boolean isTopLevel() {
-        return block instanceof FunctionNode && ((FunctionNode) block).isScript();
+        return block instanceof FunctionNode && ((FunctionNode) block).isProgram();
     }
 
 
--- a/src/jdk/nashorn/internal/ir/TernaryNode.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/TernaryNode.java	Mon Apr 01 21:42:31 2013 -0700
@@ -77,11 +77,11 @@
 
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
-            lhs = lhs.accept(visitor);
-            rhs = rhs.accept(visitor);
-            third = third.accept(visitor);
-            return visitor.leave(this);
+        if (visitor.enterTernaryNode(this) != null) {
+            final Node newLhs = lhs().accept(visitor);
+            final Node newRhs = rhs().accept(visitor);
+            final Node newThird = third.accept(visitor);
+            return visitor.leaveTernaryNode((TernaryNode)setThird(newThird).setLHS(newLhs).setRHS(newRhs));
         }
 
         return this;
@@ -133,8 +133,12 @@
     /**
      * Reset the "third" node for this ternary expression, i.e. "z" in x ? y : z
      * @param third a node
+     * @return a node equivalent to this one except for the requested change.
      */
-    public void setThird(final Node third) {
-        this.third = third;
+    public TernaryNode setThird(final Node third) {
+        if(this.third == third) return this;
+        final TernaryNode n = (TernaryNode)clone();
+        n.third = third;
+        return n;
     }
 }
--- a/src/jdk/nashorn/internal/ir/ThrowNode.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/ThrowNode.java	Mon Apr 01 21:42:31 2013 -0700
@@ -75,9 +75,9 @@
      */
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
+        if (visitor.enterThrowNode(this) != null) {
             setExpression(expression.accept(visitor));
-            return visitor.leave(this);
+            return visitor.leaveThrowNode(this);
         }
 
         return this;
--- a/src/jdk/nashorn/internal/ir/TryNode.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/TryNode.java	Mon Apr 01 21:42:31 2013 -0700
@@ -101,7 +101,7 @@
      */
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
+        if (visitor.enterTryNode(this) != null) {
             // Need to do first for termination analysis.
             if (finallyBody != null) {
                 finallyBody = (Block)finallyBody.accept(visitor);
@@ -115,7 +115,7 @@
             }
             this.catchBlocks = newCatchBlocks;
 
-            return visitor.leave(this);
+            return visitor.leaveTryNode(this);
         }
 
         return this;
@@ -155,6 +155,15 @@
     }
 
     /**
+     * Returns true if the specified block is the body of this try block, or any of its catch blocks.
+     * @param block the block
+     * @return true if the specified block is the body of this try block, or any of its catch blocks.
+     */
+    public boolean isChildBlock(Block block) {
+        return body == block || catchBlocks.contains(block);
+    }
+
+    /**
      * Get the catch blocks for this try block
      * @return a list of blocks
      */
--- a/src/jdk/nashorn/internal/ir/TypeOverride.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/TypeOverride.java	Mon Apr 01 21:42:31 2013 -0700
@@ -36,15 +36,17 @@
  * by using JSType.toInt32. Especially in scenarios where the field is already stored
  * as a primitive, this will be much faster than the "object is all I see" scope
  * available in the method
+ * @param <T> the type of the node implementing the interface
  */
 
-public interface TypeOverride {
+public interface TypeOverride<T extends Node> {
     /**
      * Set the override type
      *
      * @param type  the type
+     * @return a node equivalent to this one except for the requested change.
      */
-    public void setType(final Type type);
+    public T setType(final Type type);
 
     /**
      * Returns true if this node can have a callsite override, e.g. all scope ident nodes
--- a/src/jdk/nashorn/internal/ir/UnaryNode.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/UnaryNode.java	Mon Apr 01 21:42:31 2013 -0700
@@ -41,7 +41,7 @@
  */
 public class UnaryNode extends Node implements Assignment<Node> {
     /** Right hand side argument. */
-    protected Node rhs;
+    private Node rhs;
 
     /**
      * Constructor
@@ -104,6 +104,11 @@
     }
 
     @Override
+    public Node setAssignmentDest(Node n) {
+        return setRHS(n);
+    }
+
+    @Override
     public Node getAssignmentSource() {
         return getAssignmentDest();
     }
@@ -132,9 +137,8 @@
      */
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
-            rhs = rhs.accept(visitor);
-            return visitor.leave(this);
+        if (visitor.enterUnaryNode(this) != null) {
+            return visitor.leaveUnaryNode(setRHS(rhs.accept(visitor)));
         }
 
         return this;
@@ -152,9 +156,9 @@
 
         if (isConvert) {
             convertPos = sb.length();
-            sb.append("((");
+            sb.append("(");
             sb.append(getType());
-            sb.append(")");
+            sb.append(")(");
         }
 
         if (!isPostfix && !isConvert) {
@@ -191,8 +195,6 @@
                 sb.setCharAt(convertPos, ' ');
             }
         }
-
-        //TODO - conversions still have too many parenthesis - makes --print-lower-parse hard to read
     }
 
     /**
@@ -214,10 +216,12 @@
      * @see BinaryNode
      *
      * @param rhs right hand side or expression node
+     * @return a node equivalent to this one except for the requested change.
      */
-    public void setRHS(final Node rhs) {
-        this.rhs = rhs;
+    public UnaryNode setRHS(final Node rhs) {
+        if(this.rhs == rhs) return this;
+        final UnaryNode n = (UnaryNode)clone();
+        n.rhs = rhs;
+        return n;
     }
-
-
 }
--- a/src/jdk/nashorn/internal/ir/VarNode.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/VarNode.java	Mon Apr 01 21:42:31 2013 -0700
@@ -38,8 +38,8 @@
     /** Initialization expression. */
     private Node init;
 
-    /** Is this a function var node */
-    private boolean isFunctionVarNode;
+    /** Is this a var statement (as opposed to a "var" in a for loop statement) */
+    private final boolean isStatement;
 
     /**
      * Constructor
@@ -51,20 +51,34 @@
      * @param init   init node or null if just a declaration
      */
     public VarNode(final Source source, final long token, final int finish, final IdentNode name, final Node init) {
+        this(source, token, finish, name, init, true);
+    }
+
+    /**
+     * Constructor
+     *
+     * @param source the source
+     * @param token  token
+     * @param finish finish
+     * @param name   name of variable
+     * @param init   init node or null if just a declaration
+     * @param isStatement if this is a var statement (true), or a for-loop initializer (false)
+     */
+    public VarNode(final Source source, final long token, final int finish, final IdentNode name, final Node init, boolean isStatement) {
         super(source, token, finish);
 
-        this.name  = name;
+        this.name  = init == null ? name : name.setIsInitializedHere();
         this.init  = init;
-        if (init != null) {
-            this.name.setIsInitializedHere();
-        }
+        this.isStatement = isStatement;
     }
 
+
     private VarNode(final VarNode varNode, final CopyState cs) {
         super(varNode);
 
         this.name = (IdentNode)cs.existingOrCopy(varNode.name);
         this.init = cs.existingOrCopy(varNode.init);
+        this.isStatement = varNode.isStatement;
     }
 
     @Override
@@ -83,6 +97,11 @@
     }
 
     @Override
+    public Node setAssignmentDest(IdentNode n) {
+        return setName(n);
+    }
+
+    @Override
     public Node getAssignmentSource() {
         return isAssignment() ? getInit() : null;
     }
@@ -127,16 +146,19 @@
      */
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
-            name = (IdentNode)name.accept(visitor);
-
-            if (init != null) {
-                init = init.accept(visitor);
+        if (visitor.enterVarNode(this) != null) {
+            final IdentNode newName = (IdentNode)name.accept(visitor);
+            final Node newInit = init == null ? null : init.accept(visitor);
+            final VarNode newThis;
+            if(name != newName || init != newInit) {
+                newThis = (VarNode)clone();
+                newThis.init = newInit;
+                newThis.name = newInit == null ? newName : newName.setIsInitializedHere();
+            } else {
+                newThis = this;
             }
-
-            return visitor.leave(this);
+            return visitor.leaveVarNode(newThis);
         }
-
         return this;
     }
 
@@ -162,9 +184,13 @@
     /**
      * Reset the initialization expression
      * @param init new initialization expression
+     * @return a node equivalent to this one except for the requested change.
      */
-    public void setInit(final Node init) {
-        this.init = init;
+    public VarNode setInit(final Node init) {
+        if(this.init == init) return this;
+        final VarNode n = (VarNode)clone();
+        n.init = init;
+        return n;
     }
 
     /**
@@ -179,30 +205,26 @@
      * Reset the identifier for this VarNode
      * @param name new IdentNode representing the variable being set or declared
      */
-    public void setName(final IdentNode name) {
-        this.name = name;
+    private VarNode setName(final IdentNode name) {
+        if(this.name == name) return this;
+        final VarNode n = (VarNode)clone();
+        n.name = name;
+        return n;
     }
 
     /**
-     * Check if this is a virtual assignment of a function node. Function nodes declared
-     * with a name are hoisted to the top of the scope and appear as symbols too. This is
-     * implemented by representing them as virtual VarNode assignments added to the code
-     * during lowering
-     *
-     * @see FunctionNode
-     *
-     * @return true if this is a virtual function declaration
+     * Returns true if this is a var statement (as opposed to a var initializer in a for loop).
+     * @return true if this is a var statement (as opposed to a var initializer in a for loop).
      */
-    public boolean isFunctionVarNode() {
-        return isFunctionVarNode;
+    public boolean isStatement() {
+        return isStatement;
     }
 
     /**
-     * Flag this var node as a virtual function var node assignment as described in
-     * {@link VarNode#isFunctionVarNode()}
+     * Returns true if this is a function declaration.
+     * @return true if this is a function declaration.
      */
-    public void setIsFunctionVarNode() {
-        this.isFunctionVarNode = true;
+    public boolean isFunctionDeclaration() {
+        return init instanceof FunctionNode && ((FunctionNode)init).isDeclared();
     }
-
 }
--- a/src/jdk/nashorn/internal/ir/WhileNode.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/WhileNode.java	Mon Apr 01 21:42:31 2013 -0700
@@ -88,11 +88,11 @@
      */
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
+        if (visitor.enterWhileNode(this) != null) {
             test = test.accept(visitor);
             body = (Block)body.accept(visitor);
 
-            return visitor.leave(this);
+            return visitor.leaveWhileNode(this);
         }
         return this;
     }
--- a/src/jdk/nashorn/internal/ir/WithNode.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/WithNode.java	Mon Apr 01 21:42:31 2013 -0700
@@ -73,10 +73,10 @@
      */
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
+        if (visitor.enterWithNode(this) != null) {
             expression = expression.accept(visitor);
             body = (Block)body.accept(visitor);
-            return visitor.leave(this);
+            return visitor.leaveWithNode(this);
         }
 
         return this;
--- a/src/jdk/nashorn/internal/ir/annotations/ChildNode.java	Thu Mar 28 10:55:17 2013 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +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.ir.annotations;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * This is a child node, a real node, not a reference, to an IR node that should
- * be traversed.
- * <p>
- * TODO Currently not in use.  Would make e.g. accept methods simple and unified
- * @see jdk.nashorn.internal.ir.Node
- */
-@Retention(value=RetentionPolicy.RUNTIME)
-public @interface ChildNode {
-    /** order of traversal compared to other children */
-    public int order() default -1;
-}
--- a/src/jdk/nashorn/internal/ir/annotations/ParentNode.java	Thu Mar 28 10:55:17 2013 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,44 +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.ir.annotations;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Signifies a parent of a node, i.e. node that should not be traversed if we
- * go down the AST. In automatic parsing this can be handled by @Reference
- * annotations instead, as all parents are references.
- * <p>
- * TODO The use case is automating and creating one implementation of something like
- * Node.getParent()
- *
- * @see jdk.nashorn.internal.ir.Node
- */
-@Retention(value=RetentionPolicy.RUNTIME)
-public @interface ParentNode {
-    // EMPTY
-}
--- a/src/jdk/nashorn/internal/ir/annotations/Reference.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/annotations/Reference.java	Mon Apr 01 21:42:31 2013 -0700
@@ -33,7 +33,6 @@
  * AST traversal and cloning. Cloning currently as a rule uses
  * existingOrSame for references and otherwise existingOrCopy
  * <p>
- * TODO this could probably be automated using the @Reference annotation.
  */
 
 @Retention(value=RetentionPolicy.RUNTIME)
--- a/src/jdk/nashorn/internal/ir/debug/ASTWriter.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/debug/ASTWriter.java	Mon Apr 01 21:42:31 2013 -0700
@@ -27,6 +27,7 @@
 
 import java.lang.reflect.Field;
 import java.util.ArrayDeque;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Deque;
 import java.util.Iterator;
@@ -36,10 +37,10 @@
 import jdk.nashorn.internal.ir.Node;
 import jdk.nashorn.internal.ir.TernaryNode;
 import jdk.nashorn.internal.ir.annotations.Ignore;
-import jdk.nashorn.internal.ir.annotations.ParentNode;
 import jdk.nashorn.internal.ir.annotations.Reference;
 import jdk.nashorn.internal.parser.Token;
 import jdk.nashorn.internal.runtime.Context;
+import jdk.nashorn.internal.runtime.Debug;
 
 /**
  * AST-as-text visualizer. Sometimes you want tree form and not source
@@ -47,7 +48,6 @@
  *
  * see the flags --print-ast and --print-ast-lower
  */
-
 public final class ASTWriter {
     /** Root node from which to start the traversal */
     private final Node root;
@@ -71,12 +71,22 @@
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
-        printAST(sb, null, "root", root, 0);
+        printAST(sb, null, null, "root", root, 0);
         return sb.toString();
     }
 
+    /**
+     * Return the visited nodes in an ordered list
+     * @return the list of nodes in order
+     */
+    public Node[] toArray() {
+        final List<Node> preorder = new ArrayList<>();
+        printAST(new StringBuilder(), preorder, null, "root", root, 0);
+        return preorder.toArray(new Node[preorder.size()]);
+    }
+
     @SuppressWarnings("unchecked")
-    private void printAST(final StringBuilder sb, final Field field, final String name, final Node node, final int indent) {
+    private void printAST(final StringBuilder sb, final List<Node> preorder, final Field field, final String name, final Node node, final int indent) {
         ASTWriter.indent(sb, indent);
         if (node == null) {
             sb.append("[Object ");
@@ -85,13 +95,23 @@
             return;
         }
 
+        if (preorder != null) {
+            preorder.add(node);
+        }
+
         final boolean isReference = field != null && field.getAnnotation(Reference.class) != null;
 
         Class<?> clazz = node.getClass();
         String   type  = clazz.getName();
 
         type = type.substring(type.lastIndexOf('.') + 1, type.length());
-//        type += "@" + Debug.id(node) + "#" + node.getSymbol();
+        if (isReference) {
+            type = "ref: " + type;
+        }
+        type += "@" + Debug.id(node);
+        if (node.getSymbol() != null) {
+            type += "#" + node.getSymbol();
+        }
 
         final List<Field> children = new LinkedList<>();
 
@@ -153,9 +173,7 @@
                 append('\n');
 
             for (final Field child : children) {
-                if (child.getAnnotation(ParentNode.class) != null) {
-                    continue;
-                } else if (child.getAnnotation(Ignore.class) != null) {
+                if (child.getAnnotation(Ignore.class) != null) {
                     continue;
                 }
 
@@ -168,7 +186,7 @@
                 }
 
                 if (value instanceof Node) {
-                    printAST(sb, child, child.getName(), (Node)value, indent + 1);
+                    printAST(sb, preorder, child, child.getName(), (Node)value, indent + 1);
                 } else if (value instanceof Collection) {
                     int pos = 0;
                     ASTWriter.indent(sb, indent + 1);
@@ -180,7 +198,7 @@
                         append('\n');
 
                     for (final Node member : (Collection<Node>)value) {
-                        printAST(sb, child, child.getName() + "[" + pos++ + "]", member, indent + 2);
+                        printAST(sb, preorder, child, child.getName() + "[" + pos++ + "]", member, indent + 2);
                     }
                 }
             }
--- a/src/jdk/nashorn/internal/ir/debug/JSONWriter.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/debug/JSONWriter.java	Mon Apr 01 21:42:31 2013 -0700
@@ -112,7 +112,7 @@
     }
 
     @Override
-    public Node enter(final AccessNode accessNode) {
+    public Node enterAccessNode(final AccessNode accessNode) {
         enterDefault(accessNode);
 
         type("MemberExpression");
@@ -132,7 +132,7 @@
     }
 
     @Override
-    public Node enter(final Block block) {
+    public Node enterBlock(final Block block) {
         enterDefault(block);
 
         type("BlockStatement");
@@ -154,7 +154,7 @@
     }
 
     @Override
-    public Node enter(final BinaryNode binaryNode) {
+    public Node enterBinaryNode(final BinaryNode binaryNode) {
         enterDefault(binaryNode);
 
         final String name;
@@ -183,7 +183,7 @@
     }
 
     @Override
-    public Node enter(final BreakNode breakNode) {
+    public Node enterBreakNode(final BreakNode breakNode) {
         enterDefault(breakNode);
 
         type("BreakStatement");
@@ -201,7 +201,7 @@
     }
 
     @Override
-    public Node enter(final CallNode callNode) {
+    public Node enterCallNode(final CallNode callNode) {
         enterDefault(callNode);
 
         type("CallExpression");
@@ -217,7 +217,7 @@
     }
 
     @Override
-    public Node enter(final CaseNode caseNode) {
+    public Node enterCaseNode(final CaseNode caseNode) {
         enterDefault(caseNode);
 
         type("SwitchCase");
@@ -238,7 +238,7 @@
     }
 
     @Override
-    public Node enter(final CatchNode catchNode) {
+    public Node enterCatchNode(final CatchNode catchNode) {
         enterDefault(catchNode);
 
         type("CatchClause");
@@ -264,7 +264,7 @@
     }
 
     @Override
-    public Node enter(final ContinueNode continueNode) {
+    public Node enterContinueNode(final ContinueNode continueNode) {
         enterDefault(continueNode);
 
         type("ContinueStatement");
@@ -282,7 +282,7 @@
     }
 
     @Override
-    public Node enter(final DoWhileNode doWhileNode) {
+    public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
         enterDefault(doWhileNode);
 
         type("DoWhileStatement");
@@ -299,7 +299,7 @@
     }
 
     @Override
-    public Node enter(final EmptyNode emptyNode) {
+    public Node enterEmptyNode(final EmptyNode emptyNode) {
         enterDefault(emptyNode);
 
         type("EmptyStatement");
@@ -308,7 +308,7 @@
     }
 
     @Override
-    public Node enter(final ExecuteNode executeNode) {
+    public Node enterExecuteNode(final ExecuteNode executeNode) {
         enterDefault(executeNode);
 
         type("ExpressionStatement");
@@ -321,7 +321,7 @@
     }
 
     @Override
-    public Node enter(final ForNode forNode) {
+    public Node enterForNode(final ForNode forNode) {
         enterDefault(forNode);
 
         if (forNode.isForIn() || (forNode.isForEach() && forNode.getInit() != null)) {
@@ -384,14 +384,14 @@
     }
 
     @Override
-    public Node enter(final FunctionNode functionNode) {
+    public Node enterFunctionNode(final FunctionNode functionNode) {
         enterDefault(functionNode);
 
-        final boolean program = functionNode.isScript();
+        final boolean program = functionNode.isProgram();
         final String name;
         if (program) {
             name = "Program";
-        } else if (functionNode.isStatement()) {
+        } else if (functionNode.isDeclared()) {
             name = "FunctionDeclaration";
         } else {
             name = "FunctionExpression";
@@ -419,20 +419,11 @@
         }
 
         // body consists of nested functions and statements
-        final List<FunctionNode> funcs = functionNode.getFunctions();
         final List<Node> stats = functionNode.getStatements();
-        final int size = stats.size() + funcs.size();
+        final int size = stats.size();
         int idx = 0;
         arrayStart("body");
 
-        for (final Node func : funcs) {
-            func.accept(this);
-            if (idx != (size - 1)) {
-                comma();
-            }
-            idx++;
-        }
-
         for (final Node stat : stats) {
             if (! stat.isDebug()) {
                 stat.accept(this);
@@ -448,7 +439,7 @@
     }
 
     @Override
-    public Node enter(final IdentNode identNode) {
+    public Node enterIdentNode(final IdentNode identNode) {
         enterDefault(identNode);
 
         final String name = identNode.getName();
@@ -464,7 +455,7 @@
     }
 
     @Override
-    public Node enter(final IfNode ifNode) {
+    public Node enterIfNode(final IfNode ifNode) {
         enterDefault(ifNode);
 
         type("IfStatement");
@@ -490,7 +481,7 @@
     }
 
     @Override
-    public Node enter(final IndexNode indexNode) {
+    public Node enterIndexNode(final IndexNode indexNode) {
         enterDefault(indexNode);
 
         type("MemberExpression");
@@ -510,7 +501,7 @@
     }
 
     @Override
-    public Node enter(final LabelNode labelNode) {
+    public Node enterLabelNode(final LabelNode labelNode) {
         enterDefault(labelNode);
 
         type("LabeledStatement");
@@ -527,13 +518,13 @@
     }
 
     @Override
-    public Node enter(final LineNumberNode lineNumberNode) {
+    public Node enterLineNumberNode(final LineNumberNode lineNumberNode) {
         return null;
     }
 
     @SuppressWarnings("rawtypes")
     @Override
-    public Node enter(final LiteralNode literalNode) {
+    public Node enterLiteralNode(final LiteralNode literalNode) {
         enterDefault(literalNode);
 
         if (literalNode instanceof LiteralNode.ArrayLiteralNode) {
@@ -569,7 +560,7 @@
     }
 
     @Override
-    public Node enter(final ObjectNode objectNode) {
+    public Node enterObjectNode(final ObjectNode objectNode) {
         enterDefault(objectNode);
 
         type("ObjectExpression");
@@ -581,7 +572,7 @@
     }
 
     @Override
-    public Node enter(final PropertyNode propertyNode) {
+    public Node enterPropertyNode(final PropertyNode propertyNode) {
         final Node key = propertyNode.getKey();
 
         final Node value = propertyNode.getValue();
@@ -647,7 +638,7 @@
     }
 
     @Override
-    public Node enter(final ReturnNode returnNode) {
+    public Node enterReturnNode(final ReturnNode returnNode) {
         enterDefault(returnNode);
 
         type("ReturnStatement");
@@ -665,7 +656,7 @@
     }
 
     @Override
-    public Node enter(final RuntimeNode runtimeNode) {
+    public Node enterRuntimeNode(final RuntimeNode runtimeNode) {
         final RuntimeNode.Request req = runtimeNode.getRequest();
 
         if (req == RuntimeNode.Request.DEBUGGER) {
@@ -680,12 +671,12 @@
     }
 
     @Override
-    public Node enter(final SplitNode splitNode) {
+    public Node enterSplitNode(final SplitNode splitNode) {
         return null;
     }
 
     @Override
-    public Node enter(final SwitchNode switchNode) {
+    public Node enterSwitchNode(final SwitchNode switchNode) {
         enterDefault(switchNode);
 
         type("SwitchStatement");
@@ -701,7 +692,7 @@
     }
 
     @Override
-    public Node enter(final TernaryNode ternaryNode) {
+    public Node enterTernaryNode(final TernaryNode ternaryNode) {
         enterDefault(ternaryNode);
 
         type("ConditionalExpression");
@@ -722,7 +713,7 @@
     }
 
     @Override
-    public Node enter(final ThrowNode throwNode) {
+    public Node enterThrowNode(final ThrowNode throwNode) {
         enterDefault(throwNode);
 
         type("ThrowStatement");
@@ -735,7 +726,7 @@
     }
 
     @Override
-    public Node enter(final TryNode tryNode) {
+    public Node enterTryNode(final TryNode tryNode) {
         enterDefault(tryNode);
 
         type("TryStatement");
@@ -760,7 +751,7 @@
     }
 
     @Override
-    public Node enter(final UnaryNode unaryNode) {
+    public Node enterUnaryNode(final UnaryNode unaryNode) {
         enterDefault(unaryNode);
 
         final TokenType tokenType = unaryNode.tokenType();
@@ -816,7 +807,7 @@
     }
 
     @Override
-    public Node enter(final VarNode varNode) {
+    public Node enterVarNode(final VarNode varNode) {
         enterDefault(varNode);
 
         type("VariableDeclaration");
@@ -852,7 +843,7 @@
     }
 
     @Override
-    public Node enter(final WhileNode whileNode) {
+    public Node enterWhileNode(final WhileNode whileNode) {
         enterDefault(whileNode);
 
         type("WhileStatement");
@@ -869,7 +860,7 @@
     }
 
     @Override
-    public Node enter(final WithNode withNode) {
+    public Node enterWithNode(final WithNode withNode) {
         enterDefault(withNode);
 
         type("WithStatement");
--- a/src/jdk/nashorn/internal/ir/debug/PrintVisitor.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/debug/PrintVisitor.java	Mon Apr 01 21:42:31 2013 -0700
@@ -42,7 +42,6 @@
 import jdk.nashorn.internal.ir.LabelNode;
 import jdk.nashorn.internal.ir.LineNumberNode;
 import jdk.nashorn.internal.ir.Node;
-import jdk.nashorn.internal.ir.ReferenceNode;
 import jdk.nashorn.internal.ir.ReturnNode;
 import jdk.nashorn.internal.ir.RuntimeNode;
 import jdk.nashorn.internal.ir.SplitNode;
@@ -138,13 +137,13 @@
      * Visits.
      */
     @Override
-    public Node enter(final AccessNode accessNode) {
+    public Node enterAccessNode(final AccessNode accessNode) {
         accessNode.toString(sb);
         return null;
     }
 
     @Override
-    public Node enter(final Block block) {
+    public Node enterBlock(final Block block) {
         sb.append(' ');
         sb.append('{');
 
@@ -152,21 +151,6 @@
 
         final boolean isFunction = block instanceof FunctionNode;
 
-        if (isFunction) {
-            final FunctionNode       function  = (FunctionNode)block;
-            final List<FunctionNode> functions = function.getFunctions();
-
-            for (final FunctionNode f : functions) {
-                sb.append(EOLN);
-                indent();
-                f.accept(this);
-            }
-
-            if (!functions.isEmpty()) {
-                sb.append(EOLN);
-            }
-        }
-
         final List<Node> statements = block.getStatements();
 
         boolean lastLineNumber = false;
@@ -224,25 +208,25 @@
     }
 
     @Override
-    public Node enter(final BreakNode breakNode) {
+    public Node enterBreakNode(final BreakNode breakNode) {
         breakNode.toString(sb);
         return null;
     }
 
     @Override
-    public Node enter(final CallNode callNode) {
+    public Node enterCallNode(final CallNode callNode) {
         callNode.toString(sb);
         return null;
     }
 
     @Override
-    public Node enter(final ContinueNode continueNode) {
+    public Node enterContinueNode(final ContinueNode continueNode) {
         continueNode.toString(sb);
         return null;
     }
 
     @Override
-    public Node enter(final DoWhileNode doWhileNode) {
+    public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
         sb.append("do");
         doWhileNode.getBody().accept(this);
         sb.append(' ');
@@ -252,7 +236,7 @@
     }
 
     @Override
-    public Node enter(final ExecuteNode executeNode) {
+    public Node enterExecuteNode(final ExecuteNode executeNode) {
         final Node expression = executeNode.getExpression();
 
         if (expression instanceof UnaryNode) {
@@ -265,7 +249,7 @@
     }
 
     @Override
-    public Node enter(final ForNode forNode) {
+    public Node enterForNode(final ForNode forNode) {
         forNode.toString(sb);
         forNode.getBody().accept(this);
 
@@ -273,15 +257,15 @@
     }
 
     @Override
-    public Node enter(final FunctionNode functionNode) {
+    public Node enterFunctionNode(final FunctionNode functionNode) {
         functionNode.toString(sb);
-        enter((Block)functionNode);
+        enterBlock(functionNode);
 
         return null;
     }
 
     @Override
-    public Node enter(final IfNode ifNode) {
+    public Node enterIfNode(final IfNode ifNode) {
         ifNode.toString(sb);
         ifNode.getPass().accept(this);
 
@@ -296,13 +280,13 @@
     }
 
     @Override
-    public Node enter(final IndexNode indexNode) {
+    public Node enterIndexNode(final IndexNode indexNode) {
         indexNode.toString(sb);
         return null;
     }
 
     @Override
-    public Node enter(final LabelNode labeledNode) {
+    public Node enterLabelNode(final LabelNode labeledNode) {
         indent -= TABWIDTH;
         indent();
         indent += TABWIDTH;
@@ -313,7 +297,7 @@
     }
 
     @Override
-    public Node enter(final LineNumberNode lineNumberNode) {
+    public Node enterLineNumberNode(final LineNumberNode lineNumberNode) {
         if (printLineNumbers) {
             lineNumberNode.toString(sb);
         }
@@ -323,25 +307,19 @@
 
 
     @Override
-    public Node enter(final ReferenceNode referenceNode) {
-        referenceNode.toString(sb);
-        return null;
-    }
-
-    @Override
-    public Node enter(final ReturnNode returnNode) {
+    public Node enterReturnNode(final ReturnNode returnNode) {
         returnNode.toString(sb);
         return null;
     }
 
     @Override
-    public Node enter(final RuntimeNode runtimeNode) {
+    public Node enterRuntimeNode(final RuntimeNode runtimeNode) {
         runtimeNode.toString(sb);
         return null;
     }
 
     @Override
-    public Node enter(final SplitNode splitNode) {
+    public Node enterSplitNode(final SplitNode splitNode) {
         splitNode.toString(sb);
         sb.append(EOLN);
         indent += TABWIDTH;
@@ -350,7 +328,7 @@
     }
 
     @Override
-    public Node leave(final SplitNode splitNode) {
+    public Node leaveSplitNode(final SplitNode splitNode) {
         sb.append("</split>");
         sb.append(EOLN);
         indent -= TABWIDTH;
@@ -359,7 +337,7 @@
     }
 
     @Override
-    public Node enter(final SwitchNode switchNode) {
+    public Node enterSwitchNode(final SwitchNode switchNode) {
         switchNode.toString(sb);
         sb.append(" {");
 
@@ -383,13 +361,13 @@
    }
 
     @Override
-    public Node enter(final ThrowNode throwNode) {
+    public Node enterThrowNode(final ThrowNode throwNode) {
         throwNode.toString(sb);
         return null;
     }
 
     @Override
-    public Node enter(final TryNode tryNode) {
+    public Node enterTryNode(final TryNode tryNode) {
         tryNode.toString(sb);
         tryNode.getBody().accept(this);
 
@@ -412,13 +390,19 @@
     }
 
     @Override
-    public Node enter(final VarNode varNode) {
-        varNode.toString(sb);
+    public Node enterVarNode(final VarNode varNode) {
+        sb.append("var ");
+        varNode.getName().toString(sb);
+        final Node init = varNode.getInit();
+        if(init != null) {
+            sb.append(" = ");
+            init.accept(this);
+        }
         return null;
     }
 
     @Override
-    public Node enter(final WhileNode whileNode) {
+    public Node enterWhileNode(final WhileNode whileNode) {
         whileNode.toString(sb);
         whileNode.getBody().accept(this);
 
@@ -426,7 +410,7 @@
     }
 
     @Override
-    public Node enter(final WithNode withNode) {
+    public Node enterWithNode(final WithNode withNode) {
         withNode.toString(sb);
         withNode.getBody().accept(this);
 
--- a/src/jdk/nashorn/internal/ir/visitor/NodeOperatorVisitor.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/visitor/NodeOperatorVisitor.java	Mon Apr 01 21:42:31 2013 -0700
@@ -53,7 +53,7 @@
     }
 
     @Override
-    public final Node enter(final UnaryNode unaryNode) {
+    public final Node enterUnaryNode(final UnaryNode unaryNode) {
         switch (unaryNode.tokenType()) {
         case ADD:
             return enterADD(unaryNode);
@@ -81,12 +81,12 @@
         case INCPOSTFIX:
             return enterDECINC(unaryNode);
         default:
-            return super.enter(unaryNode);
+            return super.enterUnaryNode(unaryNode);
         }
     }
 
     @Override
-    public final Node leave(final UnaryNode unaryNode) {
+    public final Node leaveUnaryNode(final UnaryNode unaryNode) {
         switch (unaryNode.tokenType()) {
         case ADD:
             return leaveADD(unaryNode);
@@ -114,12 +114,12 @@
         case INCPOSTFIX:
             return leaveDECINC(unaryNode);
         default:
-            return super.leave(unaryNode);
+            return super.leaveUnaryNode(unaryNode);
         }
     }
 
     @Override
-    public final Node enter(final BinaryNode binaryNode) {
+    public final Node enterBinaryNode(final BinaryNode binaryNode) {
         switch (binaryNode.tokenType()) {
         case ADD:
             return enterADD(binaryNode);
@@ -198,12 +198,12 @@
         case SUB:
             return enterSUB(binaryNode);
         default:
-            return super.enter(binaryNode);
+            return super.enterBinaryNode(binaryNode);
         }
     }
 
     @Override
-    public final Node leave(final BinaryNode binaryNode) {
+    public final Node leaveBinaryNode(final BinaryNode binaryNode) {
         switch (binaryNode.tokenType()) {
         case ADD:
             return leaveADD(binaryNode);
@@ -282,7 +282,7 @@
         case SUB:
             return leaveSUB(binaryNode);
         default:
-            return super.leave(binaryNode);
+            return super.leaveBinaryNode(binaryNode);
         }
     }
 
--- a/src/jdk/nashorn/internal/ir/visitor/NodeVisitor.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/ir/visitor/NodeVisitor.java	Mon Apr 01 21:42:31 2013 -0700
@@ -49,7 +49,6 @@
 import jdk.nashorn.internal.ir.Node;
 import jdk.nashorn.internal.ir.ObjectNode;
 import jdk.nashorn.internal.ir.PropertyNode;
-import jdk.nashorn.internal.ir.ReferenceNode;
 import jdk.nashorn.internal.ir.ReturnNode;
 import jdk.nashorn.internal.ir.RuntimeNode;
 import jdk.nashorn.internal.ir.SplitNode;
@@ -153,7 +152,7 @@
      * @param  accessNode the node
      * @return processed node, null if traversal should end, null if traversal should end
      */
-    public Node enter(final AccessNode accessNode) {
+    public Node enterAccessNode(final AccessNode accessNode) {
         return enterDefault(accessNode);
     }
 
@@ -163,7 +162,7 @@
      * @param  accessNode the node
      * @return processed node, null if traversal should end
      */
-    public Node leave(final AccessNode accessNode) {
+    public Node leaveAccessNode(final AccessNode accessNode) {
         return leaveDefault(accessNode);
     }
 
@@ -173,7 +172,7 @@
      * @param  block     the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final Block block) {
+    public Node enterBlock(final Block block) {
         return enterDefault(block);
     }
 
@@ -183,7 +182,7 @@
      * @param  block the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final Block block) {
+    public Node leaveBlock(final Block block) {
         return leaveDefault(block);
     }
 
@@ -193,7 +192,7 @@
      * @param  binaryNode  the node
      * @return processed   node
      */
-    public Node enter(final BinaryNode binaryNode) {
+    public Node enterBinaryNode(final BinaryNode binaryNode) {
         return enterDefault(binaryNode);
     }
 
@@ -203,7 +202,7 @@
      * @param  binaryNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final BinaryNode binaryNode) {
+    public Node leaveBinaryNode(final BinaryNode binaryNode) {
         return leaveDefault(binaryNode);
     }
 
@@ -213,7 +212,7 @@
      * @param  breakNode the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final BreakNode breakNode) {
+    public Node enterBreakNode(final BreakNode breakNode) {
         return enterDefault(breakNode);
     }
 
@@ -223,7 +222,7 @@
      * @param  breakNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final BreakNode breakNode) {
+    public Node leaveBreakNode(final BreakNode breakNode) {
         return leaveDefault(breakNode);
     }
 
@@ -233,7 +232,7 @@
      * @param  callNode  the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final CallNode callNode) {
+    public Node enterCallNode(final CallNode callNode) {
         return enterDefault(callNode);
     }
 
@@ -243,7 +242,7 @@
      * @param  callNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final CallNode callNode) {
+    public Node leaveCallNode(final CallNode callNode) {
         return leaveDefault(callNode);
     }
 
@@ -253,7 +252,7 @@
      * @param  caseNode  the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final CaseNode caseNode) {
+    public Node enterCaseNode(final CaseNode caseNode) {
         return enterDefault(caseNode);
     }
 
@@ -263,7 +262,7 @@
      * @param  caseNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final CaseNode caseNode) {
+    public Node leaveCaseNode(final CaseNode caseNode) {
         return leaveDefault(caseNode);
     }
 
@@ -273,7 +272,7 @@
      * @param  catchNode the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final CatchNode catchNode) {
+    public Node enterCatchNode(final CatchNode catchNode) {
         return enterDefault(catchNode);
     }
 
@@ -283,7 +282,7 @@
      * @param  catchNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final CatchNode catchNode) {
+    public Node leaveCatchNode(final CatchNode catchNode) {
         return leaveDefault(catchNode);
     }
 
@@ -293,7 +292,7 @@
      * @param  continueNode the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final ContinueNode continueNode) {
+    public Node enterContinueNode(final ContinueNode continueNode) {
         return enterDefault(continueNode);
     }
 
@@ -303,7 +302,7 @@
      * @param  continueNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final ContinueNode continueNode) {
+    public Node leaveContinueNode(final ContinueNode continueNode) {
         return leaveDefault(continueNode);
     }
 
@@ -313,7 +312,7 @@
      * @param  doWhileNode the node
      * @return processed   node
      */
-    public Node enter(final DoWhileNode doWhileNode) {
+    public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
         return enterDefault(doWhileNode);
     }
 
@@ -323,7 +322,7 @@
      * @param  doWhileNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final DoWhileNode doWhileNode) {
+    public Node leaveDoWhileNode(final DoWhileNode doWhileNode) {
         return leaveDefault(doWhileNode);
     }
 
@@ -333,7 +332,7 @@
      * @param  emptyNode   the node
      * @return processed   node
      */
-    public Node enter(final EmptyNode emptyNode) {
+    public Node enterEmptyNode(final EmptyNode emptyNode) {
         return enterDefault(emptyNode);
     }
 
@@ -343,7 +342,7 @@
      * @param  emptyNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final EmptyNode emptyNode) {
+    public Node leaveEmptyNode(final EmptyNode emptyNode) {
         return leaveDefault(emptyNode);
     }
 
@@ -353,7 +352,7 @@
      * @param  executeNode the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final ExecuteNode executeNode) {
+    public Node enterExecuteNode(final ExecuteNode executeNode) {
         return enterDefault(executeNode);
     }
 
@@ -363,7 +362,7 @@
      * @param  executeNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final ExecuteNode executeNode) {
+    public Node leaveExecuteNode(final ExecuteNode executeNode) {
         return leaveDefault(executeNode);
     }
 
@@ -373,7 +372,7 @@
      * @param  forNode   the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final ForNode forNode) {
+    public Node enterForNode(final ForNode forNode) {
         return enterDefault(forNode);
     }
 
@@ -383,7 +382,7 @@
      * @param  forNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final ForNode forNode) {
+    public Node leaveForNode(final ForNode forNode) {
         return leaveDefault(forNode);
     }
 
@@ -393,7 +392,7 @@
      * @param  functionNode the node
      * @return processed    node
      */
-    public Node enter(final FunctionNode functionNode) {
+    public Node enterFunctionNode(final FunctionNode functionNode) {
         return enterDefault(functionNode);
     }
 
@@ -403,7 +402,7 @@
      * @param  functionNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final FunctionNode functionNode) {
+    public Node leaveFunctionNode(final FunctionNode functionNode) {
         return leaveDefault(functionNode);
     }
 
@@ -413,7 +412,7 @@
      * @param  identNode the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final IdentNode identNode) {
+    public Node enterIdentNode(final IdentNode identNode) {
         return enterDefault(identNode);
     }
 
@@ -423,7 +422,7 @@
      * @param  identNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final IdentNode identNode) {
+    public Node leaveIdentNode(final IdentNode identNode) {
         return leaveDefault(identNode);
     }
 
@@ -433,7 +432,7 @@
      * @param  ifNode    the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final IfNode ifNode) {
+    public Node enterIfNode(final IfNode ifNode) {
         return enterDefault(ifNode);
     }
 
@@ -443,7 +442,7 @@
      * @param  ifNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final IfNode ifNode) {
+    public Node leaveIfNode(final IfNode ifNode) {
         return leaveDefault(ifNode);
     }
 
@@ -453,7 +452,7 @@
      * @param  indexNode  the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final IndexNode indexNode) {
+    public Node enterIndexNode(final IndexNode indexNode) {
         return enterDefault(indexNode);
     }
 
@@ -463,7 +462,7 @@
      * @param  indexNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final IndexNode indexNode) {
+    public Node leaveIndexNode(final IndexNode indexNode) {
         return leaveDefault(indexNode);
     }
 
@@ -473,7 +472,7 @@
      * @param  labelNode the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final LabelNode labelNode) {
+    public Node enterLabelNode(final LabelNode labelNode) {
         return enterDefault(labelNode);
     }
 
@@ -483,7 +482,7 @@
      * @param  labelNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final LabelNode labelNode) {
+    public Node leaveLabelNode(final LabelNode labelNode) {
         return leaveDefault(labelNode);
     }
 
@@ -493,7 +492,7 @@
      * @param  lineNumberNode the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final LineNumberNode lineNumberNode) {
+    public Node enterLineNumberNode(final LineNumberNode lineNumberNode) {
         return enterDefault(lineNumberNode);
     }
 
@@ -503,7 +502,7 @@
      * @param  lineNumberNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final LineNumberNode lineNumberNode) {
+    public Node leaveLineNumberNode(final LineNumberNode lineNumberNode) {
         return leaveDefault(lineNumberNode);
     }
 
@@ -513,8 +512,7 @@
      * @param  literalNode the node
      * @return processed   node
      */
-    @SuppressWarnings("rawtypes")
-    public Node enter(final LiteralNode literalNode) {
+    public Node enterLiteralNode(final LiteralNode<?> literalNode) {
         return enterDefault(literalNode);
     }
 
@@ -524,8 +522,7 @@
      * @param  literalNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    @SuppressWarnings("rawtypes")
-    public Node leave(final LiteralNode literalNode) {
+    public Node leaveLiteralNode(final LiteralNode<?> literalNode) {
         return leaveDefault(literalNode);
     }
 
@@ -535,7 +532,7 @@
      * @param  objectNode the node
      * @return processed  node
      */
-    public Node enter(final ObjectNode objectNode) {
+    public Node enterObjectNode(final ObjectNode objectNode) {
         return enterDefault(objectNode);
     }
 
@@ -545,7 +542,7 @@
      * @param  objectNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final ObjectNode objectNode) {
+    public Node leaveObjectNode(final ObjectNode objectNode) {
         return leaveDefault(objectNode);
     }
 
@@ -555,7 +552,7 @@
      * @param  propertyNode the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final PropertyNode propertyNode) {
+    public Node enterPropertyNode(final PropertyNode propertyNode) {
         return enterDefault(propertyNode);
     }
 
@@ -565,37 +562,17 @@
      * @param  propertyNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final PropertyNode propertyNode) {
+    public Node leavePropertyNode(final PropertyNode propertyNode) {
         return leaveDefault(propertyNode);
     }
 
     /**
-     * Callback for entering a ReferenceNode
-     *
-     * @param  referenceNode the node
-     * @return processed node, null if traversal should end
-     */
-    public Node enter(final ReferenceNode referenceNode) {
-        return enterDefault(referenceNode);
-    }
-
-    /**
-     * Callback for leaving a ReferenceNode
-     *
-     * @param  referenceNode the node
-     * @return processed node, which will replace the original one, or the original node
-     */
-    public Node leave(final ReferenceNode referenceNode) {
-        return leaveDefault(referenceNode);
-    }
-
-    /**
      * Callback for entering a ReturnNode
      *
      * @param  returnNode the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final ReturnNode returnNode) {
+    public Node enterReturnNode(final ReturnNode returnNode) {
         return enterDefault(returnNode);
     }
 
@@ -605,7 +582,7 @@
      * @param  returnNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final ReturnNode returnNode) {
+    public Node leaveReturnNode(final ReturnNode returnNode) {
         return leaveDefault(returnNode);
     }
 
@@ -615,7 +592,7 @@
      * @param  runtimeNode the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final RuntimeNode runtimeNode) {
+    public Node enterRuntimeNode(final RuntimeNode runtimeNode) {
         return enterDefault(runtimeNode);
     }
 
@@ -625,7 +602,7 @@
      * @param  runtimeNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final RuntimeNode runtimeNode) {
+    public Node leaveRuntimeNode(final RuntimeNode runtimeNode) {
         return leaveDefault(runtimeNode);
     }
 
@@ -635,7 +612,7 @@
      * @param  splitNode the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final SplitNode splitNode) {
+    public Node enterSplitNode(final SplitNode splitNode) {
         return enterDefault(splitNode);
     }
 
@@ -645,7 +622,7 @@
      * @param  splitNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final SplitNode splitNode) {
+    public Node leaveSplitNode(final SplitNode splitNode) {
         return leaveDefault(splitNode);
     }
 
@@ -655,7 +632,7 @@
      * @param  switchNode the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final SwitchNode switchNode) {
+    public Node enterSwitchNode(final SwitchNode switchNode) {
         return enterDefault(switchNode);
     }
 
@@ -665,7 +642,7 @@
      * @param  switchNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final SwitchNode switchNode) {
+    public Node leaveSwitchNode(final SwitchNode switchNode) {
         return leaveDefault(switchNode);
     }
 
@@ -675,7 +652,7 @@
      * @param  ternaryNode the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final TernaryNode ternaryNode) {
+    public Node enterTernaryNode(final TernaryNode ternaryNode) {
         return enterDefault(ternaryNode);
     }
 
@@ -685,7 +662,7 @@
      * @param  ternaryNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final TernaryNode ternaryNode) {
+    public Node leaveTernaryNode(final TernaryNode ternaryNode) {
         return leaveDefault(ternaryNode);
     }
 
@@ -695,7 +672,7 @@
      * @param  throwNode the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final ThrowNode throwNode) {
+    public Node enterThrowNode(final ThrowNode throwNode) {
         return enterDefault(throwNode);
     }
 
@@ -705,7 +682,7 @@
      * @param  throwNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final ThrowNode throwNode) {
+    public Node leaveThrowNode(final ThrowNode throwNode) {
         return leaveDefault(throwNode);
     }
 
@@ -715,7 +692,7 @@
      * @param  tryNode the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final TryNode tryNode) {
+    public Node enterTryNode(final TryNode tryNode) {
         return enterDefault(tryNode);
     }
 
@@ -725,7 +702,7 @@
      * @param  tryNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final TryNode tryNode) {
+    public Node leaveTryNode(final TryNode tryNode) {
         return leaveDefault(tryNode);
     }
 
@@ -735,7 +712,7 @@
      * @param  unaryNode the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final UnaryNode unaryNode) {
+    public Node enterUnaryNode(final UnaryNode unaryNode) {
         return enterDefault(unaryNode);
     }
 
@@ -745,7 +722,7 @@
      * @param  unaryNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final UnaryNode unaryNode) {
+    public Node leaveUnaryNode(final UnaryNode unaryNode) {
         return leaveDefault(unaryNode);
     }
 
@@ -755,7 +732,7 @@
      * @param  varNode   the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final VarNode varNode) {
+    public Node enterVarNode(final VarNode varNode) {
         return enterDefault(varNode);
     }
 
@@ -765,7 +742,7 @@
      * @param  varNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final VarNode varNode) {
+    public Node leaveVarNode(final VarNode varNode) {
         return leaveDefault(varNode);
     }
 
@@ -775,7 +752,7 @@
      * @param  whileNode the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final WhileNode whileNode) {
+    public Node enterWhileNode(final WhileNode whileNode) {
         return enterDefault(whileNode);
     }
 
@@ -785,7 +762,7 @@
      * @param  whileNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final WhileNode whileNode) {
+    public Node leaveWhileNode(final WhileNode whileNode) {
         return leaveDefault(whileNode);
     }
 
@@ -795,7 +772,7 @@
      * @param  withNode  the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final WithNode withNode) {
+    public Node enterWithNode(final WithNode withNode) {
         return enterDefault(withNode);
     }
 
@@ -805,7 +782,7 @@
      * @param  withNode  the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final WithNode withNode) {
+    public Node leaveWithNode(final WithNode withNode) {
         return leaveDefault(withNode);
     }
 
--- a/src/jdk/nashorn/internal/objects/Global.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/objects/Global.java	Mon Apr 01 21:42:31 2013 -0700
@@ -34,6 +34,9 @@
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.ref.SoftReference;
+import java.lang.reflect.Field;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -256,13 +259,29 @@
     @Property(name = "Packages", attributes = Attribute.NOT_ENUMERABLE)
     public volatile Object packages;
 
+    /** Nashorn extension: Java access - global.com */
+    @Property(attributes = Attribute.NOT_ENUMERABLE)
+    public volatile Object com;
+
+    /** Nashorn extension: Java access - global.edu */
+    @Property(attributes = Attribute.NOT_ENUMERABLE)
+    public volatile Object edu;
+
     /** Nashorn extension: Java access - global.java */
     @Property(attributes = Attribute.NOT_ENUMERABLE)
     public volatile Object java;
 
+    /** Nashorn extension: Java access - global.javafx */
+    @Property(attributes = Attribute.NOT_ENUMERABLE)
+    public volatile Object javafx;
+
     /** Nashorn extension: Java access - global.javax */
     @Property(attributes = Attribute.NOT_ENUMERABLE)
-    public Object javax;
+    public volatile Object javax;
+
+    /** Nashorn extension: Java access - global.org */
+    @Property(attributes = Attribute.NOT_ENUMERABLE)
+    public volatile Object org;
 
     /** Nashorn extension: Java access - global.javaImporter */
     @Property(name = "JavaImporter", attributes = Attribute.NOT_ENUMERABLE)
@@ -317,8 +336,12 @@
     private ScriptFunction builtinTypeError;
     private ScriptFunction builtinURIError;
     private ScriptObject   builtinPackages;
+    private ScriptObject   builtinCom;
+    private ScriptObject   builtinEdu;
     private ScriptObject   builtinJava;
+    private ScriptObject   builtinJavafx;
     private ScriptObject   builtinJavax;
+    private ScriptObject   builtinOrg;
     private ScriptObject   builtinJavaImporter;
     private ScriptObject   builtinJavaApi;
     private ScriptObject   builtinArrayBuffer;
@@ -1479,8 +1502,12 @@
     private void initJavaAccess() {
         final ScriptObject objectProto = getObjectPrototype();
         this.builtinPackages = new NativeJavaPackage("", objectProto);
+        this.builtinCom = new NativeJavaPackage("com", objectProto);
+        this.builtinEdu = new NativeJavaPackage("edu", objectProto);
         this.builtinJava = new NativeJavaPackage("java", objectProto);
+        this.builtinJavafx = new NativeJavaPackage("javafx", objectProto);
         this.builtinJavax = new NativeJavaPackage("javax", objectProto);
+        this.builtinOrg = new NativeJavaPackage("org", objectProto);
         this.builtinJavaImporter = initConstructor("JavaImporter");
         this.builtinJavaApi = initConstructor("Java");
     }
@@ -1503,8 +1530,10 @@
         addOwnProperty("echo", Attribute.NOT_ENUMERABLE, value);
 
         // Nashorn extension: global.$OPTIONS (scripting-mode-only)
-        value = context.getEnv();
-        addOwnProperty("$OPTIONS", Attribute.NOT_ENUMERABLE, value);
+        final ScriptObject options = newEmptyInstance();
+        final ScriptEnvironment scriptEnv = context.getEnv();
+        copyOptions(options, scriptEnv);
+        addOwnProperty("$OPTIONS", Attribute.NOT_ENUMERABLE, options);
 
         // Nashorn extension: global.$ENV (scripting-mode-only)
         if (System.getSecurityManager() == null) {
@@ -1523,6 +1552,22 @@
         addOwnProperty(ScriptingFunctions.EXIT_NAME, Attribute.NOT_ENUMERABLE, UNDEFINED);
     }
 
+    private static void copyOptions(final ScriptObject options, final ScriptEnvironment scriptEnv) {
+        AccessController.doPrivileged(new PrivilegedAction<Void>() {
+            @Override
+            public Void run() {
+                for (Field f : scriptEnv.getClass().getFields()) {
+                    try {
+                        options.set(f.getName(), f.get(scriptEnv), false);
+                    } catch (final IllegalArgumentException | IllegalAccessException exp) {
+                        throw new RuntimeException(exp);
+                    }
+                }
+                return null;
+            }
+        });
+    }
+
     private void initTypedArray() {
         this.builtinArrayBuffer       = initConstructor("ArrayBuffer");
         this.builtinInt8Array         = initConstructor("Int8Array");
@@ -1545,8 +1590,12 @@
         this.function          = this.builtinFunction;
         this.jsadapter         = this.builtinJSAdapter;
         this.json              = this.builtinJSON;
+        this.com               = this.builtinCom;
+        this.edu               = this.builtinEdu;
         this.java              = this.builtinJava;
+        this.javafx            = this.builtinJavafx;
         this.javax             = this.builtinJavax;
+        this.org               = this.builtinOrg;
         this.javaImporter      = this.builtinJavaImporter;
         this.javaApi           = this.builtinJavaApi;
         this.math              = this.builtinMath;
--- a/src/jdk/nashorn/internal/objects/NativeArray.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/objects/NativeArray.java	Mon Apr 01 21:42:31 2013 -0700
@@ -605,7 +605,7 @@
             final boolean strict    = sobj.isStrictContext();
 
             if (bulkable(sobj)) {
-                return ((NativeArray)sobj).getArray().pop();
+                return sobj.getArray().pop();
             }
 
             final long len = JSType.toUint32(sobj.getLength());
@@ -725,7 +725,7 @@
             first = sobj.get(0);
 
             if (bulkable(sobj)) {
-                ((NativeArray) sobj).getArray().shiftLeft(1);
+                sobj.getArray().shiftLeft(1);
             } else {
                 for (long k = 1; k < len; k++) {
                     sobj.set(k - 1, sobj.get(k), strict);
--- a/src/jdk/nashorn/internal/objects/NativeDebug.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/objects/NativeDebug.java	Mon Apr 01 21:42:31 2013 -0700
@@ -66,7 +66,7 @@
     public static Object getContext(final Object self) {
         final SecurityManager sm = System.getSecurityManager();
         if (sm != null) {
-            sm.checkPermission(new RuntimePermission("getNashornContext"));
+            sm.checkPermission(new RuntimePermission("nashorn.getContext"));
         }
         return Global.getThisContext();
     }
@@ -162,21 +162,6 @@
     }
 
     /**
-     * Nashorn extension: get invocation handle from {@link ScriptFunction}
-     *
-     * @param self self reference
-     * @param obj script function
-     * @return the invocation handle for the given ScriptFunction
-     */
-    @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
-    public static Object methodHandle(final Object self, final Object obj) {
-        if (obj instanceof ScriptFunction) {
-            return ((ScriptFunction)obj).getInvokeHandle();
-        }
-        return UNDEFINED;
-    }
-
-    /**
      * Check object identity comparison regardless of type
      *
      * @param self self reference
--- a/src/jdk/nashorn/internal/objects/NativeError.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/objects/NativeError.java	Mon Apr 01 21:42:31 2013 -0700
@@ -317,7 +317,7 @@
             return name;
         }
         // Step 10 : return name + ": " + msg
-        return (String)name + ": " + (String)msg;
+        return name + ": " + msg;
     }
 
     private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
--- a/src/jdk/nashorn/internal/objects/NativeJava.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/objects/NativeJava.java	Mon Apr 01 21:42:31 2013 -0700
@@ -223,6 +223,23 @@
     }
 
     /**
+     * Returns name of a java type {@link StaticClass}.
+     * @param self not used
+     * @param type the type whose name is returned
+     * @return name of the given type
+     */
+    @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
+    public static Object typeName(final Object self, final Object type) {
+        if (type instanceof StaticClass) {
+            return ((StaticClass)type).getRepresentedClass().getName();
+        } else if (type instanceof Class) {
+            return ((Class<?>)type).getName();
+        } else {
+            return UNDEFINED;
+        }
+    }
+
+    /**
      * Given a JavaScript array and a Java type, returns a Java array with the same initial contents, and with the
      * specified component type. Example:
      * <pre>
--- a/src/jdk/nashorn/internal/objects/NativeString.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/objects/NativeString.java	Mon Apr 01 21:42:31 2013 -0700
@@ -122,6 +122,19 @@
         return value.length();
     }
 
+    // This is to support length as method call as well.
+    @Override
+    protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
+        final String name = desc.getNameToken(2);
+
+        // if str.length(), then let the bean linker handle it
+        if ("length".equals(name) && "getMethod".equals(operator)) {
+            return null;
+        }
+
+        return super.findGetMethod(desc, request, operator);
+    }
+
     // This is to provide array-like access to string characters without creating a NativeString wrapper.
     @Override
     protected GuardedInvocation findGetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) {
--- a/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java	Thu Mar 28 10:55:17 2013 -0700
+++ b/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java	Mon Apr 01 21:42:31 2013 -0700
@@ -31,6 +31,7 @@
 import jdk.nashorn.internal.runtime.GlobalFunctions;
 import jdk.nashorn.internal.runtime.Property;
 import jdk.nashorn.internal.runtime.PropertyMap;
+import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
 import jdk.nashorn.internal.runtime.ScriptFunction;
 import jdk.nashorn.internal.runtime.ScriptFunctionData;
 import jdk.nashorn.internal.runtime.ScriptObject;
@@ -86,8 +87,8 @@
      * @param builtin is this a built-in function
      * @param isConstructor can the function be used as a constructor (most can; some built-ins are restricted).
      */
-    ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final MethodHandle[] specs, final boolean strict, final boolean builtin, final boolean isConstructor) {
-        super(name, methodHandle, getMap(strict), scope, specs, strict, builtin, isConstructor);
+    ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final MethodHandle[] specs, final boolean isStrict, final boolean isBuiltin, final boolean isConstructor) {
+        super(name, methodHandle, getMap(isStrict), scope, specs, isStrict, isBuiltin, isConstructor);
         init();
     }
 
@@ -95,14 +96,10 @@
      * Constructor called by (compiler) generated code for {@link ScriptObject}s.
      *
      * @param data static function data
-     * @param methodHandle handle for invocation
      * @param scope scope object
-     * @param allocator instance constructor for function
      */
-    public ScriptFunctionImpl(final MethodHandle methodHandle, final ScriptFunctionData data, final ScriptObject scope, final MethodHandle allocator) {
+    public ScriptFunctionImpl(final R