changeset 817:26a5fdb90de2

8035820: Optimistic recompilation Reviewed-by: hannesw, jlaskey, sundar Contributed-by: attila.szegedi@oracle.com, marcus.lagergren@oracle.com
author attila
date Wed, 26 Feb 2014 13:17:57 +0100
parents feceb45debac
children e008b751c93f
files .hgignore bin/rundiff.sh bin/runnormal.sh bin/runnormaldual.sh bin/runopt.sh bin/runoptdual.sh bin/runoptdualcatch.sh buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MethodGenerator.java make/build.xml make/nbproject/ide-targets.xml make/project.properties src/jdk/internal/dynalink/DynamicLinker.java src/jdk/internal/dynalink/DynamicLinkerFactory.java src/jdk/internal/dynalink/GuardedInvocationFilter.java src/jdk/internal/dynalink/beans/AbstractJavaLinker.java src/jdk/internal/dynalink/beans/BeanLinker.java src/jdk/internal/dynalink/beans/OverloadedDynamicMethod.java src/jdk/internal/dynalink/beans/OverloadedMethod.java src/jdk/internal/dynalink/beans/SingleDynamicMethod.java src/jdk/internal/dynalink/linker/GuardedInvocation.java src/jdk/internal/dynalink/linker/GuardingDynamicLinker.java src/jdk/internal/dynalink/linker/LinkRequest.java src/jdk/internal/dynalink/linker/LinkerServices.java src/jdk/internal/dynalink/support/CatchExceptionCombinator.java src/jdk/internal/dynalink/support/DefaultPrelinkFilter.java src/jdk/internal/dynalink/support/LinkRequestImpl.java src/jdk/internal/dynalink/support/LinkerServicesImpl.java src/jdk/internal/dynalink/support/RuntimeContextLinkRequestImpl.java src/jdk/internal/dynalink/support/TypeUtilities.java src/jdk/nashorn/api/scripting/JSObject.java src/jdk/nashorn/api/scripting/NashornException.java src/jdk/nashorn/api/scripting/NashornScriptEngine.java src/jdk/nashorn/api/scripting/ScriptObjectMirror.java src/jdk/nashorn/internal/IntDeque.java src/jdk/nashorn/internal/codegen/Attr.java src/jdk/nashorn/internal/codegen/BranchOptimizer.java src/jdk/nashorn/internal/codegen/ClassEmitter.java src/jdk/nashorn/internal/codegen/CodeGenerator.java src/jdk/nashorn/internal/codegen/CodeGeneratorLexicalContext.java src/jdk/nashorn/internal/codegen/CompilationEnvironment.java src/jdk/nashorn/internal/codegen/CompilationPhase.java src/jdk/nashorn/internal/codegen/CompileUnit.java src/jdk/nashorn/internal/codegen/Compiler.java src/jdk/nashorn/internal/codegen/CompilerConstants.java src/jdk/nashorn/internal/codegen/Condition.java src/jdk/nashorn/internal/codegen/ConstantData.java src/jdk/nashorn/internal/codegen/DumpBytecode.java src/jdk/nashorn/internal/codegen/FieldObjectCreator.java src/jdk/nashorn/internal/codegen/FinalizeTypes.java src/jdk/nashorn/internal/codegen/FoldConstants.java src/jdk/nashorn/internal/codegen/FunctionSignature.java src/jdk/nashorn/internal/codegen/Label.java src/jdk/nashorn/internal/codegen/LocalStateRestorationInfo.java src/jdk/nashorn/internal/codegen/Lower.java src/jdk/nashorn/internal/codegen/MapCreator.java src/jdk/nashorn/internal/codegen/MapTuple.java src/jdk/nashorn/internal/codegen/MethodEmitter.java src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java src/jdk/nashorn/internal/codegen/ObjectCreator.java src/jdk/nashorn/internal/codegen/ParamTypeMap.java src/jdk/nashorn/internal/codegen/ProgramPoints.java src/jdk/nashorn/internal/codegen/RangeAnalyzer.java src/jdk/nashorn/internal/codegen/RuntimeCallSite.java src/jdk/nashorn/internal/codegen/SharedScopeCall.java src/jdk/nashorn/internal/codegen/SpillObjectCreator.java src/jdk/nashorn/internal/codegen/Splitter.java src/jdk/nashorn/internal/codegen/WeighNodes.java src/jdk/nashorn/internal/codegen/types/BooleanType.java src/jdk/nashorn/internal/codegen/types/BytecodeNumericOps.java src/jdk/nashorn/internal/codegen/types/BytecodeOps.java src/jdk/nashorn/internal/codegen/types/IntType.java src/jdk/nashorn/internal/codegen/types/LongType.java src/jdk/nashorn/internal/codegen/types/NumberType.java src/jdk/nashorn/internal/codegen/types/ObjectType.java src/jdk/nashorn/internal/codegen/types/Type.java src/jdk/nashorn/internal/ir/AccessNode.java src/jdk/nashorn/internal/ir/BaseNode.java src/jdk/nashorn/internal/ir/BinaryNode.java src/jdk/nashorn/internal/ir/Block.java src/jdk/nashorn/internal/ir/BlockStatement.java src/jdk/nashorn/internal/ir/CallNode.java src/jdk/nashorn/internal/ir/CatchNode.java src/jdk/nashorn/internal/ir/Expression.java src/jdk/nashorn/internal/ir/FunctionCall.java src/jdk/nashorn/internal/ir/FunctionNode.java src/jdk/nashorn/internal/ir/IdentNode.java src/jdk/nashorn/internal/ir/IndexNode.java src/jdk/nashorn/internal/ir/LexicalContext.java src/jdk/nashorn/internal/ir/LiteralNode.java src/jdk/nashorn/internal/ir/Node.java src/jdk/nashorn/internal/ir/Optimistic.java src/jdk/nashorn/internal/ir/OptimisticLexicalContext.java src/jdk/nashorn/internal/ir/RuntimeNode.java src/jdk/nashorn/internal/ir/Symbol.java src/jdk/nashorn/internal/ir/UnaryNode.java src/jdk/nashorn/internal/ir/VarNode.java src/jdk/nashorn/internal/ir/debug/ASTWriter.java src/jdk/nashorn/internal/ir/debug/JSONWriter.java src/jdk/nashorn/internal/ir/debug/NashornClassReader.java src/jdk/nashorn/internal/ir/debug/NashornTextifier.java src/jdk/nashorn/internal/ir/debug/PrintVisitor.java src/jdk/nashorn/internal/lookup/Lookup.java src/jdk/nashorn/internal/lookup/MethodHandleFactory.java src/jdk/nashorn/internal/lookup/MethodHandleFunctionality.java src/jdk/nashorn/internal/objects/AccessorPropertyDescriptor.java src/jdk/nashorn/internal/objects/ArrayBufferView.java src/jdk/nashorn/internal/objects/DataPropertyDescriptor.java src/jdk/nashorn/internal/objects/GenericPropertyDescriptor.java src/jdk/nashorn/internal/objects/Global.java src/jdk/nashorn/internal/objects/NativeArray.java src/jdk/nashorn/internal/objects/NativeDebug.java src/jdk/nashorn/internal/objects/NativeFunction.java src/jdk/nashorn/internal/objects/NativeJSAdapter.java src/jdk/nashorn/internal/objects/NativeJavaImporter.java src/jdk/nashorn/internal/objects/NativeNumber.java src/jdk/nashorn/internal/objects/NativeObject.java src/jdk/nashorn/internal/objects/NativeRegExp.java src/jdk/nashorn/internal/objects/NativeStrictArguments.java src/jdk/nashorn/internal/objects/NativeString.java src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java src/jdk/nashorn/internal/objects/annotations/Optimistic.java src/jdk/nashorn/internal/objects/annotations/Property.java src/jdk/nashorn/internal/parser/AbstractParser.java src/jdk/nashorn/internal/parser/JSONParser.java src/jdk/nashorn/internal/parser/Lexer.java src/jdk/nashorn/internal/parser/Parser.java src/jdk/nashorn/internal/runtime/AccessorProperty.java src/jdk/nashorn/internal/runtime/CompiledFunction.java src/jdk/nashorn/internal/runtime/CompiledFunctions.java src/jdk/nashorn/internal/runtime/Context.java src/jdk/nashorn/internal/runtime/Debug.java src/jdk/nashorn/internal/runtime/DebugLogger.java src/jdk/nashorn/internal/runtime/DebuggerSupport.java src/jdk/nashorn/internal/runtime/DefaultPropertyAccess.java src/jdk/nashorn/internal/runtime/ECMAErrors.java src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java src/jdk/nashorn/internal/runtime/FindProperty.java src/jdk/nashorn/internal/runtime/FunctionScope.java src/jdk/nashorn/internal/runtime/JSONFunctions.java src/jdk/nashorn/internal/runtime/JSType.java src/jdk/nashorn/internal/runtime/NativeJavaPackage.java src/jdk/nashorn/internal/runtime/OptimisticReturnFilters.java src/jdk/nashorn/internal/runtime/Property.java src/jdk/nashorn/internal/runtime/PropertyAccess.java src/jdk/nashorn/internal/runtime/PropertyHashMap.java src/jdk/nashorn/internal/runtime/PropertyMap.java src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java src/jdk/nashorn/internal/runtime/RewriteException.java src/jdk/nashorn/internal/runtime/ScriptEnvironment.java src/jdk/nashorn/internal/runtime/ScriptFunction.java src/jdk/nashorn/internal/runtime/ScriptFunctionData.java src/jdk/nashorn/internal/runtime/ScriptObject.java src/jdk/nashorn/internal/runtime/ScriptRuntime.java src/jdk/nashorn/internal/runtime/SetMethodCreator.java src/jdk/nashorn/internal/runtime/SpillProperty.java src/jdk/nashorn/internal/runtime/Undefined.java src/jdk/nashorn/internal/runtime/UnwarrantedOptimismException.java src/jdk/nashorn/internal/runtime/UserAccessorProperty.java src/jdk/nashorn/internal/runtime/WithObject.java src/jdk/nashorn/internal/runtime/arrays/ArrayData.java src/jdk/nashorn/internal/runtime/arrays/ArrayFilter.java src/jdk/nashorn/internal/runtime/arrays/ContinuousArray.java src/jdk/nashorn/internal/runtime/arrays/IntArrayData.java src/jdk/nashorn/internal/runtime/arrays/LongArrayData.java src/jdk/nashorn/internal/runtime/arrays/NoTypeArrayData.java src/jdk/nashorn/internal/runtime/arrays/NumberArrayData.java src/jdk/nashorn/internal/runtime/arrays/ObjectArrayData.java src/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java src/jdk/nashorn/internal/runtime/arrays/UndefinedArrayFilter.java src/jdk/nashorn/internal/runtime/linker/Bootstrap.java src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java src/jdk/nashorn/internal/runtime/linker/JavaSuperAdapter.java src/jdk/nashorn/internal/runtime/linker/JavaSuperAdapterLinker.java src/jdk/nashorn/internal/runtime/linker/LinkerCallSite.java src/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java src/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java src/jdk/nashorn/internal/runtime/linker/NashornCallSiteDescriptor.java src/jdk/nashorn/internal/runtime/linker/NashornGuards.java src/jdk/nashorn/internal/runtime/linker/NashornLinker.java src/jdk/nashorn/internal/runtime/linker/NashornPrimitiveLinker.java src/jdk/nashorn/internal/runtime/resources/Messages.properties src/jdk/nashorn/internal/runtime/resources/Options.properties src/jdk/nashorn/internal/scripts/JO.java src/jdk/nashorn/tools/Shell.java test/script/assert.js test/script/basic/JDK-8025515.js test/script/basic/JDK-8029384.js test/script/basic/JDK-8029384.js.EXPECTED test/script/basic/JDK-8029467.js test/script/basic/JDK-8029667.js test/script/basic/exprclosure.js.EXPECTED test/script/basic/hideLocationProperties.js test/script/basic/hideLocationProperties.js.EXPECTED test/script/basic/relinkIndexGetter.js test/script/basic/relinkIndexGetter.js.EXPECTED test/script/basic/run-octane.js test/script/basic/runsunspider-lazy.js test/script/basic/runsunspider.js test/script/basic/typedarrays2.js test/script/basic/typedarrays2.js.EXPECTED test/script/currently-failing/property_delete.js test/script/maptests/constructor.js test/script/maptests/maputil.js test/script/maptests/object_literals.js test/script/maptests/point.js test/script/maptests/property_add.js test/script/maptests/property_delete.js test/script/trusted/JDK-8006529.js test/src/jdk/nashorn/api/javaaccess/NumberAccessTest.java test/src/jdk/nashorn/api/javaaccess/NumberBoxingTest.java test/src/jdk/nashorn/api/javaaccess/SharedObject.java test/src/jdk/nashorn/internal/codegen/CompilerTest.java test/src/jdk/nashorn/internal/parser/ParserTest.java test/src/jdk/nashorn/internal/test/framework/ScriptRunnable.java
diffstat 215 files changed, 15823 insertions(+), 5046 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Tue Feb 25 18:56:10 2014 +0530
+++ b/.hgignore	Wed Feb 26 13:17:57 2014 +0100
@@ -13,6 +13,8 @@
 *.clazz
 *.log
 *.orig
+*.rej
+*~
 genfiles.properties
 hotspot.log
 .DS_Store*
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/rundiff.sh	Wed Feb 26 13:17:57 2014 +0100
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+# do two runs of a script, one optimistic and one pessimistic, expect identical outputs
+# if not, display and error message and a diff
+
+which opendiff >/dev/null
+RES=$?
+if [ $RES = 0 ]; then
+    DIFFTOOL=opendiff
+else
+    DIFFTOOL=diff
+fi
+
+OPTIMISTIC=out_optimistic
+PESSIMISTIC=out_pessimistic
+$JAVA_HOME/bin/java -ea -jar ../dist/nashorn.jar ${@} >$PESSIMISTIC
+$JAVA_HOME/bin/java -ea -Dnashorn.optimistic -jar ../dist/nashorn.jar ${@} >$OPTIMISTIC
+
+if ! diff -q $PESSIMISTIC $OPTIMISTIC >/dev/null ; then
+    echo "Failure! Results are different"
+    echo ""
+    $DIFFTOOL $PESSIMISTIC $OPTIMISTIC
+else
+    echo "OK - Results are identical"
+fi
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/runnormal.sh	Wed Feb 26 13:17:57 2014 +0100
@@ -0,0 +1,3 @@
+#!/bin/sh
+FILENAME="./pessimistic_$(date|sed "s/ /_/g"|sed "s/:/_/g").jfr"
+$JAVA_HOME/bin/java -ea -Xms2G -Xmx2G -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:FlightRecorderOptions=defaultrecording=true,disk=true,dumponexit=true,dumponexitpath=$FILENAME,stackdepth=128 -Djava.ext.dirs=dist jdk.nashorn.tools.Shell ${@}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/runnormaldual.sh	Wed Feb 26 13:17:57 2014 +0100
@@ -0,0 +1,3 @@
+#!/bin/sh
+FILENAME="./optimistic_dual_$(date|sed "s/ /_/g"|sed "s/:/_/g").jfr"
+$JAVA_HOME/bin/java -ea -Dnashorn.fields.dual -Xms2G -Xmx2G -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:FlightRecorderOptions=defaultrecording=true,disk=true,dumponexit=true,dumponexitpath=$FILENAME,stackdepth=128 -XX:TypeProfileLevel=222 -XX:+UnlockExperimentalVMOptions -XX:+UseTypeSpeculation -XX:+UseMathExactIntrinsics ${@}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/runopt.sh	Wed Feb 26 13:17:57 2014 +0100
@@ -0,0 +1,3 @@
+#!/bin/sh
+FILENAME="./optimistic_$(date|sed "s/ /_/g"|sed "s/:/_/g").jfr"
+$JAVA_HOME/bin/java -ea -Dnashorn.optimistic -Dnashorn.fastrewrite -Xms2G -Xmx2G -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:FlightRecorderOptions=defaultrecording=true,disk=true,dumponexit=true,dumponexitpath=$FILENAME,stackdepth=128 -XX:TypeProfileLevel=222 -XX:+UnlockExperimentalVMOptions -XX:+UseTypeSpeculation -XX:-UseMathExactIntrinsics -Xbootclasspath/p:dist/nashorn.jar jdk.nashorn.tools.Shell ${@} 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/runoptdual.sh	Wed Feb 26 13:17:57 2014 +0100
@@ -0,0 +1,3 @@
+#!/bin/sh
+FILENAME="./optimistic_dual_$(date|sed "s/ /_/g"|sed "s/:/_/g").jfr"
+$JAVA_HOME/bin/java -ea -Dnashorn.fields.dual -Dnashorn.optimistic -Xms2G -Xmx2G -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:FlightRecorderOptions=defaultrecording=true,disk=true,dumponexit=true,dumponexitpath=$FILENAME,stackdepth=128 -XX:TypeProfileLevel=222 -XX:+UnlockExperimentalVMOptions -XX:+UseTypeSpeculation -XX:-UseMathExactIntrinsics ${@}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/runoptdualcatch.sh	Wed Feb 26 13:17:57 2014 +0100
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+#FLAGS="-Djava.lang.invoke.MethodHandle.COMPILE_THRESHOLD=3 -Djava.lang.invoke.MethodHandle.DUMP_CLASS_FILES=true -Djava.lang.invoke.MethodHandle.TRACE_METHOD_LINKAGE=true -Djava.lang.invoke.MethodHandle.TRACE_INTERPRETER=true"
+
+FILENAME="./optimistic_dual_catch$(date|sed "s/ /_/g"|sed "s/:/_/g").jfr"
+
+$JAVA_HOME/bin/java \
+-ea \
+-esa \
+$FLAGS \
+-Dnashorn.fastrewrite \
+-Dnashorn.optimistic \
+-Xbootclasspath/p:/Users/marcus/src/tip/dist/nashorn.jar \
+-Xms2G -Xmx2G \
+-XX:+UnlockCommercialFeatures \
+-XX:+FlightRecorder \
+-XX:FlightRecorderOptions=defaultrecording=true,disk=true,dumponexit=true,dumponexitpath=$FILENAME,stackdepth=1024 \
+-XX:TypeProfileLevel=222 \
+-XX:+UnlockExperimentalVMOptions \
+-XX:+UseTypeSpeculation \
+-XX:+UseMathExactIntrinsics \
+-XX:+UnlockDiagnosticVMOptions \
+-cp $CLASSPATH:../build/test/classes/ \
+jdk.nashorn.tools.Shell ${@}
+
--- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MethodGenerator.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MethodGenerator.java	Wed Feb 26 13:17:57 2014 +0100
@@ -413,7 +413,8 @@
         super.visitMethodInsn(INVOKEVIRTUAL,
                     "java/io/PrintStream",
                     "println",
-                    "(Ljava/lang/String;)V", false);
+                    "(Ljava/lang/String;)V",
+                    false);
     }
 
     // print the object on the top of the stack
@@ -426,6 +427,7 @@
         super.visitMethodInsn(INVOKEVIRTUAL,
                     "java/io/PrintStream",
                     "println",
-                    "(Ljava/lang/Object;)V", false);
+                    "(Ljava/lang/Object;)V",
+                    false);
     }
 }
--- a/make/build.xml	Tue Feb 25 18:56:10 2014 +0530
+++ b/make/build.xml	Wed Feb 26 13:17:57 2014 +0100
@@ -365,18 +365,6 @@
     </testng>
   </target>
 
-  <target name="test-basicparallel" depends="jar, check-testng, check-external-tests, compile-test, generate-policy-file">
-      <!-- use just build.test.classes.dir to avoid referring to TestNG -->
-      <java classname="${parallel.test.runner}" dir="${basedir}" classpath="${build.test.classes.dir}" failonerror="true" fork="true">
-      <jvmarg line="${ext.class.path}"/>
-      <jvmarg line="${run.test.jvmargs} -Xmx${run.test.xmx} ${run.test.jvmsecurityargs}"/>
-      <syspropertyset>
-          <propertyref prefix="test-sys-prop."/>
-          <mapper type="glob" from="test-sys-prop.*" to="*"/>
-      </syspropertyset>
-      </java>
-  </target>
-
   <target name="check-jemmy.jfx.testng" unless="jemmy.jfx.testng.available">
     <echo message="WARNING: Jemmy or JavaFX or TestNG not available, will not run tests. Please copy testng.jar, JemmyCore.jar, JemmyFX.jar, JemmyAWTInput.jar under test${file.separator}lib directory. And make sure you have jfxrt.jar in ${java.home}${file.separator}lib${file.separator}ext dir."/>
   </target>
@@ -467,6 +455,28 @@
     </java>
   </target>
 
+<!--	    classpath="${build.test.classes.dir}"-->
+
+  <target name="testparallel" depends="test-parallel"/>
+
+  <target name="test-parallel" depends="jar, check-testng, check-external-tests, compile-test, generate-policy-file" if="testng.available">
+      <!-- use just build.test.classes.dir to avoid referring to TestNG -->
+      <java classname="${parallel.test.runner}" dir="${basedir}"
+	    failonerror="true" 
+	    fork="true">
+      <jvmarg line="${ext.class.path}"/>
+      <jvmarg line="${run.test.jvmargs} -Xmx${run.test.xmx} ${run.test.jvmsecurityargs}"/>
+      <classpath>
+          <pathelement path="${run.test.classpath}"/>
+	  <pathelement path="${build.test.classes.dir}"/>
+      </classpath>
+      <syspropertyset>
+          <propertyref prefix="test-sys-prop."/>
+          <mapper type="glob" from="test-sys-prop.*" to="*"/>
+      </syspropertyset>
+      </java>
+  </target>
+
   <target name="all" depends="test, docs"
       description="Build, test and generate docs for nashorn"/>
 
--- a/make/nbproject/ide-targets.xml	Tue Feb 25 18:56:10 2014 +0530
+++ b/make/nbproject/ide-targets.xml	Wed Feb 26 13:17:57 2014 +0100
@@ -31,9 +31,10 @@
             <classpath path="${run.test.classpath}"/>
         </nbjpdastart>
         <java classname="jdk.nashorn.tools.Shell" classpath="${run.test.classpath}" dir="samples" fork="true">
+	    <jvmarg line="-Dnashorn.optimistic"/>
             <jvmarg line="${ext.class.path}"/> 
             <jvmarg line="${run.test.jvmargs}"/>
-            <arg value="test.js"/>
+            <arg value="../make/str.js"/>
             <jvmarg value="-Xdebug"/>
             <jvmarg value="-Xrunjdwp:transport=dt_socket,address=${jpda.address}"/>
         </java>
--- a/make/project.properties	Tue Feb 25 18:56:10 2014 +0530
+++ b/make/project.properties	Wed Feb 26 13:17:57 2014 +0100
@@ -175,7 +175,7 @@
     mandreel.js
 
 # test root for sunspider
-sunspider-test-sys-prop.test.js.roots=${test.external.dir}/sunspider/tests/sunspider-1.0/
+sunspider-test-sys-prop.test.js.roots=${test.external.dir}/sunspider/tests/sunspider-1.0.2/
 
 # framework root for sunspider
 sunspider-test-sys-prop.test.js.framework=${test.basic.dir}/runsunspider.js
@@ -258,20 +258,29 @@
 src.dir=src
 test.src.dir=test/src
 
-# -Xmx is used for all tests, -Xms only for octane benchmark
 run.test.xmx=3G
 run.test.xms=2G
 
+#uncomment to enable flight recording - crank up stack trace for lambda forms
+#jfr.args=-XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:FlightRecorderOptions=defaultrecording=true,disk=true,dumponexit=true,dumponexitpath="test_suite.jfr",stackdepth=1024 \
+jfr.args=
+
 run.test.user.language=tr
 run.test.user.country=TR
 
-run.test.jvmargs.common=-server -XX:+TieredCompilation -Dfile.encoding=UTF-8 -Duser.language=${run.test.user.language} -Duser.country=${run.test.user.country} -XX:+HeapDumpOnOutOfMemoryError
+run.test.jvmargs.common=\
+  -server \
+  -Dfile.encoding=UTF-8 \
+  -Duser.language=${run.test.user.language} \
+  -Duser.country=${run.test.user.country} \
+  ${jfr.args} \
+  -XX:+HeapDumpOnOutOfMemoryError
 
 #-XX:-UseCompressedKlassPointers -XX:+PrintHeapAtGC -XX:ClassMetaspaceSize=300M
 # -XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintNMethods
 
 # turn on assertions for tests
-run.test.jvmargs.main=${run.test.jvmargs.common} -ea
+run.test.jvmargs.main=${run.test.jvmargs.common} -ea -Dnashorn.optimistic -Dnashorn.lazy
 
 #-XX:-UseCompressedKlassPointers -XX:+PrintHeapAtGC -XX:ClassMetaspaceSize=300M  
 run.test.jvmargs.octane.main=${run.test.jvmargs.common}
--- a/src/jdk/internal/dynalink/DynamicLinker.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/src/jdk/internal/dynalink/DynamicLinker.java	Wed Feb 26 13:17:57 2014 +0100
@@ -140,7 +140,6 @@
  * @author Attila Szegedi
  */
 public class DynamicLinker {
-
     private static final String CLASS_NAME = DynamicLinker.class.getName();
     private static final String RELINK_METHOD_NAME = "relink";
 
@@ -148,6 +147,7 @@
     private static final String INITIAL_LINK_METHOD_NAME = "linkCallSite";
 
     private final LinkerServices linkerServices;
+    private final GuardedInvocationFilter prelinkFilter;
     private final int runtimeContextArgCount;
     private final boolean syncOnRelink;
     private final int unstableRelinkThreshold;
@@ -156,18 +156,20 @@
      * Creates a new dynamic linker.
      *
      * @param linkerServices the linkerServices used by the linker, created by the factory.
+     * @param prelinkFilter see {@link DynamicLinkerFactory#setPrelinkFilter(GuardedInvocationFilter)}
      * @param runtimeContextArgCount see {@link DynamicLinkerFactory#setRuntimeContextArgCount(int)}
      */
-    DynamicLinker(LinkerServices linkerServices, int runtimeContextArgCount, boolean syncOnRelink,
-            int unstableRelinkThreshold) {
+    DynamicLinker(LinkerServices linkerServices, GuardedInvocationFilter prelinkFilter, int runtimeContextArgCount,
+            boolean syncOnRelink, int unstableRelinkThreshold) {
         if(runtimeContextArgCount < 0) {
             throw new IllegalArgumentException("runtimeContextArgCount < 0");
         }
         if(unstableRelinkThreshold < 0) {
             throw new IllegalArgumentException("unstableRelinkThreshold < 0");
         }
+        this.linkerServices = linkerServices;
+        this.prelinkFilter = prelinkFilter;
         this.runtimeContextArgCount = runtimeContextArgCount;
-        this.linkerServices = linkerServices;
         this.syncOnRelink = syncOnRelink;
         this.unstableRelinkThreshold = unstableRelinkThreshold;
     }
@@ -224,11 +226,10 @@
         final boolean unstableDetectionEnabled = unstableRelinkThreshold > 0;
         final boolean callSiteUnstable = unstableDetectionEnabled && relinkCount >= unstableRelinkThreshold;
         final LinkRequest linkRequest =
-                runtimeContextArgCount == 0 ? new LinkRequestImpl(callSiteDescriptor, callSiteUnstable, arguments)
-                        : new RuntimeContextLinkRequestImpl(callSiteDescriptor, callSiteUnstable, arguments,
-                                runtimeContextArgCount);
+                runtimeContextArgCount == 0 ?
+                        new LinkRequestImpl(callSiteDescriptor, callSite, callSiteUnstable, arguments) :
+                        new RuntimeContextLinkRequestImpl(callSiteDescriptor, callSite, callSiteUnstable, arguments, runtimeContextArgCount);
 
-        // Find a suitable method handle with a guard
         GuardedInvocation guardedInvocation = linkerServices.getGuardedInvocation(linkRequest);
 
         // None found - throw an exception
@@ -248,6 +249,11 @@
             }
         }
 
+        // Make sure we filter the invocation before linking it into the call site. This is typically used to match the
+        // return type of the invocation to the call site.
+        guardedInvocation = prelinkFilter.filter(guardedInvocation, linkRequest, linkerServices);
+        guardedInvocation.getClass(); // null pointer check
+
         int newRelinkCount = relinkCount;
         // Note that the short-circuited "&&" evaluation below ensures we'll increment the relinkCount until
         // threshold + 1 but not beyond that. Threshold + 1 is treated as a special value to signal that resetAndRelink
--- a/src/jdk/internal/dynalink/DynamicLinkerFactory.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/src/jdk/internal/dynalink/DynamicLinkerFactory.java	Wed Feb 26 13:17:57 2014 +0100
@@ -102,14 +102,15 @@
 import jdk.internal.dynalink.support.ClassLoaderGetterContextProvider;
 import jdk.internal.dynalink.support.CompositeGuardingDynamicLinker;
 import jdk.internal.dynalink.support.CompositeTypeBasedGuardingDynamicLinker;
+import jdk.internal.dynalink.support.DefaultPrelinkFilter;
 import jdk.internal.dynalink.support.LinkerServicesImpl;
 import jdk.internal.dynalink.support.TypeConverterFactory;
 
 /**
  * A factory class for creating {@link DynamicLinker}s. The most usual dynamic linker is a linker that is a composition
  * of all {@link GuardingDynamicLinker}s known and pre-created by the caller as well as any
- * {@link AutoDiscovery automatically discovered} guarding linkers and the standard fallback {@link BeansLinker}. See
- * {@link DynamicLinker} documentation for tips on how to use this class.
+ * {@link AutoDiscovery automatically discovered} guarding linkers and the standard fallback {@link BeansLinker} and a
+ * {@link DefaultPrelinkFilter}. See {@link DynamicLinker} documentation for tips on how to use this class.
  *
  * @author Attila Szegedi
  */
@@ -128,6 +129,7 @@
     private int runtimeContextArgCount = 0;
     private boolean syncOnRelink = false;
     private int unstableRelinkThreshold = DEFAULT_UNSTABLE_RELINK_THRESHOLD;
+    private GuardedInvocationFilter prelinkFilter;
 
     /**
      * Sets the class loader for automatic discovery of available linkers. If not set explicitly, then the thread
@@ -246,7 +248,19 @@
     }
 
     /**
-     * Creates a new dynamic linker consisting of all the prioritized, autodiscovered, and fallback linkers.
+     * Set the pre-link filter. This is a {@link GuardedInvocationFilter} that will get the final chance to modify the
+     * guarded invocation after it has been created by a component linker and before the dynamic linker links it into
+     * the call site. It is normally used to adapt the return value type of the invocation to the type of the call site.
+     * When not set explicitly, {@link DefaultPrelinkFilter} will be used.
+     * @param prelinkFilter the pre-link filter for the dynamic linker.
+     */
+    public void setPrelinkFilter(GuardedInvocationFilter prelinkFilter) {
+        this.prelinkFilter = prelinkFilter;
+    }
+
+    /**
+     * Creates a new dynamic linker consisting of all the prioritized, autodiscovered, and fallback linkers as well as
+     * the pre-link filter.
      *
      * @return the new dynamic Linker
      */
@@ -306,8 +320,12 @@
             }
         }
 
+        if(prelinkFilter == null) {
+            prelinkFilter = new DefaultPrelinkFilter();
+        }
+
         return new DynamicLinker(new LinkerServicesImpl(new TypeConverterFactory(typeConverters), composite),
-                runtimeContextArgCount, syncOnRelink, unstableRelinkThreshold);
+                prelinkFilter, runtimeContextArgCount, syncOnRelink, unstableRelinkThreshold);
     }
 
     private static ClassLoader getThreadContextClassLoader() {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/internal/dynalink/GuardedInvocationFilter.java	Wed Feb 26 13:17:57 2014 +0100
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file, and Oracle licenses the original version of this file under the BSD
+ * license:
+ */
+/*
+   Copyright 2009-2013 Attila Szegedi
+
+   Licensed under both the Apache License, Version 2.0 (the "Apache License")
+   and the BSD License (the "BSD License"), with licensee being free to
+   choose either of the two at their discretion.
+
+   You may not use this file except in compliance with either the Apache
+   License or the BSD License.
+
+   If you choose to use this file in compliance with the Apache License, the
+   following notice applies to you:
+
+       You may obtain a copy of the Apache License at
+
+           http://www.apache.org/licenses/LICENSE-2.0
+
+       Unless required by applicable law or agreed to in writing, software
+       distributed under the License is distributed on an "AS IS" BASIS,
+       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+       implied. See the License for the specific language governing
+       permissions and limitations under the License.
+
+   If you choose to use this file in compliance with the BSD License, the
+   following notice applies to you:
+
+       Redistribution and use in source and binary forms, with or without
+       modification, are permitted provided that the following conditions are
+       met:
+       * Redistributions of source code must retain the above copyright
+         notice, this list of conditions and the following disclaimer.
+       * Redistributions in binary form must reproduce the above copyright
+         notice, this list of conditions and the following disclaimer in the
+         documentation and/or other materials provided with the distribution.
+       * Neither the name of the copyright holder nor the names of
+         contributors may be used to endorse or promote products derived from
+         this software without specific prior written permission.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+       IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+       TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+       PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER
+       BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+       CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+       SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+       BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+       WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+       OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+       ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package jdk.internal.dynalink;
+
+import jdk.internal.dynalink.linker.GuardedInvocation;
+import jdk.internal.dynalink.linker.LinkRequest;
+import jdk.internal.dynalink.linker.LinkerServices;
+
+/**
+ * Interface for objects that are used to transform one guarded invocation into another one. Typical usage is for
+ * implementing {@link DynamicLinkerFactory#setPrelinkFilter(GuardedInvocationFilter) pre-link filters}.
+ */
+public interface GuardedInvocationFilter {
+    /**
+     * Given a guarded invocation, return a potentially different guarded invocation.
+     * @param inv the original guarded invocation. Null is never passed.
+     * @param linkRequest the link request for which the invocation was generated (usually by some linker).
+     * @param linkerServices the linker services that can be used during creation of a new invocation.
+     * @return either the passed guarded invocation or a different one, with the difference usually determined based on
+     * information in the link request and the differing invocation created with the assistance of the linker services.
+     * Whether or not {@code null} is an accepted return value is dependent on the user of the filter.
+     */
+    public GuardedInvocation filter(GuardedInvocation inv, LinkRequest linkRequest, LinkerServices linkerServices);
+}
--- a/src/jdk/internal/dynalink/beans/AbstractJavaLinker.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/src/jdk/internal/dynalink/beans/AbstractJavaLinker.java	Wed Feb 26 13:17:57 2014 +0100
@@ -97,7 +97,6 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-
 import jdk.internal.dynalink.CallSiteDescriptor;
 import jdk.internal.dynalink.beans.GuardedInvocationComponent.ValidationType;
 import jdk.internal.dynalink.linker.GuardedInvocation;
@@ -107,6 +106,7 @@
 import jdk.internal.dynalink.support.CallSiteDescriptorFactory;
 import jdk.internal.dynalink.support.Guards;
 import jdk.internal.dynalink.support.Lookup;
+import jdk.internal.dynalink.support.TypeUtilities;
 
 /**
  * A base class for both {@link StaticClassLinker} and {@link BeanLinker}. Deals with common aspects of property
@@ -459,12 +459,16 @@
 
     private GuardedInvocationComponent getPropertySetter(CallSiteDescriptor callSiteDescriptor,
             LinkerServices linkerServices, List<String> operations) throws Exception {
-        final MethodType type = callSiteDescriptor.getMethodType();
         switch(callSiteDescriptor.getNameTokenCount()) {
             case 2: {
                 // Must have three arguments: target object, property name, and property value.
                 assertParameterCount(callSiteDescriptor, 3);
 
+                // We want setters that conform to "Object(O, V)". Note, we aren't doing "R(O, V)" as it might not be
+                // valid for us to convert return values proactively. Also, since we don't know what setters will be
+                // invoked, we'll conservatively presume Object return type.
+                final MethodType type = callSiteDescriptor.getMethodType().changeReturnType(Object.class);
+
                 // What's below is basically:
                 //   foldArguments(guardWithTest(isNotNull, invoke, null|nextComponent.invocation),
                 //     get_setter_handle(type, linkerServices))
@@ -473,8 +477,8 @@
                 // component's invocation.
 
                 // Call site type is "ret_type(object_type,property_name_type,property_value_type)", which we'll
-                // abbreviate to R(O, N, V) going forward.
-                // We want setters that conform to "R(O, V)"
+                // abbreviate to R(O, N, V) going forward, although we don't really use R here (see above about using
+                // Object return type).
                 final MethodType setterType = type.dropParameterTypes(1, 2);
                 // Bind property setter handle to the expected setter type and linker services. Type is
                 // MethodHandle(Object, String, Object)
@@ -495,11 +499,11 @@
 
                 final MethodHandle fallbackFolded;
                 if(nextComponent == null) {
-                    // Object(MethodHandle)->R(MethodHandle, O, N, V); returns constant null
+                    // Object(MethodHandle)->Object(MethodHandle, O, N, V); returns constant null
                     fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_METHOD_HANDLE, 1,
                             type.parameterList()).asType(type.insertParameterTypes(0, MethodHandle.class));
                 } else {
-                    // R(O, N, V)->R(MethodHandle, O, N, V); adapts the next component's invocation to drop the
+                    // Object(O, N, V)->Object(MethodHandle, O, N, V); adapts the next component's invocation to drop the
                     // extra argument resulting from fold
                     fallbackFolded = MethodHandles.dropArguments(nextComponent.getGuardedInvocation().getInvocation(),
                             0, MethodHandle.class);
@@ -545,9 +549,12 @@
 
     private GuardedInvocationComponent getPropertyGetter(CallSiteDescriptor callSiteDescriptor,
             LinkerServices linkerServices, List<String> ops) throws Exception {
-        final MethodType type = callSiteDescriptor.getMethodType();
         switch(callSiteDescriptor.getNameTokenCount()) {
             case 2: {
+                // Since we can't know what kind of a getter we'll get back on different invocations, we'll just
+                // conservatively presume Object. Note we can't just coerce to a narrower call site type as the linking
+                // runtime might not allow coercing at that call site.
+                final MethodType type = callSiteDescriptor.getMethodType().changeReturnType(Object.class);
                 // Must have exactly two arguments: receiver and name
                 assertParameterCount(callSiteDescriptor, 2);
 
@@ -563,11 +570,11 @@
                         GET_ANNOTATED_METHOD, 1, callSiteDescriptor.getLookup());
                 final MethodHandle callSiteBoundInvoker = MethodHandles.filterArguments(GETTER_INVOKER, 0,
                         callSiteBoundMethodGetter);
-                // Object(AnnotatedDynamicMethod, Object)->R(AnnotatedDynamicMethod, T0)
+                // Object(AnnotatedDynamicMethod, Object)->Object(AnnotatedDynamicMethod, T0)
                 final MethodHandle invokeHandleTyped = linkerServices.asType(callSiteBoundInvoker,
                         MethodType.methodType(type.returnType(), AnnotatedDynamicMethod.class, type.parameterType(0)));
                 // Since it's in the target of a fold, drop the unnecessary second argument
-                // R(AnnotatedDynamicMethod, T0)->R(AnnotatedDynamicMethod, T0, T1)
+                // Object(AnnotatedDynamicMethod, T0)->Object(AnnotatedDynamicMethod, T0, T1)
                 final MethodHandle invokeHandleFolded = MethodHandles.dropArguments(invokeHandleTyped, 2,
                         type.parameterType(1));
                 final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
@@ -575,17 +582,19 @@
 
                 final MethodHandle fallbackFolded;
                 if(nextComponent == null) {
-                    // Object(AnnotatedDynamicMethod)->R(AnnotatedDynamicMethod, T0, T1); returns constant null
+                    // Object(AnnotatedDynamicMethod)->Object(AnnotatedDynamicMethod, T0, T1); returns constant null
                     fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_ANNOTATED_METHOD, 1,
                             type.parameterList()).asType(type.insertParameterTypes(0, AnnotatedDynamicMethod.class));
                 } else {
-                    // R(T0, T1)->R(AnnotatedDynamicMethod, T0, T1); adapts the next component's invocation to drop the
-                    // extra argument resulting from fold
-                    fallbackFolded = MethodHandles.dropArguments(nextComponent.getGuardedInvocation().getInvocation(),
-                            0, AnnotatedDynamicMethod.class);
+                    // Object(T0, T1)->Object(AnnotatedDynamicMethod, T0, T1); adapts the next component's invocation to
+                    // drop the extra argument resulting from fold and to change its return type to Object.
+                    final MethodHandle nextInvocation = nextComponent.getGuardedInvocation().getInvocation();
+                    final MethodType nextType = nextInvocation.type();
+                    fallbackFolded = MethodHandles.dropArguments(nextInvocation.asType(
+                            nextType.changeReturnType(Object.class)), 0, AnnotatedDynamicMethod.class);
                 }
 
-                // fold(R(AnnotatedDynamicMethod, T0, T1), AnnotatedDynamicMethod(T0, T1))
+                // fold(Object(AnnotatedDynamicMethod, T0, T1), AnnotatedDynamicMethod(T0, T1))
                 final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(
                             IS_ANNOTATED_METHOD_NOT_NULL, invokeHandleFolded, fallbackFolded), typedGetter);
                 if(nextComponent == null) {
@@ -612,8 +621,8 @@
                 // value is null.
                 final ValidationType validationType = annGetter.validationType;
                 // TODO: we aren't using the type that declares the most generic getter here!
-                return new GuardedInvocationComponent(linkerServices.asType(getter, type), getGuard(validationType,
-                        type), clazz, validationType);
+                return new GuardedInvocationComponent(getter, getGuard(validationType,
+                        callSiteDescriptor.getMethodType()), clazz, validationType);
             }
             default: {
                 // Can't do anything with more than 3 name components
@@ -642,21 +651,25 @@
         }
     }
 
-    private static final MethodHandle IS_DYNAMIC_METHOD_NOT_NULL = Guards.asType(Guards.isNotNull(),
-            MethodType.methodType(boolean.class, DynamicMethod.class));
-    private static final MethodHandle DYNAMIC_METHOD_IDENTITY = MethodHandles.identity(DynamicMethod.class);
+    private static final MethodHandle IS_DYNAMIC_METHOD = Guards.isInstance(DynamicMethod.class,
+            MethodType.methodType(boolean.class, Object.class));
+    private static final MethodHandle OBJECT_IDENTITY = MethodHandles.identity(Object.class);
 
     private GuardedInvocationComponent getMethodGetter(CallSiteDescriptor callSiteDescriptor,
             LinkerServices linkerServices, List<String> ops) throws Exception {
-        final MethodType type = callSiteDescriptor.getMethodType();
+        // The created method handle will always return a DynamicMethod (or null), but since we don't want that type to
+        // be visible outside of this linker, declare it to return Object.
+        final MethodType type = callSiteDescriptor.getMethodType().changeReturnType(Object.class);
         switch(callSiteDescriptor.getNameTokenCount()) {
             case 2: {
                 // Must have exactly two arguments: receiver and name
                 assertParameterCount(callSiteDescriptor, 2);
                 final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
                         linkerServices, ops);
-                if(nextComponent == null) {
-                    // No next component operation; just return a component for this operation.
+                if(nextComponent == null || !TypeUtilities.areAssignable(DynamicMethod.class,
+                        nextComponent.getGuardedInvocation().getInvocation().type().returnType())) {
+                    // No next component operation, or it can never produce a dynamic method; just return a component
+                    // for this operation.
                     return getClassGuardedInvocationComponent(linkerServices.asType(getDynamicMethod, type), type);
                 }
 
@@ -665,21 +678,20 @@
                 // bunch of method signature adjustments. Basically, execute method getter; if it returns a non-null
                 // DynamicMethod, use identity to return it, otherwise delegate to nextComponent's invocation.
 
-                final MethodHandle typedGetter = linkerServices.asType(getDynamicMethod, type.changeReturnType(
-                        DynamicMethod.class));
+                final MethodHandle typedGetter = linkerServices.asType(getDynamicMethod, type);
                 // Since it is part of the foldArgument() target, it will have extra args that we need to drop.
                 final MethodHandle returnMethodHandle = linkerServices.asType(MethodHandles.dropArguments(
-                        DYNAMIC_METHOD_IDENTITY, 1, type.parameterList()), type.insertParameterTypes(0,
-                                DynamicMethod.class));
+                        OBJECT_IDENTITY, 1, type.parameterList()), type.insertParameterTypes(0, Object.class));
                 final MethodHandle nextComponentInvocation = nextComponent.getGuardedInvocation().getInvocation();
-                // The assumption is that getGuardedInvocationComponent() already asType()'d it correctly
-                assert nextComponentInvocation.type().equals(type);
+                // The assumption is that getGuardedInvocationComponent() already asType()'d it correctly modulo the
+                // return type.
+                assert nextComponentInvocation.type().changeReturnType(type.returnType()).equals(type);
                 // Since it is part of the foldArgument() target, we have to drop an extra arg it receives.
                 final MethodHandle nextCombinedInvocation = MethodHandles.dropArguments(nextComponentInvocation, 0,
-                        DynamicMethod.class);
+                        Object.class);
                 // Assemble it all into a fold(guard(isNotNull, identity, nextInvocation), get)
                 final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(
-                        IS_DYNAMIC_METHOD_NOT_NULL, returnMethodHandle, nextCombinedInvocation), typedGetter);
+                        IS_DYNAMIC_METHOD, returnMethodHandle, nextCombinedInvocation), typedGetter);
 
                 return nextComponent.compose(compositeGetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
             }
@@ -695,7 +707,7 @@
                 // No delegation to the next component of the composite operation; if we have a method with that name,
                 // we'll always return it at this point.
                 return getClassGuardedInvocationComponent(linkerServices.asType(MethodHandles.dropArguments(
-                        MethodHandles.constant(DynamicMethod.class, method), 0, type.parameterType(0)), type), type);
+                        MethodHandles.constant(Object.class, method), 0, type.parameterType(0)), type), type);
             }
             default: {
                 // Can't do anything with more than 3 name components
@@ -704,6 +716,30 @@
         }
     }
 
+    static class MethodPair {
+        final MethodHandle method1;
+        final MethodHandle method2;
+
+        MethodPair(final MethodHandle method1, final MethodHandle method2) {
+            this.method1 = method1;
+            this.method2 = method2;
+        }
+
+        MethodHandle guardWithTest(final MethodHandle test) {
+            return MethodHandles.guardWithTest(test, method1, method2);
+        }
+    }
+
+    static MethodPair matchReturnTypes(MethodHandle m1, MethodHandle m2) {
+        final MethodType type1 = m1.type();
+        final MethodType type2 = m2.type();
+        final Class<?> commonRetType = TypeUtilities.getCommonLosslessConversionType(type1.returnType(),
+                type2.returnType());
+        return new MethodPair(
+                m1.asType(type1.changeReturnType(commonRetType)),
+                m2.asType(type2.changeReturnType(commonRetType)));
+    }
+
     private static void assertParameterCount(CallSiteDescriptor descriptor, int paramCount) {
         if(descriptor.getMethodType().parameterCount() != paramCount) {
             throw new BootstrapMethodError(descriptor.getName() + " must have exactly " + paramCount + " parameters.");
@@ -739,11 +775,14 @@
     }
 
     private static MethodHandle GET_DYNAMIC_METHOD = MethodHandles.dropArguments(privateLookup.findOwnSpecial(
-            "getDynamicMethod", DynamicMethod.class, Object.class), 1, Object.class);
+            "getDynamicMethod", Object.class, Object.class), 1, Object.class);
     private final MethodHandle getDynamicMethod = GET_DYNAMIC_METHOD.bindTo(this);
 
     @SuppressWarnings("unused")
-    private DynamicMethod getDynamicMethod(Object name) {
+    // This method is marked to return Object instead of DynamicMethod as it's used as a linking component and we don't
+    // want to make the DynamicMethod type observable externally (e.g. as the return type of a MethodHandle returned for
+    // "dyn:getMethod" linking).
+    private Object getDynamicMethod(Object name) {
         return getDynamicMethod(String.valueOf(name), methods);
     }
 
--- a/src/jdk/internal/dynalink/beans/BeanLinker.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/src/jdk/internal/dynalink/beans/BeanLinker.java	Wed Feb 26 13:17:57 2014 +0100
@@ -235,8 +235,9 @@
         } else {
             checkGuard = convertArgToInt(RANGE_CHECK_ARRAY, linkerServices, callSiteDescriptor);
         }
-        return nextComponent.compose(MethodHandles.guardWithTest(binder.bindTest(checkGuard),
-                binder.bind(invocation), nextComponent.getGuardedInvocation().getInvocation()), gi.getGuard(),
+        final MethodPair matchedInvocations = matchReturnTypes(binder.bind(invocation),
+                nextComponent.getGuardedInvocation().getInvocation());
+        return nextComponent.compose(matchedInvocations.guardWithTest(binder.bindTest(checkGuard)), gi.getGuard(),
                 gic.getValidatorClass(), gic.getValidationType());
     }
 
@@ -306,7 +307,7 @@
         }
 
         /*private*/ MethodHandle bind(MethodHandle handle) {
-            return bindToFixedKey(linkerServices.asType(handle, methodType));
+            return bindToFixedKey(linkerServices.asTypeLosslessReturn(handle, methodType));
         }
 
         /*private*/ MethodHandle bindTest(MethodHandle handle) {
@@ -438,8 +439,9 @@
 
         final MethodHandle checkGuard = convertArgToInt(invocation == SET_LIST_ELEMENT ? RANGE_CHECK_LIST :
             RANGE_CHECK_ARRAY, linkerServices, callSiteDescriptor);
-        return nextComponent.compose(MethodHandles.guardWithTest(binder.bindTest(checkGuard),
-                binder.bind(invocation), nextComponent.getGuardedInvocation().getInvocation()), gi.getGuard(),
+        final MethodPair matchedInvocations = matchReturnTypes(binder.bind(invocation),
+                nextComponent.getGuardedInvocation().getInvocation());
+        return nextComponent.compose(matchedInvocations.guardWithTest(binder.bindTest(checkGuard)), gi.getGuard(),
                 gic.getValidatorClass(), gic.getValidationType());
     }
 
--- a/src/jdk/internal/dynalink/beans/OverloadedDynamicMethod.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/src/jdk/internal/dynalink/beans/OverloadedDynamicMethod.java	Wed Feb 26 13:17:57 2014 +0100
@@ -148,7 +148,6 @@
         }
     }
 
-    @SuppressWarnings("fallthrough")
     @Override
     public MethodHandle getInvocation(final CallSiteDescriptor callSiteDescriptor, final LinkerServices linkerServices) {
         final MethodType callSiteType = callSiteDescriptor.getMethodType();
@@ -207,7 +206,7 @@
             case 1: {
                 // Very lucky, we ended up with a single candidate method handle based on the call site signature; we
                 // can link it very simply by delegating to the SingleDynamicMethod.
-                invokables.iterator().next().getInvocation(callSiteDescriptor, linkerServices);
+                return invokables.iterator().next().getInvocation(callSiteDescriptor, linkerServices);
             }
             default: {
                 // We have more than one candidate. We have no choice but to link to a method that resolves overloads on
--- a/src/jdk/internal/dynalink/beans/OverloadedMethod.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/src/jdk/internal/dynalink/beans/OverloadedMethod.java	Wed Feb 26 13:17:57 2014 +0100
@@ -93,6 +93,7 @@
 import java.util.concurrent.ConcurrentHashMap;
 import jdk.internal.dynalink.linker.LinkerServices;
 import jdk.internal.dynalink.support.Lookup;
+import jdk.internal.dynalink.support.TypeUtilities;
 
 /**
  * Represents a subset of overloaded methods for a certain method name on a certain class. It can be either a fixarg or
@@ -114,13 +115,15 @@
     OverloadedMethod(List<MethodHandle> methodHandles, OverloadedDynamicMethod parent, MethodType callSiteType,
             LinkerServices linkerServices) {
         this.parent = parent;
-        this.callSiteType = callSiteType;
+        final Class<?> commonRetType = getCommonReturnType(methodHandles);
+        this.callSiteType = callSiteType.changeReturnType(commonRetType);
         this.linkerServices = linkerServices;
 
         fixArgMethods = new ArrayList<>(methodHandles.size());
         varArgMethods = new ArrayList<>(methodHandles.size());
         final int argNum = callSiteType.parameterCount();
         for(MethodHandle mh: methodHandles) {
+            mh = mh.asType(mh.type().changeReturnType(commonRetType));
             if(mh.isVarargsCollector()) {
                 final MethodHandle asFixed = mh.asFixedArity();
                 if(argNum == asFixed.type().parameterCount()) {
@@ -137,7 +140,7 @@
         final MethodHandle bound = SELECT_METHOD.bindTo(this);
         final MethodHandle collecting = SingleDynamicMethod.collectArguments(bound, argNum).asType(
                 callSiteType.changeReturnType(MethodHandle.class));
-        invoker = MethodHandles.foldArguments(MethodHandles.exactInvoker(callSiteType), collecting);
+        invoker = MethodHandles.foldArguments(MethodHandles.exactInvoker(this.callSiteType), collecting);
     }
 
     MethodHandle getInvoker() {
@@ -262,4 +265,13 @@
             b.append(classes[l - 1].getComponentType().getCanonicalName()).append("...");
         }
     }
+
+    private static Class<?> getCommonReturnType(List<MethodHandle> methodHandles) {
+        final Iterator<MethodHandle> it = methodHandles.iterator();
+        Class<?> retType = it.next().type().returnType();
+        while(it.hasNext()) {
+            retType = TypeUtilities.getCommonLosslessConversionType(retType, it.next().type().returnType());
+        }
+        return retType;
+    }
 }
--- a/src/jdk/internal/dynalink/beans/SingleDynamicMethod.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/src/jdk/internal/dynalink/beans/SingleDynamicMethod.java	Wed Feb 26 13:17:57 2014 +0100
@@ -156,7 +156,9 @@
     /**
      * Given a method handle and a call site type, adapts the method handle to the call site type. Performs type
      * conversions as needed using the specified linker services, and in case that the method handle is a vararg
-     * collector, matches it to the arity of the call site.
+     * collector, matches it to the arity of the call site. The type of the return value is only changed if it can be
+     * converted using a conversion that loses neither precision nor magnitude, see
+     * {@link LinkerServices#asTypeLosslessReturn(MethodHandle, MethodType)}.
      * @param target the method handle to adapt
      * @param callSiteType the type of the call site
      * @param linkerServices the linker services used for type conversions
@@ -286,7 +288,7 @@
 
     private static MethodHandle createConvertingInvocation(final MethodHandle sizedMethod,
             final LinkerServices linkerServices, final MethodType callSiteType) {
-        return linkerServices.asType(sizedMethod, callSiteType);
+        return linkerServices.asTypeLosslessReturn(sizedMethod, callSiteType);
     }
 
     private static boolean typeMatchesDescription(String paramTypes, MethodType type) {
--- a/src/jdk/internal/dynalink/linker/GuardedInvocation.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/src/jdk/internal/dynalink/linker/GuardedInvocation.java	Wed Feb 26 13:17:57 2014 +0100
@@ -90,7 +90,9 @@
 import java.lang.invoke.WrongMethodTypeException;
 import java.util.List;
 import jdk.internal.dynalink.CallSiteDescriptor;
+import jdk.internal.dynalink.support.CatchExceptionCombinator;
 import jdk.internal.dynalink.support.Guards;
+import jdk.nashorn.internal.runtime.options.Options;
 
 /**
  * Represents a conditionally valid method handle. It is an immutable triple of an invocation method handle, a guard
@@ -102,11 +104,24 @@
  * @author Attila Szegedi
  */
 public class GuardedInvocation {
+    private static final boolean USE_FAST_REWRITE = Options.getBooleanProperty("nashorn.fastrewrite");
+
     private final MethodHandle invocation;
     private final MethodHandle guard;
+    private final Class<? extends Throwable> exception;
     private final SwitchPoint switchPoint;
 
     /**
+     * Creates a new guarded invocation. This invocation is unconditional as it has no invalidations.
+     *
+     * @param invocation the method handle representing the invocation. Must not be null.
+     * @throws NullPointerException if invocation is null.
+     */
+    public GuardedInvocation(MethodHandle invocation) {
+        this(invocation, null, null, null);
+    }
+
+    /**
      * Creates a new guarded invocation.
      *
      * @param invocation the method handle representing the invocation. Must not be null.
@@ -116,7 +131,18 @@
      * @throws NullPointerException if invocation is null.
      */
     public GuardedInvocation(MethodHandle invocation, MethodHandle guard) {
-        this(invocation, guard, null);
+        this(invocation, guard, null, null);
+    }
+
+    /**
+     * Creates a new guarded invocation.
+     *
+     * @param invocation the method handle representing the invocation. Must not be null.
+     * @param switchPoint the optional switch point that can be used to invalidate this linkage.
+     * @throws NullPointerException if invocation is null.
+     */
+    public GuardedInvocation(MethodHandle invocation, SwitchPoint switchPoint) {
+        this(invocation, null, switchPoint, null);
     }
 
     /**
@@ -130,25 +156,29 @@
      * @throws NullPointerException if invocation is null.
      */
     public GuardedInvocation(MethodHandle invocation, MethodHandle guard, SwitchPoint switchPoint) {
-        invocation.getClass(); // NPE check
-        this.invocation = invocation;
-        this.guard = guard;
-        this.switchPoint = switchPoint;
+        this(invocation, guard, switchPoint, null);
     }
 
     /**
      * Creates a new guarded invocation.
      *
      * @param invocation the method handle representing the invocation. Must not be null.
-     * @param switchPoint the optional switch point that can be used to invalidate this linkage.
      * @param guard the method handle representing the guard. Must have the same method type as the invocation, except
      * it must return boolean. For some useful guards, check out the {@link Guards} class. It can be null. If both it
      * and the switch point are null, this represents an unconditional invocation, which is legal but unusual.
+     * @param switchPoint the optional switch point that can be used to invalidate this linkage.
+     * @param exception the optional exception type that is expected to be thrown by the invocation and that also
+     * invalidates the linkage.
      * @throws NullPointerException if invocation is null.
      */
-    public GuardedInvocation(MethodHandle invocation, SwitchPoint switchPoint, MethodHandle guard) {
-        this(invocation, guard, switchPoint);
+    public GuardedInvocation(MethodHandle invocation, MethodHandle guard, SwitchPoint switchPoint, Class<? extends Throwable> exception) {
+        invocation.getClass(); // NPE check
+        this.invocation = invocation;
+        this.guard = guard;
+        this.switchPoint = switchPoint;
+        this.exception = exception;
     }
+
     /**
      * Returns the invocation method handle.
      *
@@ -177,6 +207,15 @@
     }
 
     /**
+     * Returns the exception type that if thrown should be used to invalidate the linkage.
+     *
+     * @return the exception type that if thrown should be used to invalidate the linkage. Can be null.
+     */
+    public Class<? extends Throwable> getException() {
+        return exception;
+    }
+
+    /**
      * Returns true if and only if this guarded invocation has a switchpoint, and that switchpoint has been invalidated.
      * @return true if and only if this guarded invocation has a switchpoint, and that switchpoint has been invalidated.
      */
@@ -206,7 +245,7 @@
      * @return a new guarded invocation with the replaced methods and the same switch point as this invocation.
      */
     public GuardedInvocation replaceMethods(MethodHandle newInvocation, MethodHandle newGuard) {
-        return new GuardedInvocation(newInvocation, newGuard, switchPoint);
+        return new GuardedInvocation(newInvocation, newGuard, switchPoint, exception);
     }
 
     private GuardedInvocation replaceMethodsOrThis(MethodHandle newInvocation, MethodHandle newGuard) {
@@ -241,6 +280,20 @@
     }
 
     /**
+     * Changes the type of the invocation, as if {@link LinkerServices#asTypeLosslessReturn(MethodHandle, MethodType)} was
+     * applied to its invocation and {@link LinkerServices#asType(MethodHandle, MethodType)} applied to its guard, if it
+     * has one (with return type changed to boolean, and parameter count potentially truncated for the guard). If the
+     * invocation doesn't change its type, returns this object.
+     * @param linkerServices the linker services to use for the conversion
+     * @param newType the new type of the invocation.
+     * @return a guarded invocation with the new type applied to it.
+     */
+    public GuardedInvocation asTypeSafeReturn(LinkerServices linkerServices, MethodType newType) {
+        return replaceMethodsOrThis(linkerServices.asTypeLosslessReturn(invocation, newType), guard == null ? null :
+            Guards.asType(linkerServices, guard, newType));
+    }
+
+    /**
      * Changes the type of the invocation, as if {@link MethodHandle#asType(MethodType)} was applied to its invocation
      * and its guard, if it has one (with return type changed to boolean for guard). If the invocation already is of the
      * required type, returns this object.
@@ -303,9 +356,17 @@
     public MethodHandle compose(MethodHandle switchpointFallback, MethodHandle guardFallback) {
         final MethodHandle guarded =
                 guard == null ? invocation : MethodHandles.guardWithTest(guard, invocation, guardFallback);
-        return switchPoint == null ? guarded : switchPoint.guardWithTest(guarded, switchpointFallback);
+        final MethodHandle catchGuarded = exception == null ? guarded : catchException(guarded, exception,
+                MethodHandles.dropArguments(guardFallback, 0, exception));
+        return switchPoint == null ? catchGuarded : switchPoint.guardWithTest(catchGuarded, switchpointFallback);
     }
 
+    private static MethodHandle catchException(final MethodHandle target, final Class<? extends Throwable> exType, final MethodHandle handler) {
+        if(USE_FAST_REWRITE) {
+            return CatchExceptionCombinator.catchException(target, exType, handler);
+        }
+        return MethodHandles.catchException(target, exType, handler);
+    }
     private static void assertType(MethodHandle mh, MethodType type) {
         if(!mh.type().equals(type)) {
             throw new WrongMethodTypeException("Expected type: " + type + " actual type: " + mh.type());
--- a/src/jdk/internal/dynalink/linker/GuardingDynamicLinker.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/src/jdk/internal/dynalink/linker/GuardingDynamicLinker.java	Wed Feb 26 13:17:57 2014 +0100
@@ -101,10 +101,16 @@
      * @return a guarded invocation with a method handle suitable for the arguments, as well as a guard condition that
      * if fails should trigger relinking. Must return null if it can't resolve the invocation. If the returned
      * invocation is unconditional (which is actually quite rare), the guard in the return value can be null. The
-     * invocation can also have a switch point for asynchronous invalidation of the linkage. If the linker does not
-     * recognize any native language runtime contexts in arguments, or does recognize its own, but receives a call site
-     * descriptor without its recognized context in the arguments, it should invoke
-     * {@link LinkRequest#withoutRuntimeContext()} and link for that.
+     * invocation can also have a switch point for asynchronous invalidation of the linkage, as well as a
+     * {@link Throwable} subclass that describes an expected exception condition that also triggers relinking (often it
+     * is faster to rely on an infrequent but expected {@link ClassCastException} than on an always evaluated
+     * {@code instanceof} guard). If the linker does not recognize any native language runtime contexts in arguments, or
+     * does recognize its own, but receives a call site descriptor without its recognized context in the arguments, it
+     * should invoke {@link LinkRequest#withoutRuntimeContext()} and link for that. While the linker must produce an
+     * invocation with parameter types matching those in the call site descriptor of the link request, it should not try
+     * to match the return type expected at the call site except when it can do it with only the conversions that lose
+     * neither precision nor magnitude, see {@link LinkerServices#asTypeLosslessReturn(java.lang.invoke.MethodHandle,
+     * java.lang.invoke.MethodType)}.
      * @throws Exception if the operation fails for whatever reason
      */
     public GuardedInvocation getGuardedInvocation(LinkRequest linkRequest, LinkerServices linkerServices)
--- a/src/jdk/internal/dynalink/linker/LinkRequest.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/src/jdk/internal/dynalink/linker/LinkRequest.java	Wed Feb 26 13:17:57 2014 +0100
@@ -101,6 +101,17 @@
     public CallSiteDescriptor getCallSiteDescriptor();
 
     /**
+     * Returns the call site token for the call site being linked. This token is an opaque object that is guaranteed to
+     * have different identity for different call sites, and is also guaranteed to not become weakly reachable before
+     * the call site does and to become weakly reachable some time after the call site does. This makes it ideal as a
+     * candidate for a key in a weak hash map in which a linker might want to keep per-call site linking state (usually
+     * profiling information).
+     *
+     * @return the call site token for the call site being linked.
+     */
+    public Object getCallSiteToken();
+
+    /**
      * Returns the arguments for the invocation being linked. The returned array is a clone; modifications to it won't
      * affect the arguments in this request.
      *
--- a/src/jdk/internal/dynalink/linker/LinkerServices.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/src/jdk/internal/dynalink/linker/LinkerServices.java	Wed Feb 26 13:17:57 2014 +0100
@@ -87,7 +87,9 @@
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
 import jdk.internal.dynalink.DynamicLinker;
+import jdk.internal.dynalink.DynamicLinkerFactory;
 import jdk.internal.dynalink.linker.ConversionComparator.Comparison;
+import jdk.internal.dynalink.support.TypeUtilities;
 
 /**
  * Interface for services provided to {@link GuardingDynamicLinker} instances by the {@link DynamicLinker} that owns
@@ -103,18 +105,34 @@
      * parameters. It will apply {@link MethodHandle#asType(MethodType)} for all primitive-to-primitive,
      * wrapper-to-primitive, primitive-to-wrapper conversions as well as for all upcasts. For all other conversions,
      * it'll insert {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)} with composite filters
-     * provided by {@link GuardingTypeConverterFactory} implementations. It doesn't use language-specific conversions on
-     * the return type.
+     * provided by {@link GuardingTypeConverterFactory} implementations.
      *
      * @param handle target method handle
      * @param fromType the types of source arguments
-     * @return a method handle that is a suitable combination of {@link MethodHandle#asType(MethodType)} and
-     * {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)} with
-     * {@link GuardingTypeConverterFactory} produced type converters as filters.
+     * @return a method handle that is a suitable combination of {@link MethodHandle#asType(MethodType)},
+     * {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)}, and
+     * {@link MethodHandles#filterReturnValue(MethodHandle, MethodHandle)} with
+     * {@link GuardingTypeConverterFactory}-produced type converters as filters.
      */
     public MethodHandle asType(MethodHandle handle, MethodType fromType);
 
     /**
+     * Similar to {@link #asType(MethodHandle, MethodType)} except it only converts the return type of the method handle
+     * when it can be done using a conversion that loses neither precision nor magnitude, otherwise it leaves it
+     * unchanged. The idea is that other conversions should not be performed by individual linkers, but instead the
+     * {@link DynamicLinkerFactory#setPrelinkFilter(jdk.internal.dynalink.GuardedInvocationFilter) pre-link filter of
+     * the dynamic linker} should implement the strategy of dealing with potentially lossy return type conversions in a
+     * manner specific to the language runtime.
+     *
+     * @param handle target method handle
+     * @param fromType the types of source arguments
+     * @return a method handle that is a suitable combination of {@link MethodHandle#asType(MethodType)}, and
+     * {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)} with
+     * {@link GuardingTypeConverterFactory}-produced type converters as filters.
+     */
+    public MethodHandle asTypeLosslessReturn(MethodHandle handle, MethodType fromType);
+
+    /**
      * Given a source and target type, returns a method handle that converts between them. Never returns null; in worst
      * case it will return an identity conversion (that might fail for some values at runtime). You rarely need to use
      * this method directly; you should mostly rely on {@link #asType(MethodHandle, MethodType)} instead. You really
@@ -161,4 +179,23 @@
      * conversion.
      */
     public Comparison compareConversion(Class<?> sourceType, Class<?> targetType1, Class<?> targetType2);
+
+    /**
+     * If we could just use Java 8 constructs, then {@code asTypeSafeReturn} would be a method with default
+     * implementation. Since we can't do that, we extract common default implementations into this static class.
+     */
+    public static class Implementation {
+        /**
+         * Default implementation for {@link LinkerServices#asTypeLosslessReturn(MethodHandle, MethodType)}.
+         * @param linkerServices the linker services that delegates to this implementation
+         * @param handle the passed handle
+         * @param fromType the passed type
+         * @return the converted method handle, as per the {@code asTypeSafeReturn} semantics.
+         */
+        public static MethodHandle asTypeLosslessReturn(LinkerServices linkerServices, MethodHandle handle, MethodType fromType) {
+            final Class<?> handleReturnType = handle.type().returnType();
+            return linkerServices.asType(handle, TypeUtilities.isConvertibleWithoutLoss(handleReturnType, fromType.returnType()) ?
+                    fromType : fromType.changeReturnType(handleReturnType));
+        }
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/internal/dynalink/support/CatchExceptionCombinator.java	Wed Feb 26 13:17:57 2014 +0100
@@ -0,0 +1,180 @@
+package jdk.internal.dynalink.support;
+
+import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
+import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC;
+import static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER;
+import static jdk.internal.org.objectweb.asm.Opcodes.V1_7;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.function.Function;
+import jdk.internal.org.objectweb.asm.ClassWriter;
+import jdk.internal.org.objectweb.asm.Label;
+import jdk.internal.org.objectweb.asm.Type;
+import jdk.internal.org.objectweb.asm.commons.InstructionAdapter;
+import jdk.nashorn.internal.runtime.RewriteException;
+import sun.misc.Unsafe;
+
+/**
+ * Generates method handles that combine an invocation and a handler for a {@link RewriteException}. Always immediately
+ * generates compiled bytecode.
+ */
+public class CatchExceptionCombinator {
+    static {
+        System.err.println("*** Running with fast catch combinator handler ***");
+    }
+    private static final Type METHOD_HANDLE_TYPE = Type.getType(MethodHandle.class);
+    private static final String METHOD_HANDLE_TYPE_NAME = METHOD_HANDLE_TYPE.getInternalName();
+    private static final String OBJECT_TYPE_NAME = Type.getInternalName(Object.class);
+
+    private static final String HANDLER_TYPE_NAME = "java.lang.invoke.CatchExceptionCombinator$MH";
+    private static final String INVOKE_METHOD_NAME = "invoke";
+
+    private static final Unsafe UNSAFE = Unsafe.getUnsafe();
+
+    private static final ConcurrentMap<CombinatorParameters, ClassTemplate> handlerClassBytes = new ConcurrentHashMap<>();
+
+    private static final class CombinatorParameters {
+        final MethodType targetType;
+        final Class<? extends Throwable> exType;
+        final MethodType handlerType;
+
+        CombinatorParameters(final MethodType targetType, final Class<? extends Throwable> exType, MethodType handlerType) {
+            this.targetType = targetType;
+            this.exType = exType;
+            this.handlerType = handlerType;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if(obj instanceof CombinatorParameters) {
+                final CombinatorParameters p = (CombinatorParameters)obj;
+                return targetType.equals(p.targetType) && exType.equals(p.exType) && handlerType.equals(p.handlerType);
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            return targetType.hashCode() ^ exType.hashCode() ^ handlerType.hashCode();
+        }
+    }
+
+    /**
+     * Catch exception - create combinator
+     * @param target  target
+     * @param exType  type to check for
+     * @param handler catch handler
+     * @return target wrapped in catch handler
+     */
+    public static MethodHandle catchException(final MethodHandle target, final Class<? extends Throwable> exType, final MethodHandle handler) {
+        final MethodType targetType = target.type();
+        final MethodType handlerType = handler.type();
+
+        final ClassTemplate classTemplate = handlerClassBytes.computeIfAbsent(
+                new CombinatorParameters(targetType, exType, handlerType), new Function<CombinatorParameters, ClassTemplate>() {
+            @Override
+            public ClassTemplate apply(final CombinatorParameters parameters) {
+                return generateClassTemplate(parameters);
+            }
+        });
+        return classTemplate.instantiate(target, handler, targetType);
+    }
+
+    private static final class ClassTemplate {
+        final byte[] bytes;
+        final int target_cp_index;
+        final int handler_cp_index;
+        final int cp_size;
+
+        ClassTemplate(final byte[] bytes, final int target_cp_index, final int handler_cp_index, final int cp_size) {
+            this.bytes = bytes;
+            this.target_cp_index = target_cp_index;
+            this.handler_cp_index = handler_cp_index;
+            this.cp_size = cp_size;
+        }
+
+        MethodHandle instantiate(final MethodHandle target, final MethodHandle handler, final MethodType type) {
+            final Object[] cpPatch = new Object[cp_size];
+            cpPatch[target_cp_index] = target;
+            cpPatch[handler_cp_index] = handler;
+            final Class<?> handlerClass = UNSAFE.defineAnonymousClass(CatchExceptionCombinator.class, bytes, cpPatch);
+            try {
+                return MethodHandles.lookup().findStatic(handlerClass, INVOKE_METHOD_NAME, type);
+            } catch (NoSuchMethodException | IllegalAccessException e) {
+                throw new AssertionError(e);
+            }
+        }
+    }
+
+    private static ClassTemplate generateClassTemplate(final CombinatorParameters combinatorParameters) {
+        final ClassWriter w = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
+        w.visit(V1_7, ACC_PUBLIC | ACC_SUPER, HANDLER_TYPE_NAME, null, OBJECT_TYPE_NAME, null);
+
+        final MethodType targetType = combinatorParameters.targetType;
+        final Class<? extends Throwable> exType = combinatorParameters.exType;
+        final String methodDescriptor = targetType.toMethodDescriptorString();
+        final Class<?> returnType = targetType.returnType();
+        final MethodType handlerType = combinatorParameters.handlerType;
+
+        // NOTE: must use strings as placeholders in the constant pool, even if we'll be replacing them with method handles.
+        final String targetPlaceholder = "T_PLACEHOLDER";
+        final String handlerPlaceholder = "H_PLACEHOLDER";
+        final int target_cp_index = w.newConst(targetPlaceholder);
+        final int handler_cp_index = w.newConst(handlerPlaceholder);
+
+        final InstructionAdapter mv = new InstructionAdapter(w.visitMethod(ACC_PUBLIC | ACC_STATIC, INVOKE_METHOD_NAME, methodDescriptor, null, null));
+        mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true);
+        mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Compiled;", true);
+        mv.visitAnnotation("Ljava/lang/invoke/ForceInline;", true);
+
+        mv.visitCode();
+
+        final Label _try = new Label();
+        final Label _end_try= new Label();
+
+        mv.visitLabel(_try);
+        // Invoke
+        mv.aconst(targetPlaceholder);
+        mv.checkcast(METHOD_HANDLE_TYPE);
+        final Class<?>[] paramTypes = targetType.parameterArray();
+        for(int i = 0, slot = 0; i < paramTypes.length; ++i) {
+            final Type paramType = Type.getType(paramTypes[i]);
+            mv.load(slot, paramType);
+            slot += paramType.getSize();
+        }
+        generateInvokeBasic(mv, methodDescriptor);
+        final Type asmReturnType = Type.getType(returnType);
+        mv.areturn(asmReturnType);
+
+        mv.visitTryCatchBlock(_try, _end_try, _end_try, Type.getInternalName(exType));
+        mv.visitLabel(_end_try);
+        // Handle exception
+        mv.aconst(handlerPlaceholder);
+        mv.checkcast(METHOD_HANDLE_TYPE);
+        mv.swap();
+        final Class<?>[] handlerParamTypes = handlerType.parameterArray();
+        for(int i = 1, slot = 0; i < handlerParamTypes.length; ++i) {
+            final Type paramType = Type.getType(handlerParamTypes[i]);
+            mv.load(slot, paramType);
+            slot += paramType.getSize();
+        }
+        generateInvokeBasic(mv, handlerType.toMethodDescriptorString());
+        mv.areturn(asmReturnType);
+
+        mv.visitMaxs(0, 0);
+        mv.visitEnd();
+
+        w.visitEnd();
+        final byte[] bytes = w.toByteArray();
+        final int cp_size = (((bytes[8] & 0xFF) << 8) | (bytes[9] & 0xFF));
+        return new ClassTemplate(bytes, target_cp_index, handler_cp_index, cp_size);
+    }
+
+    private static void generateInvokeBasic(final InstructionAdapter mv, final String methodDesc) {
+        mv.invokevirtual(METHOD_HANDLE_TYPE_NAME, "invokeBasic", methodDesc, false);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/internal/dynalink/support/DefaultPrelinkFilter.java	Wed Feb 26 13:17:57 2014 +0100
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file, and Oracle licenses the original version of this file under the BSD
+ * license:
+ */
+/*
+   Copyright 2009-2013 Attila Szegedi
+
+   Licensed under both the Apache License, Version 2.0 (the "Apache License")
+   and the BSD License (the "BSD License"), with licensee being free to
+   choose either of the two at their discretion.
+
+   You may not use this file except in compliance with either the Apache
+   License or the BSD License.
+
+   If you choose to use this file in compliance with the Apache License, the
+   following notice applies to you:
+
+       You may obtain a copy of the Apache License at
+
+           http://www.apache.org/licenses/LICENSE-2.0
+
+       Unless required by applicable law or agreed to in writing, software
+       distributed under the License is distributed on an "AS IS" BASIS,
+       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+       implied. See the License for the specific language governing
+       permissions and limitations under the License.
+
+   If you choose to use this file in compliance with the BSD License, the
+   following notice applies to you:
+
+       Redistribution and use in source and binary forms, with or without
+       modification, are permitted provided that the following conditions are
+       met:
+       * Redistributions of source code must retain the above copyright
+         notice, this list of conditions and the following disclaimer.
+       * Redistributions in binary form must reproduce the above copyright
+         notice, this list of conditions and the following disclaimer in the
+         documentation and/or other materials provided with the distribution.
+       * Neither the name of the copyright holder nor the names of
+         contributors may be used to endorse or promote products derived from
+         this software without specific prior written permission.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+       IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+       TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+       PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER
+       BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+       CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+       SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+       BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+       WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+       OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+       ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package jdk.internal.dynalink.support;
+
+import jdk.internal.dynalink.GuardedInvocationFilter;
+import jdk.internal.dynalink.linker.GuardedInvocation;
+import jdk.internal.dynalink.linker.LinkRequest;
+import jdk.internal.dynalink.linker.LinkerServices;
+
+/**
+ * Default filter for guarded invocation pre link filtering
+ */
+public class DefaultPrelinkFilter implements GuardedInvocationFilter {
+    @Override
+    public GuardedInvocation filter(GuardedInvocation inv, LinkRequest request, LinkerServices linkerServices) {
+        return inv.asType(linkerServices, request.getCallSiteDescriptor().getMethodType());
+    }
+}
--- a/src/jdk/internal/dynalink/support/LinkRequestImpl.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/src/jdk/internal/dynalink/support/LinkRequestImpl.java	Wed Feb 26 13:17:57 2014 +0100
@@ -95,6 +95,7 @@
 public class LinkRequestImpl implements LinkRequest {
 
     private final CallSiteDescriptor callSiteDescriptor;
+    private final Object callSiteToken;
     private final Object[] arguments;
     private final boolean callSiteUnstable;
 
@@ -102,11 +103,13 @@
      * Creates a new link request.
      *
      * @param callSiteDescriptor the descriptor for the call site being linked
+     * @param callSiteToken the opaque token for the call site being linked.
      * @param callSiteUnstable true if the call site being linked is considered unstable
      * @param arguments the arguments for the invocation
      */
-    public LinkRequestImpl(CallSiteDescriptor callSiteDescriptor, boolean callSiteUnstable, Object... arguments) {
+    public LinkRequestImpl(CallSiteDescriptor callSiteDescriptor, Object callSiteToken, boolean callSiteUnstable, Object... arguments) {
         this.callSiteDescriptor = callSiteDescriptor;
+        this.callSiteToken = callSiteToken;
         this.callSiteUnstable = callSiteUnstable;
         this.arguments = arguments;
     }
@@ -127,6 +130,11 @@
     }
 
     @Override
+    public Object getCallSiteToken() {
+        return callSiteToken;
+    }
+
+    @Override
     public boolean isCallSiteUnstable() {
         return callSiteUnstable;
     }
@@ -138,6 +146,6 @@
 
     @Override
     public LinkRequest replaceArguments(CallSiteDescriptor newCallSiteDescriptor, Object[] newArguments) {
-        return new LinkRequestImpl(newCallSiteDescriptor, callSiteUnstable, newArguments);
+        return new LinkRequestImpl(newCallSiteDescriptor, callSiteToken, callSiteUnstable, newArguments);
     }
 }
--- a/src/jdk/internal/dynalink/support/LinkerServicesImpl.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/src/jdk/internal/dynalink/support/LinkerServicesImpl.java	Wed Feb 26 13:17:57 2014 +0100
@@ -127,6 +127,11 @@
     }
 
     @Override
+    public MethodHandle asTypeLosslessReturn(MethodHandle handle, MethodType fromType) {
+        return Implementation.asTypeLosslessReturn(this, handle, fromType);
+    }
+
+    @Override
     public MethodHandle getTypeConverter(Class<?> sourceType, Class<?> targetType) {
         return typeConverterFactory.getTypeConverter(sourceType, targetType);
     }
--- a/src/jdk/internal/dynalink/support/RuntimeContextLinkRequestImpl.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/src/jdk/internal/dynalink/support/RuntimeContextLinkRequestImpl.java	Wed Feb 26 13:17:57 2014 +0100
@@ -101,15 +101,16 @@
      * Creates a new link request.
      *
      * @param callSiteDescriptor the descriptor for the call site being linked
+     * @param callSiteToken the opaque token for the call site being linked.
      * @param arguments the arguments for the invocation
      * @param callSiteUnstable true if the call site being linked is considered unstable
      * @param runtimeContextArgCount the number of the leading arguments on the stack that represent the language
      * runtime specific context arguments.
      * @throws IllegalArgumentException if runtimeContextArgCount is less than 1.
      */
-    public RuntimeContextLinkRequestImpl(CallSiteDescriptor callSiteDescriptor, boolean callSiteUnstable,
-            Object[] arguments, int runtimeContextArgCount) {
-        super(callSiteDescriptor, callSiteUnstable, arguments);
+    public RuntimeContextLinkRequestImpl(CallSiteDescriptor callSiteDescriptor, Object callSiteToken,
+            boolean callSiteUnstable, Object[] arguments, int runtimeContextArgCount) {
+        super(callSiteDescriptor, callSiteToken, callSiteUnstable, arguments);
         if(runtimeContextArgCount < 1) {
             throw new IllegalArgumentException("runtimeContextArgCount < 1");
         }
@@ -121,14 +122,14 @@
         if(contextStrippedRequest == null) {
             contextStrippedRequest =
                     new LinkRequestImpl(CallSiteDescriptorFactory.dropParameterTypes(getCallSiteDescriptor(), 1,
-                            runtimeContextArgCount + 1), isCallSiteUnstable(), getTruncatedArguments());
+                            runtimeContextArgCount + 1), getCallSiteToken(), isCallSiteUnstable(), getTruncatedArguments());
         }
         return contextStrippedRequest;
     }
 
     @Override
     public LinkRequest replaceArguments(CallSiteDescriptor callSiteDescriptor, Object[] arguments) {
-        return new RuntimeContextLinkRequestImpl(callSiteDescriptor, isCallSiteUnstable(), arguments,
+        return new RuntimeContextLinkRequestImpl(callSiteDescriptor, getCallSiteToken(), isCallSiteUnstable(), arguments,
                 runtimeContextArgCount);
     }
 
--- a/src/jdk/internal/dynalink/support/TypeUtilities.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/src/jdk/internal/dynalink/support/TypeUtilities.java	Wed Feb 26 13:17:57 2014 +0100
@@ -106,38 +106,49 @@
     }
 
     /**
-     * Given two types represented by c1 and c2, returns a type that is their most specific common superclass or
-     * superinterface.
+     * Given two types represented by c1 and c2, returns a type that is their most specific common supertype for
+     * purposes of lossless conversions.
      *
      * @param c1 one type
      * @param c2 another type
-     * @return their most common superclass or superinterface. If they have several unrelated superinterfaces as their
-     * most specific common type, or the types themselves are completely unrelated interfaces, {@link java.lang.Object}
-     * is returned.
+     * @return their most common superclass or superinterface for purposes of lossless conversions. If they have several
+     * unrelated superinterfaces as their most specific common type, or the types themselves are completely
+     * unrelated interfaces, {@link java.lang.Object} is returned.
      */
-    public static Class<?> getMostSpecificCommonType(Class<?> c1, Class<?> c2) {
+    public static Class<?> getCommonLosslessConversionType(Class<?> c1, Class<?> c2) {
         if(c1 == c2) {
             return c1;
+        } else if(isConvertibleWithoutLoss(c2, c1)) {
+            return c1;
+        } else if(isConvertibleWithoutLoss(c1, c2)) {
+            return c2;
         }
-        Class<?> c3 = c2;
-        if(c3.isPrimitive()) {
-            if(c3 == Byte.TYPE)
-                c3 = Byte.class;
-            else if(c3 == Short.TYPE)
-                c3 = Short.class;
-            else if(c3 == Character.TYPE)
-                c3 = Character.class;
-            else if(c3 == Integer.TYPE)
-                c3 = Integer.class;
-            else if(c3 == Float.TYPE)
-                c3 = Float.class;
-            else if(c3 == Long.TYPE)
-                c3 = Long.class;
-            else if(c3 == Double.TYPE)
-                c3 = Double.class;
+        if(c1 == void.class) {
+            return c2;
+        } else if(c2 == void.class) {
+            return c1;
         }
-        Set<Class<?>> a1 = getAssignables(c1, c3);
-        Set<Class<?>> a2 = getAssignables(c3, c1);
+        if(c1.isPrimitive() && c2.isPrimitive()) {
+            if((c1 == byte.class && c2 == char.class) || (c1 == char.class && c2 == byte.class)) {
+                // byte + char = int
+                return int.class;
+            } else if((c1 == short.class && c2 == char.class) || (c1 == char.class && c2 == short.class)) {
+                // short + char = int
+                return int.class;
+            } else if((c1 == int.class && c2 == float.class) || (c1 == float.class && c2 == int.class)) {
+                // int + float = double
+                return double.class;
+            }
+        }
+        // For all other cases. This will handle long + (float|double) = Number case as well as boolean + anything = Object case too.
+        return getMostSpecificCommonTypeUnequalNonprimitives(c1, c2);
+    }
+
+    private static Class<?> getMostSpecificCommonTypeUnequalNonprimitives(Class<?> c1, Class<?> c2) {
+        final Class<?> npc1 = c1.isPrimitive() ? getWrapperType(c1) : c1;
+        final Class<?> npc2 = c2.isPrimitive() ? getWrapperType(c2) : c2;
+        Set<Class<?>> a1 = getAssignables(npc1, npc2);
+        Set<Class<?>> a2 = getAssignables(npc2, npc1);
         a1.retainAll(a2);
         if(a1.isEmpty()) {
             // Can happen when at least one of the arguments is an interface,
@@ -168,7 +179,7 @@
             max.add(clazz);
         }
         if(max.size() > 1) {
-            return OBJECT_CLASS;
+            return Object.class;
         }
         return max.get(0);
     }
@@ -232,25 +243,60 @@
      * {@link #isSubtype(Class, Class)}) as well as boxing conversion (JLS 5.1.7) optionally followed by widening
      * reference conversion and unboxing conversion (JLS 5.1.8) optionally followed by widening primitive conversion.
      *
-     * @param callSiteType the parameter type at the call site
-     * @param methodType the parameter type in the method declaration
-     * @return true if callSiteType is method invocation convertible to the methodType.
+     * @param sourceType the type being converted from (call site type for parameter types, method type for return types)
+     * @param targetType the parameter type being converted to (method type for parameter types, call site type for return types)
+     * @return true if source type is method invocation convertible to target type.
      */
-    public static boolean isMethodInvocationConvertible(Class<?> callSiteType, Class<?> methodType) {
-        if(methodType.isAssignableFrom(callSiteType)) {
+    public static boolean isMethodInvocationConvertible(Class<?> sourceType, Class<?> targetType) {
+        if(targetType.isAssignableFrom(sourceType)) {
             return true;
         }
-        if(callSiteType.isPrimitive()) {
-            if(methodType.isPrimitive()) {
-                return isProperPrimitiveSubtype(callSiteType, methodType);
+        if(sourceType.isPrimitive()) {
+            if(targetType.isPrimitive()) {
+                return isProperPrimitiveSubtype(sourceType, targetType);
             }
             // Boxing + widening reference conversion
-            return methodType.isAssignableFrom(WRAPPER_TYPES.get(callSiteType));
+            assert WRAPPER_TYPES.get(sourceType) != null : sourceType.getName();
+            return targetType.isAssignableFrom(WRAPPER_TYPES.get(sourceType));
         }
-        if(methodType.isPrimitive()) {
-            final Class<?> unboxedCallSiteType = PRIMITIVE_TYPES.get(callSiteType);
+        if(targetType.isPrimitive()) {
+            final Class<?> unboxedCallSiteType = PRIMITIVE_TYPES.get(sourceType);
             return unboxedCallSiteType != null
-                    && (unboxedCallSiteType == methodType || isProperPrimitiveSubtype(unboxedCallSiteType, methodType));
+                    && (unboxedCallSiteType == targetType || isProperPrimitiveSubtype(unboxedCallSiteType, targetType));
+        }
+        return false;
+    }
+
+    /**
+     * Determines whether a type can be converted to another without losing any
+     * precision.
+     *
+     * @param sourceType the source type
+     * @param targetType the target type
+     * @return true if lossess conversion is possible
+     */
+    public static boolean isConvertibleWithoutLoss(Class<?> sourceType, Class<?> targetType) {
+        if(targetType.isAssignableFrom(sourceType)) {
+            return true;
+        }
+        if(sourceType.isPrimitive()) {
+            if(sourceType == void.class) {
+                return true; // Void can be losslessly represented by any type
+            }
+            if(targetType.isPrimitive()) {
+                return isProperPrimitiveLosslessSubtype(sourceType, targetType);
+            }
+            // Boxing + widening reference conversion
+            assert WRAPPER_TYPES.get(sourceType) != null : sourceType.getName();
+            return targetType.isAssignableFrom(WRAPPER_TYPES.get(sourceType));
+        }
+        if(targetType.isPrimitive()) {
+            if(targetType == void.class) {
+                return false; // Void can't represent anything losslessly
+            }
+            final Class<?> unboxedCallSiteType = PRIMITIVE_TYPES.get(sourceType);
+            return unboxedCallSiteType != null
+                    && (unboxedCallSiteType == targetType || isProperPrimitiveLosslessSubtype(unboxedCallSiteType, targetType));
         }
         return false;
     }
@@ -266,7 +312,7 @@
      */
     public static boolean isPotentiallyConvertible(Class<?> callSiteType, Class<?> methodType) {
         // Widening or narrowing reference conversion
-        if(methodType.isAssignableFrom(callSiteType) || callSiteType.isAssignableFrom(methodType)) {
+        if(areAssignable(callSiteType, methodType)) {
             return true;
         }
         if(callSiteType.isPrimitive()) {
@@ -287,6 +333,16 @@
     }
 
     /**
+     * Returns true if either of the types is assignable from the other.
+     * @param c1 one of the types
+     * @param c2 another one of the types
+     * @return true if either c1 is assignable from c2 or c2 is assignable from c1.
+     */
+    public static boolean areAssignable(Class<?> c1, Class<?> c2) {
+        return c1.isAssignableFrom(c2) || c2.isAssignableFrom(c1);
+    }
+
+    /**
      * Determines whether one type is a subtype of another type, as per JLS 4.10 "Subtyping". Note: this is not strict
      * or proper subtype, therefore true is also returned for identical types; to be completely precise, it allows
      * identity conversion (JLS 5.1.1), widening primitive conversion (JLS 5.1.2) and widening reference conversion (JLS
@@ -353,6 +409,37 @@
         return false;
     }
 
+    /**
+     * Similar to {@link #isProperPrimitiveSubtype(Class, Class)}, except it disallows conversions from int and long to
+     * float, and from long to double, as those can lose precision. It also disallows conversion from and to char and
+     * anything else (similar to boolean) as char is not meant to be an arithmetic type.
+     * @param subType the supposed subtype
+     * @param superType the supposed supertype
+     * @return true if subType is a proper (not identical to) primitive subtype of the superType that can be represented
+     * by the supertype without no precision loss.
+     */
+    private static boolean isProperPrimitiveLosslessSubtype(Class<?> subType, Class<?> superType) {
+        if(superType == boolean.class || subType == boolean.class) {
+            return false;
+        }
+        if(superType == char.class || subType == char.class) {
+            return false;
+        }
+        if(subType == byte.class) {
+            return true;
+        }
+        if(subType == short.class) {
+            return superType != byte.class;
+        }
+        if(subType == int.class) {
+            return superType == long.class || superType == double.class;
+        }
+        if(subType == float.class) {
+            return superType == double.class;
+        }
+        return false;
+    }
+
     private static final Map<Class<?>, Class<?>> WRAPPER_TO_PRIMITIVE_TYPES = createWrapperToPrimitiveTypes();
 
     private static Map<Class<?>, Class<?>> createWrapperToPrimitiveTypes() {
--- a/src/jdk/nashorn/api/scripting/JSObject.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/src/jdk/nashorn/api/scripting/JSObject.java	Wed Feb 26 13:17:57 2014 +0100
@@ -26,7 +26,6 @@
 package jdk.nashorn.api.scripting;
 
 import java.util.Collection;
-import java.util.Collections;
 import java.util.Set;
 
 /**
--- a/src/jdk/nashorn/api/scripting/NashornException.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/src/jdk/nashorn/api/scripting/NashornException.java	Wed Feb 26 13:17:57 2014 +0100
@@ -182,7 +182,7 @@
             if (ECMAErrors.isScriptFrame(st)) {
                 final String className = "<" + st.getFileName() + ">";
                 String methodName = st.getMethodName();
-                if (methodName.equals(CompilerConstants.RUN_SCRIPT.symbolName())) {
+                if (methodName.equals(CompilerConstants.PROGRAM.symbolName())) {
                     methodName = "<program>";
                 }
 
@@ -224,10 +224,22 @@
         return buf.toString();
     }
 
+    /**
+     * Get the thrown object. Subclass responsibility
+     * @return thrown object
+     */
     protected Object getThrown() {
         return null;
     }
 
+    /**
+     * Initialization function for ECMA errors. Stores the error
+     * in the ecmaError field of this class. It is only initialized
+     * once, and then reused
+     *
+     * @param global the global
+     * @return initialized exception
+     */
     protected NashornException initEcmaError(final ScriptObject global) {
         if (ecmaError != null) {
             return this; // initialized already!
--- a/src/jdk/nashorn/api/scripting/NashornScriptEngine.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/src/jdk/nashorn/api/scripting/NashornScriptEngine.java	Wed Feb 26 13:17:57 2014 +0100
@@ -61,6 +61,7 @@
 import jdk.nashorn.internal.runtime.ErrorManager;
 import jdk.nashorn.internal.runtime.GlobalObject;
 import jdk.nashorn.internal.runtime.Property;
+import jdk.nashorn.internal.runtime.ScriptEnvironment;
 import jdk.nashorn.internal.runtime.ScriptFunction;
 import jdk.nashorn.internal.runtime.ScriptObject;
 import jdk.nashorn.internal.runtime.ScriptRuntime;
@@ -463,7 +464,7 @@
     private void setContextVariables(final ScriptObject ctxtGlobal, final ScriptContext ctxt) {
         // set "context" global variable via contextProperty - because this
         // property is non-writable
-        contextProperty.setObjectValue(ctxtGlobal, ctxtGlobal, ctxt, false);
+        contextProperty.setValue(ctxtGlobal, ctxtGlobal, ctxt, false);
         Object args = ScriptObjectMirror.unwrap(ctxt.getAttribute("arguments"), ctxtGlobal);
         if (args == null || args == UNDEFINED) {
             args = ScriptRuntime.EMPTY_ARRAY;
@@ -598,6 +599,15 @@
         };
     }
 
+    /**
+     * Check if the global script environment tells us to do optimistic
+     * compilation
+     * @return true if optimistic compilation enabled
+     */
+    public static boolean isOptimistic() {
+        return ScriptEnvironment.globalOptimistic();
+    }
+
     private ScriptFunction compileImpl(final Source source, final ScriptContext ctxt) throws ScriptException {
         return compileImpl(source, getNashornGlobalFrom(ctxt));
     }
--- a/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java	Wed Feb 26 13:17:57 2014 +0100
@@ -169,6 +169,12 @@
         });
     }
 
+    /**
+     * Call member function
+     * @param functionName function name
+     * @param args         arguments
+     * @return return value of function
+     */
     public Object callMember(final String functionName, final Object... args) {
         functionName.getClass(); // null check
         final ScriptObject oldGlobal = Context.getGlobal();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/IntDeque.java	Wed Feb 26 13:17:57 2014 +0100
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal;
+
+/**
+ * Small helper class for fast int deques
+ */
+public class IntDeque {
+    private int[] deque = new int[16];
+    private int nextFree = 0;
+
+    /**
+     * Push an int value
+     * @param value value
+     */
+    public void push(final int value) {
+        if (nextFree == deque.length) {
+            final int[] newDeque = new int[nextFree * 2];
+            System.arraycopy(deque, 0, newDeque, 0, nextFree);
+            deque = newDeque;
+        }
+        deque[nextFree++] = value;
+    }
+
+    /**
+     * Pop an int value
+     * @return value
+     */
+    public int pop() {
+        return deque[--nextFree];
+    }
+
+    /**
+     * Peek
+     * @return top value
+     */
+    public int peek() {
+        return deque[nextFree - 1];
+    }
+
+    /**
+     * Get the value of the top element and increment it.
+     * @return top value
+     */
+    public int getAndIncrement() {
+        return deque[nextFree - 1]++;
+    }
+
+    /**
+     * Decrement the value of the top element and return it.
+     * @return decremented top value
+     */
+    public int decrementAndGet() {
+        return --deque[nextFree - 1];
+    }
+
+    /**
+     * Check if deque is empty
+     * @return true if empty
+     */
+    public boolean isEmpty() {
+        return nextFree == 0;
+    }
+}
--- a/src/jdk/nashorn/internal/codegen/Attr.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/src/jdk/nashorn/internal/codegen/Attr.java	Wed Feb 26 13:17:57 2014 +0100
@@ -43,6 +43,7 @@
 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_PROGRAM_LEVEL;
 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;
@@ -63,10 +64,13 @@
 import jdk.nashorn.internal.ir.CaseNode;
 import jdk.nashorn.internal.ir.CatchNode;
 import jdk.nashorn.internal.ir.Expression;
+import jdk.nashorn.internal.ir.ExpressionStatement;
 import jdk.nashorn.internal.ir.ForNode;
+import jdk.nashorn.internal.ir.FunctionCall;
 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.LexicalContextNode;
@@ -74,9 +78,12 @@
 import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
 import jdk.nashorn.internal.ir.Node;
 import jdk.nashorn.internal.ir.ObjectNode;
+import jdk.nashorn.internal.ir.Optimistic;
+import jdk.nashorn.internal.ir.OptimisticLexicalContext;
 import jdk.nashorn.internal.ir.ReturnNode;
 import jdk.nashorn.internal.ir.RuntimeNode;
 import jdk.nashorn.internal.ir.RuntimeNode.Request;
+import jdk.nashorn.internal.ir.SplitNode;
 import jdk.nashorn.internal.ir.Statement;
 import jdk.nashorn.internal.ir.SwitchNode;
 import jdk.nashorn.internal.ir.Symbol;
@@ -85,6 +92,7 @@
 import jdk.nashorn.internal.ir.TryNode;
 import jdk.nashorn.internal.ir.UnaryNode;
 import jdk.nashorn.internal.ir.VarNode;
+import jdk.nashorn.internal.ir.WhileNode;
 import jdk.nashorn.internal.ir.WithNode;
 import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
@@ -111,7 +119,9 @@
  * computed.
  */
 
-final class Attr extends NodeOperatorVisitor<LexicalContext> {
+final class Attr extends NodeOperatorVisitor<OptimisticLexicalContext> {
+
+    private final CompilationEnvironment env;
 
     /**
      * Local definitions in current block (to discriminate from function
@@ -127,7 +137,8 @@
      */
     private final Deque<Set<String>> localUses;
 
-    private final Deque<Type> returnTypes;
+    private final Set<Long> optimistic = new HashSet<>();
+    private final Set<Long> neverOptimistic = new HashSet<>();
 
     private int catchNestingLevel;
 
@@ -139,12 +150,12 @@
     /**
      * Constructor.
      */
-    Attr(final TemporarySymbols temporarySymbols) {
-        super(new LexicalContext());
+    Attr(final CompilationEnvironment env, final TemporarySymbols temporarySymbols) {
+        super(new OptimisticLexicalContext(env.useOptimisticTypes()));
+        this.env              = env;
         this.temporarySymbols = temporarySymbols;
-        this.localDefs   = new ArrayDeque<>();
-        this.localUses   = new ArrayDeque<>();
-        this.returnTypes = new ArrayDeque<>();
+        this.localDefs        = new ArrayDeque<>();
+        this.localUses        = new ArrayDeque<>();
     }
 
     @Override
@@ -159,10 +170,7 @@
 
     @Override
     public Node leaveAccessNode(final AccessNode accessNode) {
-        //While Object type is assigned here, Access Specialization in FinalizeTypes may narrow this, that
-        //is why we can't set the access node base to be an object here, that will ruin access specialization
-        //for example for a.x | 17.
-        return end(ensureSymbol(Type.OBJECT, accessNode));
+        return end(ensureSymbolTypeOverride(accessNode, Type.OBJECT));
     }
 
     private void initFunctionWideVariables(final FunctionNode functionNode, final Block body) {
@@ -235,6 +243,11 @@
 
             @Override
             public boolean enterVarNode(final VarNode varNode) {
+                final Expression init = varNode.getInit();
+                if (init != null) {
+                    tagOptimistic(init);
+                }
+
                 final String name = varNode.getName().getName();
                 //if this is used before the var node, the var node symbol needs to be tagged as can be undefined
                 if (uses.contains(name)) {
@@ -264,7 +277,7 @@
                 if (varNode.isStatement()) {
                     final IdentNode ident  = varNode.getName();
                     final Symbol    symbol = defineSymbol(body, ident.getName(), IS_VAR);
-                    if (canBeUndefined.contains(ident.getName())) {
+                    if (canBeUndefined.contains(ident.getName()) && varNode.getInit() == null) {
                         symbol.setType(Type.OBJECT);
                         symbol.setCanBeUndefined();
                     }
@@ -299,6 +312,9 @@
             if (!(anonymous || body.getExistingSymbol(name) != null)) {
                 assert !anonymous && name != null;
                 newType(defineSymbol(body, name, IS_VAR | IS_FUNCTION_SELF), Type.OBJECT);
+                if(functionNode.allVarsInScope()) { // basically, has deep eval
+                    lc.setFlag(body, Block.USES_SELF_SYMBOL);
+                }
             }
         }
 
@@ -326,14 +342,24 @@
         return end(block);
     }
 
+    private boolean useOptimisticTypes() {
+        return env.useOptimisticTypes() && !lc.isInSplitNode();
+    }
+
     @Override
     public boolean enterCallNode(final CallNode callNode) {
-        return start(callNode);
+        for (final Expression arg : callNode.getArgs()) {
+            tagOptimistic(arg);
+        }
+        return true;
     }
 
     @Override
     public Node leaveCallNode(final CallNode callNode) {
-        return end(ensureSymbol(callNode.getType(), callNode));
+        for (final Expression arg : callNode.getArgs()) {
+            inferParameter(arg, arg.getType());
+        }
+        return end(ensureSymbolTypeOverride(callNode, Type.OBJECT));
     }
 
     @Override
@@ -346,8 +372,13 @@
 
         // define block-local exception variable
         final String exname = exception.getName();
-        final Symbol def = defineSymbol(block, exname, IS_VAR | IS_LET | IS_ALWAYS_DEFINED);
-        newType(def, Type.OBJECT); //we can catch anything, not just ecma exceptions
+        // If the name of the exception starts with ":e", this is a synthetic catch block, likely a catch-all. Its
+        // symbol is naturally internal, and should be treated as such.
+        final boolean isInternal = exname.startsWith(EXCEPTION_PREFIX.symbolName());
+        final Symbol def = defineSymbol(block, exname, IS_VAR | IS_LET | IS_ALWAYS_DEFINED | (isInternal ? IS_INTERNAL : 0));
+        // Normally, we can catch anything, not just ECMAExceptions, hence Type.OBJECT. However, for catches with
+        // internal symbol, we can be sure the caught type is a Throwable.
+        newType(def, isInternal ? Type.typeFor(EXCEPTION_PREFIX.type()) : Type.OBJECT);
 
         addLocalDef(exname);
 
@@ -382,6 +413,10 @@
             flags |= IS_SCOPE;
         }
 
+        if (lc.getCurrentFunction().isProgram()) {
+            flags |= IS_PROGRAM_LEVEL;
+        }
+
         final FunctionNode function = lc.getFunction(block);
         if (symbol != null) {
             // Symbol was already defined. Check if it needs to be redefined.
@@ -433,14 +468,19 @@
     }
 
     @Override
+    public boolean enterExpressionStatement(final ExpressionStatement expressionStatement) {
+        final Expression expr = expressionStatement.getExpression();
+        if (!expr.isSelfModifying()) { //self modifying ops like i++ need the optimistic type for their own operation, not just the return value, as there is no difference. gah.
+            tagNeverOptimistic(expr);
+        }
+        return true;
+    }
+
+    @Override
     public boolean enterFunctionNode(final FunctionNode functionNode) {
         start(functionNode, false);
 
-        if (functionNode.isLazy()) {
-            return false;
-        }
-
-        //an outermost function in our lexical context that is not a program (runScript)
+        //an outermost function in our lexical context that is not a program
         //is possible - it is a function being compiled lazily
         if (functionNode.isDeclared()) {
             final Iterator<Block> blocks = lc.getBlocks();
@@ -449,7 +489,6 @@
             }
         }
 
-        returnTypes.push(functionNode.getReturnType());
         pushLocalsFunction();
 
         return true;
@@ -471,7 +510,7 @@
             final boolean anonymous = functionNode.isAnonymous();
             final String  name      = anonymous ? null : functionNode.getIdent().getName();
             if (anonymous || body.getExistingSymbol(name) != null) {
-                newFunctionNode = (FunctionNode)ensureSymbol(FunctionNode.FUNCTION_TYPE, newFunctionNode);
+                newFunctionNode = (FunctionNode)ensureSymbol(newFunctionNode, FunctionNode.FUNCTION_TYPE);
             } else {
                 assert name != null;
                 final Symbol self = body.getExistingSymbol(name);
@@ -480,11 +519,6 @@
             }
         }
 
-        //unknown parameters are promoted to object type.
-        if (newFunctionNode.hasLazyChildren()) {
-            //the final body has already been assigned as we have left the function node block body by now
-            objectifySymbols(body);
-        }
         newFunctionNode = finalizeParameters(newFunctionNode);
         newFunctionNode = finalizeTypes(newFunctionNode);
         for (final Symbol symbol : newFunctionNode.getDeclaredSymbols()) {
@@ -496,7 +530,7 @@
 
         List<VarNode> syntheticInitializers = null;
 
-        if (body.getFlag(Block.NEEDS_SELF_SYMBOL)) {
+        if (newFunctionNode.usesSelfSymbol()) {
             syntheticInitializers = new ArrayList<>(2);
             LOG.info("Accepting self symbol init for ", newFunctionNode.getName());
             // "var fn = :callee"
@@ -520,19 +554,12 @@
             newFunctionNode = newFunctionNode.setBody(lc, newFunctionNode.getBody().setStatements(lc, newStatements));
         }
 
-        if (returnTypes.peek().isUnknown()) {
-            LOG.info("Unknown return type promoted to object");
-            newFunctionNode = newFunctionNode.setReturnType(lc, Type.OBJECT);
-        }
-        final Type returnType = returnTypes.pop();
-        newFunctionNode = newFunctionNode.setReturnType(lc, returnType.isUnknown() ? Type.OBJECT : returnType);
-        newFunctionNode = newFunctionNode.setState(lc, CompilationState.ATTR);
+        final int optimisticFlag = lc.hasOptimisticAssumptions() ? FunctionNode.IS_OPTIMISTIC : 0;
+        newFunctionNode = newFunctionNode.setState(lc, CompilationState.ATTR).setFlag(lc, optimisticFlag);
 
         popLocals();
 
-        end(newFunctionNode, false);
-
-        return newFunctionNode;
+        return end(newFunctionNode, false);
     }
 
     /**
@@ -580,9 +607,9 @@
                 final FunctionNode functionNode = lc.getDefiningFunction(symbol);
                 assert functionNode != null;
                 assert lc.getFunctionBody(functionNode).getExistingSymbol(CALLEE.symbolName()) != null;
-                lc.setFlag(functionNode.getBody(), Block.NEEDS_SELF_SYMBOL);
+                lc.setFlag(functionNode.getBody(), Block.USES_SELF_SYMBOL);
                 newType(symbol, FunctionNode.FUNCTION_TYPE);
-            } else if (!identNode.isInitializedHere()) {
+            } else if (!(identNode.isInitializedHere() || symbol.isAlwaysDefined())) {
                 /*
                  * See NASHORN-448, JDK-8016235
                  *
@@ -617,8 +644,12 @@
             symbol.increaseUseCount();
         }
         addLocalUse(identNode.getName());
+        IdentNode node = (IdentNode)identNode.setSymbol(lc, symbol);
+        if (isTaggedOptimistic(identNode) && symbol.isScope()) {
+            node = ensureSymbolTypeOverride(node, symbol.getSymbolType());
+        }
 
-        return end(identNode.setSymbol(lc, symbol));
+        return end(node);
     }
 
     private boolean inCatch() {
@@ -636,16 +667,21 @@
         }
     }
 
-    private boolean symbolNeedsToBeScope(Symbol symbol) {
+    private boolean symbolNeedsToBeScope(final Symbol symbol) {
         if (symbol.isThis() || symbol.isInternal()) {
             return false;
         }
+
+        if (lc.getCurrentFunction().allVarsInScope()) {
+            return true;
+        }
+
         boolean previousWasBlock = false;
         for (final Iterator<LexicalContextNode> it = lc.getAllNodes(); it.hasNext();) {
             final LexicalContextNode node = it.next();
-            if (node instanceof FunctionNode) {
-                // We reached the function boundary without seeing a definition for the symbol - it needs to be in
-                // scope.
+            if (node instanceof FunctionNode || node instanceof SplitNode) {
+                // We reached the function boundary or a splitting boundary without seeing a definition for the symbol.
+                // It needs to be in scope.
                 return true;
             } else if (node instanceof WithNode) {
                 if (previousWasBlock) {
@@ -729,7 +765,8 @@
 
     @Override
     public Node leaveIndexNode(final IndexNode indexNode) {
-        return end(ensureSymbol(Type.OBJECT, indexNode));
+       // return end(ensureSymbolOptimistic(Type.OBJECT, indexNode));
+        return end(ensureSymbolTypeOverride(indexNode, Type.OBJECT));
     }
 
     @SuppressWarnings("rawtypes")
@@ -751,31 +788,7 @@
 
     @Override
     public Node leaveObjectNode(final ObjectNode objectNode) {
-        return end(ensureSymbol(Type.OBJECT, objectNode));
-    }
-
-    @Override
-    public Node leaveReturnNode(final ReturnNode returnNode) {
-        final Expression expr = returnNode.getExpression();
-        final Type returnType;
-
-        if (expr != null) {
-            //we can't do parameter specialization if we return something that hasn't been typed yet
-            final Symbol symbol = expr.getSymbol();
-            if (expr.getType().isUnknown() && symbol.isParam()) {
-                symbol.setType(Type.OBJECT);
-            }
-
-            returnType = widestReturnType(returnTypes.pop(), symbol.getSymbolType());
-        } else {
-            returnType = Type.OBJECT; //undefined
-        }
-        LOG.info("Returntype is now ", returnType);
-        returnTypes.push(returnType);
-
-        end(returnNode);
-
-        return returnNode;
+        return end(ensureSymbol(objectNode, Type.OBJECT));
     }
 
     @Override
@@ -819,9 +832,7 @@
 
         switchNode.setTag(newInternal(lc.getCurrentFunction().uniqueName(SWITCH_TAG_PREFIX.symbolName()), type));
 
-        end(switchNode);
-
-        return switchNode.setCases(lc, newCases);
+        return end(switchNode.setCases(lc, newCases));
     }
 
     @Override
@@ -848,7 +859,10 @@
         assert symbol != null;
 
         // NASHORN-467 - use before definition of vars - conservative
-        if (isLocalUse(ident.getName())) {
+        //function each(iterator, context) {
+        //  for (var i = 0, length = this.length >>> 0; i < length; i++) { if (i in this) iterator.call(context, this[i], i, this); }
+        //
+        if (isLocalUse(ident.getName()) && varNode.getInit() == null) {
             newType(symbol, Type.OBJECT);
             symbol.setCanBeUndefined();
         }
@@ -894,29 +908,55 @@
     }
 
     @Override
+    public boolean enterNOT(UnaryNode unaryNode) {
+        tagNeverOptimistic(unaryNode.getExpression());
+        return true;
+    }
+
+    public boolean enterUnaryArithmetic(final UnaryNode unaryNode) {
+        tagOptimistic(unaryNode.getExpression());
+        return true;
+    }
+
+    private UnaryNode leaveUnaryArithmetic(final UnaryNode unaryNode) {
+        return end(coerce(unaryNode, unaryNode.getMostPessimisticType(), unaryNode.getExpression().getType()));
+    }
+
+    @Override
+    public boolean enterADD(final UnaryNode unaryNode) {
+        return enterUnaryArithmetic(unaryNode);
+    }
+
+    @Override
     public Node leaveADD(final UnaryNode unaryNode) {
-        return end(ensureSymbol(arithType(), unaryNode));
+        return leaveUnaryArithmetic(unaryNode);
     }
 
     @Override
     public Node leaveBIT_NOT(final UnaryNode unaryNode) {
-        return end(ensureSymbol(Type.INT, unaryNode));
+        return end(coerce(unaryNode, Type.INT));
+    }
+
+    @Override
+    public boolean enterDECINC(final UnaryNode unaryNode) {
+        return enterUnaryArithmetic(unaryNode);
     }
 
     @Override
     public Node leaveDECINC(final UnaryNode unaryNode) {
         // @see assignOffset
-        final Type type = arithType();
-        newType(unaryNode.rhs().getSymbol(), type);
-        return end(ensureSymbol(type, unaryNode));
+        final Type pessimisticType = unaryNode.getMostPessimisticType();
+        final UnaryNode newUnaryNode = ensureSymbolTypeOverride(unaryNode, pessimisticType, unaryNode.getExpression().getType());
+        newType(newUnaryNode.getExpression().getSymbol(), newUnaryNode.getType());
+        return end(newUnaryNode);
     }
 
     @Override
     public Node leaveDELETE(final UnaryNode unaryNode) {
-        final FunctionNode   currentFunctionNode = lc.getCurrentFunction();
-        final boolean        strictMode          = currentFunctionNode.isStrict();
-        final Expression     rhs                 = unaryNode.rhs();
-        final Expression     strictFlagNode      = (Expression)LiteralNode.newInstance(unaryNode, strictMode).accept(this);
+        final FunctionNode currentFunctionNode = lc.getCurrentFunction();
+        final boolean      strictMode          = currentFunctionNode.isStrict();
+        final Expression   rhs                 = unaryNode.getExpression();
+        final Expression   strictFlagNode      = (Expression)LiteralNode.newInstance(unaryNode, strictMode).accept(this);
 
         Request request = Request.DELETE;
         final List<Expression> args = new ArrayList<>();
@@ -925,7 +965,10 @@
             // If this is a declared variable or a function parameter, delete always fails (except for globals).
             final String name = ((IdentNode)rhs).getName();
 
-            final boolean failDelete = strictMode || rhs.getSymbol().isParam() || (rhs.getSymbol().isVar() && !isProgramLevelSymbol(name));
+            final boolean isParam         = rhs.getSymbol().isParam();
+            final boolean isVar           = rhs.getSymbol().isVar();
+            final boolean isNonProgramVar = isVar && !rhs.getSymbol().isProgramLevel();
+            final boolean failDelete      = strictMode || isParam || isNonProgramVar;
 
             if (failDelete && rhs.getSymbol().isThis()) {
                 return LiteralNode.newInstance(unaryNode, true).accept(this);
@@ -968,29 +1011,14 @@
         return leaveRuntimeNode(runtimeNode);
     }
 
-    /**
-     * Is the symbol denoted by the specified name in the current lexical context defined in the program level
-     * @param name the name of the symbol
-     * @return true if the symbol denoted by the specified name in the current lexical context defined in the program level.
-     */
-    private boolean isProgramLevelSymbol(final String name) {
-        for(final Iterator<Block> it = lc.getBlocks(); it.hasNext();) {
-            final Block next = it.next();
-            if(next.getExistingSymbol(name) != null) {
-                return next == lc.getFunctionBody(lc.getOutermostFunction());
-            }
-        }
-        throw new AssertionError("Couldn't find symbol " + name + " in the context");
-    }
-
     @Override
     public Node leaveNEW(final UnaryNode unaryNode) {
-        return end(ensureSymbol(Type.OBJECT, unaryNode.setRHS(((CallNode)unaryNode.rhs()).setIsNew())));
+        return end(coerce(unaryNode.setExpression(((CallNode)unaryNode.getExpression()).setIsNew()), Type.OBJECT));
     }
 
     @Override
     public Node leaveNOT(final UnaryNode unaryNode) {
-        return end(ensureSymbol(Type.BOOLEAN, unaryNode));
+        return end(coerce(unaryNode, Type.BOOLEAN));
     }
 
     private IdentNode compilerConstant(CompilerConstants cc) {
@@ -1010,7 +1038,7 @@
 
     @Override
     public Node leaveTYPEOF(final UnaryNode unaryNode) {
-        final Expression rhs = unaryNode.rhs();
+        final Expression rhs = unaryNode.getExpression();
 
         List<Expression> args = new ArrayList<>();
         if (rhs instanceof IdentNode && !rhs.getSymbol().isParam() && !rhs.getSymbol().isVar()) {
@@ -1033,17 +1061,29 @@
 
     @Override
     public Node leaveRuntimeNode(final RuntimeNode runtimeNode) {
-        return end(ensureSymbol(runtimeNode.getRequest().getReturnType(), runtimeNode));
+        return end(ensureSymbol(runtimeNode, runtimeNode.getRequest().getReturnType()));
+    }
+
+    @Override
+    public boolean enterSUB(final UnaryNode unaryNode) {
+        return enterUnaryArithmetic(unaryNode);
     }
 
     @Override
     public Node leaveSUB(final UnaryNode unaryNode) {
-        return end(ensureSymbol(arithType(), unaryNode));
+        return leaveUnaryArithmetic(unaryNode);
     }
 
     @Override
     public Node leaveVOID(final UnaryNode unaryNode) {
-        return end(ensureSymbol(Type.OBJECT, unaryNode));
+        return end(ensureSymbol(unaryNode, Type.OBJECT));
+    }
+
+    @Override
+    public boolean enterADD(final BinaryNode binaryNode) {
+        tagOptimistic(binaryNode.lhs());
+        tagOptimistic(binaryNode.rhs());
+        return true;
     }
 
     /**
@@ -1055,18 +1095,22 @@
         final Expression lhs = binaryNode.lhs();
         final Expression rhs = binaryNode.rhs();
 
-        ensureTypeNotUnknown(lhs);
-        ensureTypeNotUnknown(rhs);
-        //even if we are adding two known types, this can overflow. i.e.
-        //int and number -> number.
-        //int and int are also number though.
-        //something and object is object
-        return end(ensureSymbol(Type.widest(arithType(), Type.widest(lhs.getType(), rhs.getType())), binaryNode));
+        //an add is at least as wide as the current arithmetic type, possibly wider in the case of objects
+        //which will be corrected in the post pass if unknown at this stage
+
+        Type argumentsType = Type.widest(lhs.getType(), rhs.getType());
+        if(argumentsType.getTypeClass() == String.class) {
+            assert binaryNode.isTokenType(TokenType.ADD);
+            argumentsType = Type.OBJECT;
+        }
+        final Type pessimisticType = Type.widest(Type.NUMBER, argumentsType);
+
+        return end(ensureSymbolTypeOverride(binaryNode, pessimisticType, argumentsType));
     }
 
     @Override
     public Node leaveAND(final BinaryNode binaryNode) {
-        return end(ensureSymbol(Type.OBJECT, binaryNode));
+        return end(ensureSymbol(binaryNode, Type.OBJECT));
     }
 
     /**
@@ -1075,11 +1119,18 @@
      */
     private boolean enterAssignmentNode(final BinaryNode binaryNode) {
         start(binaryNode);
+        final Expression lhs = binaryNode.lhs();
+        if (lhs instanceof IdentNode) {
+            if (CompilerConstants.isCompilerConstant(((IdentNode)lhs).getName())) {
+                tagNeverOptimistic(binaryNode.rhs());
+            }
+        }
+        //tagOptimistic(binaryNode.lhs());
+        tagOptimistic(binaryNode.rhs());
 
         return true;
     }
 
-
     /**
      * This assign helper is called after an assignment, when all children of
      * the assign has been processed. It fixes the types and recursively makes
@@ -1096,6 +1147,7 @@
             final Block     block = lc.getCurrentBlock();
             final IdentNode ident = (IdentNode)lhs;
             final String    name  = ident.getName();
+
             final Symbol symbol = findSymbol(block, name);
 
             if (symbol == null) {
@@ -1114,7 +1166,7 @@
         }
 
         newType(lhs.getSymbol(), type);
-        return end(ensureSymbol(type, binaryNode));
+        return end(ensureSymbol(binaryNode, type));
     }
 
     private boolean isLocal(FunctionNode function, Symbol symbol) {
@@ -1140,11 +1192,11 @@
 
     @Override
     public Node leaveASSIGN_ADD(final BinaryNode binaryNode) {
-        final Expression lhs = binaryNode.lhs();
-        final Expression rhs = binaryNode.rhs();
+        final Expression lhs    = binaryNode.lhs();
+        final Expression rhs    = binaryNode.rhs();
+        final Type       widest = Type.widest(lhs.getType(), rhs.getType());
+        //Type.NUMBER if we can't prove that the add doesn't overflow. todo
 
-        final Type widest = Type.widest(lhs.getType(), rhs.getType());
-        //Type.NUMBER if we can't prove that the add doesn't overflow. todo
         return leaveSelfModifyingAssignmentNode(binaryNode, widest.isNumeric() ? Type.NUMBER : Type.OBJECT);
     }
 
@@ -1249,22 +1301,49 @@
     }
 
     @Override
+    public boolean enterBIT_AND(final BinaryNode binaryNode) {
+        return enterBitwiseOperator(binaryNode);
+    }
+
+    @Override
     public Node leaveBIT_AND(final BinaryNode binaryNode) {
-        return end(coerce(binaryNode, Type.INT));
+        return leaveBitwiseOperator(binaryNode);
+    }
+
+    @Override
+    public boolean enterBIT_OR(final BinaryNode binaryNode) {
+        return enterBitwiseOperator(binaryNode);
     }
 
     @Override
     public Node leaveBIT_OR(final BinaryNode binaryNode) {
-        return end(coerce(binaryNode, Type.INT));
+        return leaveBitwiseOperator(binaryNode);
+    }
+
+    @Override
+    public boolean enterBIT_XOR(final BinaryNode binaryNode) {
+        return enterBitwiseOperator(binaryNode);
     }
 
     @Override
     public Node leaveBIT_XOR(final BinaryNode binaryNode) {
+        return leaveBitwiseOperator(binaryNode);
+    }
+
+    public boolean enterBitwiseOperator(final BinaryNode binaryNode) {
+        start(binaryNode);
+        tagOptimistic(binaryNode.lhs());
+        tagOptimistic(binaryNode.rhs());
+        return true;
+    }
+
+    private Node leaveBitwiseOperator(final BinaryNode binaryNode) {
         return end(coerce(binaryNode, Type.INT));
     }
 
     @Override
     public Node leaveCOMMARIGHT(final BinaryNode binaryNode) {
+//        return end(ensureSymbol(binaryNode, binaryNode.rhs().getType()));
         return leaveComma(binaryNode, binaryNode.rhs());
     }
 
@@ -1274,8 +1353,11 @@
     }
 
     private Node leaveComma(final BinaryNode commaNode, final Expression effectiveExpr) {
-        ensureTypeNotUnknown(effectiveExpr);
-        return end(ensureSymbol(effectiveExpr.getType(), commaNode));
+        Type type = effectiveExpr.getType();
+        if (type.isUnknown()) { //TODO more optimistic
+            type = Type.OBJECT;
+        }
+        return end(ensureSymbol(commaNode, type));
     }
 
     @Override
@@ -1284,34 +1366,32 @@
     }
 
     private Node leaveCmp(final BinaryNode binaryNode) {
-        ensureTypeNotUnknown(binaryNode.lhs());
-        ensureTypeNotUnknown(binaryNode.rhs());
-        Type widest = Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType());
-        ensureSymbol(widest, binaryNode.lhs());
-        ensureSymbol(widest, binaryNode.rhs());
-        return end(ensureSymbol(Type.BOOLEAN, binaryNode));
+        //infect untyped comp with opportunistic type from other
+        final Expression lhs = binaryNode.lhs();
+        final Expression rhs = binaryNode.rhs();
+        final Type type = Type.narrowest(lhs.getType(), rhs.getType(), Type.INT);
+        inferParameter(lhs, type);
+        inferParameter(rhs, type);
+        Type widest = Type.widest(lhs.getType(), rhs.getType());
+        ensureSymbol(lhs, widest);
+        ensureSymbol(rhs, widest);
+        return end(ensureSymbol(binaryNode, Type.BOOLEAN));
     }
 
-    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);
-        return ensureSymbol(destType, binaryNode);
-    }
-
-    private Node coerce(final BinaryNode binaryNode, final Type type) {
-        return coerce(binaryNode, type, type);
+    private boolean enterBinaryArithmetic(final BinaryNode binaryNode) {
+        tagOptimistic(binaryNode.lhs());
+        tagOptimistic(binaryNode.rhs());
+        return true;
     }
 
     //leave a binary node and inherit the widest type of lhs , rhs
     private Node leaveBinaryArithmetic(final BinaryNode binaryNode) {
-        assert !Compiler.shouldUseIntegerArithmetic();
-        return end(coerce(binaryNode, Type.NUMBER));
+        return end(coerce(binaryNode, binaryNode.getMostPessimisticType(), Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType())));
+    }
+
+    @Override
+    public boolean enterEQ(BinaryNode binaryNode) {
+        return enterBinaryArithmetic(binaryNode);
     }
 
     @Override
@@ -1320,16 +1400,31 @@
     }
 
     @Override
+    public boolean enterEQ_STRICT(final BinaryNode binaryNode) {
+        return enterBinaryArithmetic(binaryNode);
+    }
+
+    @Override
     public Node leaveEQ_STRICT(final BinaryNode binaryNode) {
         return leaveCmp(binaryNode);
     }
 
     @Override
+    public boolean enterGE(final BinaryNode binaryNode) {
+        return enterBinaryArithmetic(binaryNode);
+    }
+
+    @Override
     public Node leaveGE(final BinaryNode binaryNode) {
         return leaveCmp(binaryNode);
     }
 
     @Override
+    public boolean enterGT(final BinaryNode binaryNode) {
+        return enterBinaryArithmetic(binaryNode);
+    }
+
+    @Override
     public Node leaveGT(final BinaryNode binaryNode) {
         return leaveCmp(binaryNode);
     }
@@ -1354,11 +1449,21 @@
     }
 
     @Override
+    public boolean enterLE(final BinaryNode binaryNode) {
+        return enterBinaryArithmetic(binaryNode);
+    }
+
+    @Override
     public Node leaveLE(final BinaryNode binaryNode) {
         return leaveCmp(binaryNode);
     }
 
     @Override
+    public boolean enterLT(final BinaryNode binaryNode) {
+        return enterBinaryArithmetic(binaryNode);
+    }
+
+    @Override
     public Node leaveLT(final BinaryNode binaryNode) {
         return leaveCmp(binaryNode);
     }
@@ -1369,33 +1474,63 @@
     }
 
     @Override
+    public boolean enterMUL(final BinaryNode binaryNode) {
+        return enterBinaryArithmetic(binaryNode);
+    }
+
+    @Override
     public Node leaveMUL(final BinaryNode binaryNode) {
         return leaveBinaryArithmetic(binaryNode);
     }
 
     @Override
+    public boolean enterNE(final BinaryNode binaryNode) {
+        return enterBinaryArithmetic(binaryNode);
+    }
+
+    @Override
     public Node leaveNE(final BinaryNode binaryNode) {
         return leaveCmp(binaryNode);
     }
 
     @Override
+    public boolean enterNE_STRICT(final BinaryNode binaryNode) {
+        return enterBinaryArithmetic(binaryNode);
+    }
+
+    @Override
     public Node leaveNE_STRICT(final BinaryNode binaryNode) {
         return leaveCmp(binaryNode);
     }
 
     @Override
     public Node leaveOR(final BinaryNode binaryNode) {
-        return end(ensureSymbol(Type.OBJECT, binaryNode));
+        return end(ensureSymbol(binaryNode, Type.OBJECT));
+    }
+
+    @Override
+    public boolean enterSAR(final BinaryNode binaryNode) {
+        return enterBitwiseOperator(binaryNode);
     }
 
     @Override
     public Node leaveSAR(final BinaryNode binaryNode) {
-        return end(coerce(binaryNode, Type.INT));
+        return leaveBitwiseOperator(binaryNode);
+    }
+
+    @Override
+    public boolean enterSHL(final BinaryNode binaryNode) {
+        return enterBitwiseOperator(binaryNode);
     }
 
     @Override
     public Node leaveSHL(final BinaryNode binaryNode) {
-        return end(coerce(binaryNode, Type.INT));
+        return leaveBitwiseOperator(binaryNode);
+    }
+
+    @Override
+    public boolean enterSHR(final BinaryNode binaryNode) {
+        return enterBitwiseOperator(binaryNode);
     }
 
     @Override
@@ -1404,11 +1539,22 @@
     }
 
     @Override
+    public boolean enterSUB(final BinaryNode binaryNode) {
+        return enterBinaryArithmetic(binaryNode);
+    }
+
+    @Override
     public Node leaveSUB(final BinaryNode binaryNode) {
         return leaveBinaryArithmetic(binaryNode);
     }
 
     @Override
+    public boolean enterForNode(ForNode forNode) {
+        tagNeverOptimistic(forNode.getTest());
+        return true;
+    }
+
+    @Override
     public Node leaveForNode(final ForNode forNode) {
         if (forNode.isForIn()) {
             forNode.setIterator(newInternal(lc.getCurrentFunction().uniqueName(ITERATOR_PREFIX.symbolName()), Type.typeFor(ITERATOR_PREFIX.type()))); //NASHORN-73
@@ -1420,54 +1566,59 @@
             newType(forNode.getInit().getSymbol(), Type.OBJECT);
         }
 
-        end(forNode);
+        return end(forNode);
+    }
 
-        return forNode;
+    @Override
+    public boolean enterTernaryNode(TernaryNode ternaryNode) {
+        tagNeverOptimistic(ternaryNode.getTest());
+        return true;
     }
 
     @Override
     public Node leaveTernaryNode(final TernaryNode ternaryNode) {
-        final Expression trueExpr  = ternaryNode.getTrueExpression();
-        final Expression falseExpr = ternaryNode.getFalseExpression();
-
-        ensureTypeNotUnknown(trueExpr);
-        ensureTypeNotUnknown(falseExpr);
-
-        final Type type = widestReturnType(trueExpr.getType(), falseExpr.getType());
-        return end(ensureSymbol(type, ternaryNode));
+        final Type trueType  = ternaryNode.getTrueExpression().getType();
+        final Type falseType = ternaryNode.getFalseExpression().getType();
+        final Type type;
+        if (trueType.isUnknown() || falseType.isUnknown()) {
+            type = Type.UNKNOWN;
+        } else {
+            type = widestReturnType(trueType, falseType);
+        }
+        return end(ensureSymbol(ternaryNode, type));
     }
 
     /**
      * When doing widening for return types of a function or a ternary operator, it is not valid to widen a boolean to
-     * anything other than Object. Also, widening a numeric type to an object type must widen to Object proper and not
-     * any more specific subclass (e.g. widest of int/long/double and String is Object).
+     * anything other than object. Note that this wouldn't be necessary if {@code Type.widest} did not allow
+     * boolean-to-number widening. Eventually, we should address it there, but it affects too many other parts of the
+     * system and is sometimes legitimate (e.g. whenever a boolean value would undergo ToNumber conversion anyway).
      * @param t1 type 1
      * @param t2 type 2
-     * @return wider of t1 and t2, except if one is boolean and the other is neither boolean nor unknown, or if one is
-     * numeric and the other is neither numeric nor unknown in which case {@code Type.OBJECT} is returned.
+     * @return wider of t1 and t2, except if one is boolean and the other is neither boolean nor unknown, in which case
+     * {@code Type.OBJECT} is returned.
      */
     private static Type widestReturnType(final Type t1, final Type t2) {
         if (t1.isUnknown()) {
             return t2;
         } else if (t2.isUnknown()) {
             return t1;
-        } else if (t1.isBoolean() != t2.isBoolean() || t1.isNumeric() != t2.isNumeric()) {
+        } else if(t1.isBoolean() != t2.isBoolean() || t1.isNumeric() != t2.isNumeric()) {
             return Type.OBJECT;
         }
         return Type.widest(t1, t2);
     }
 
     private void initCompileConstant(final CompilerConstants cc, final Block block, final int flags) {
-        final Class<?> type = cc.type();
         // Must not call this method for constants with no explicit types; use the one with (..., Type) signature instead.
-        assert type != null;
-        initCompileConstant(cc, block, flags, Type.typeFor(type));
+        assert cc.type() != null;
+        initCompileConstant(cc, block, flags, Type.typeFor(cc.type()));
     }
 
     private void initCompileConstant(final CompilerConstants cc, final Block block, final int flags, final Type type) {
-        final Symbol symbol = defineSymbol(block, cc.symbolName(), flags);
-        symbol.setTypeOverride(type);
-        symbol.setNeedsSlot(true);
+        defineSymbol(block, cc.symbolName(), flags).
+            setTypeOverride(type).
+            setNeedsSlot(true);
     }
 
     /**
@@ -1477,22 +1628,29 @@
      * @param functionNode the function node
      */
     private void initParameters(final FunctionNode functionNode, final Block body) {
+        final boolean isOptimistic = env.useOptimisticTypes();
         int pos = 0;
         for (final IdentNode param : functionNode.getParameters()) {
             addLocalDef(param.getName());
 
-            final Type callSiteParamType = functionNode.getHints().getParameterType(pos);
-            int flags = IS_PARAM;
-            if (callSiteParamType != null) {
-                LOG.info("Param ", param, " has a callsite type ", callSiteParamType, ". Using that.");
-                flags |= Symbol.IS_SPECIALIZED_PARAM;
-            }
-
-            final Symbol paramSymbol = defineSymbol(body, param.getName(), flags);
+            final Symbol paramSymbol = defineSymbol(body, param.getName(), IS_PARAM);
             assert paramSymbol != null;
 
-            newType(paramSymbol, callSiteParamType == null ? Type.UNKNOWN : callSiteParamType);
-
+            final Type callSiteParamType = env.getParamType(functionNode, pos);
+            if (callSiteParamType != null) {
+                LOG.info("Callsite type override for parameter " + pos + " " + paramSymbol + " => " + callSiteParamType);
+                newType(paramSymbol, callSiteParamType);
+            } else {
+                // When we're using optimistic compilation, we'll generate specialized versions of the functions anyway
+                // based on their input type, so if we're doing a compilation without parameter types explicitly
+                // specified in the compilation environment, just pre-initialize them all to Object. Note that this is
+                // not merely an optimization; it has correctness implications as Type.UNKNOWN is narrower than all
+                // other types, which when combined with optimistic typing can cause invalid coercions to be introduced
+                // in the generated code. E.g. "var b = { x: 0 }; (function (i) { this.x += i }).apply(b, [1.1])" would
+                // erroneously allow coercion of "i" to int when "this.x" is an optimistic-int and "i" starts out
+                // with Type.UNKNOWN.
+                newType(paramSymbol, isOptimistic ? Type.OBJECT : Type.UNKNOWN);
+            }
             LOG.info("Initialized param ", pos, "=", paramSymbol);
             pos++;
         }
@@ -1508,49 +1666,47 @@
     private FunctionNode finalizeParameters(final FunctionNode functionNode) {
         final List<IdentNode> newParams = new ArrayList<>();
         final boolean isVarArg = functionNode.isVarArg();
-        final int nparams = functionNode.getParameters().size();
+        final boolean pessimistic = !useOptimisticTypes();
 
-        int specialize = 0;
-        int pos = 0;
         for (final IdentNode param : functionNode.getParameters()) {
             final Symbol paramSymbol = functionNode.getBody().getExistingSymbol(param.getName());
             assert paramSymbol != null;
-            assert paramSymbol.isParam();
+            assert paramSymbol.isParam() : paramSymbol + " " + paramSymbol.getFlags();
             newParams.add((IdentNode)param.setSymbol(lc, paramSymbol));
 
             assert paramSymbol != null;
-            Type type = functionNode.getHints().getParameterType(pos);
-            if (type == null) {
-                type = Type.OBJECT;
+            Type type = paramSymbol.getSymbolType();
+
+            // all param types are initialized to unknown
+            // first we check if we do have a type (inferred during generation)
+            // and it's not an object. In that case we make an optimistic
+            // assumption
+            if (!type.isUnknown() && !type.isObject()) {
+                //optimistically inferred
+                lc.logOptimisticAssumption(paramSymbol, type);
             }
 
-            // 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:
-            // we also need more than half of the parameters to be specializable
-            // for the heuristic to be worth it, and we need more than one use of
-            // the parameter to consider it, i.e. function(x) { call(x); } doens't count
-            if (paramSymbol.getUseCount() > 1 && !paramSymbol.getSymbolType().isObject()) {
-                LOG.finest("Parameter ", param, " could profit from specialization to ", paramSymbol.getSymbolType());
-                specialize++;
+            //known runtime types are hardcoded already in initParameters so avoid any
+            //overly optimistic assumptions, e.g. a double parameter known from
+            //RecompilableScriptFunctionData is with us all the way
+            if (type.isUnknown()) {
+                newType(paramSymbol, Type.OBJECT);
             }
 
-            newType(paramSymbol, Type.widest(type, paramSymbol.getSymbolType()));
+            // if we are pessimistic, we are always an object
+            if (pessimistic) {
+                newType(paramSymbol, Type.OBJECT);
+            }
 
             // parameters should not be slots for a function that uses variable arity signature
             if (isVarArg) {
                 paramSymbol.setNeedsSlot(false);
+                newType(paramSymbol, Type.OBJECT);
             }
-
-            pos++;
         }
 
         FunctionNode newFunctionNode = functionNode;
 
-        if (nparams == 0 || (specialize * 2) < nparams) {
-            newFunctionNode = newFunctionNode.clearSnapshot(lc);
-        }
-
         return newFunctionNode.setParameters(lc, newParams);
     }
 
@@ -1571,45 +1727,6 @@
         }
     }
 
-    private static void ensureTypeNotUnknown(final Expression node) {
-
-        final Symbol symbol = node.getSymbol();
-
-        LOG.info("Ensure type not unknown for: ", symbol);
-
-        /*
-         * Note that not just unknowns, but params need to be blown
-         * up to objects, because we can have something like
-         *
-         * function f(a) {
-         *    var b = ~a; //b and a are inferred to be int
-         *    return b;
-         * }
-         *
-         * In this case, it would be correct to say that "if you have
-         * an int at the callsite, just pass it".
-         *
-         * However
-         *
-         * function f(a) {
-         *    var b = ~a;      //b and a are inferred to be int
-         *    return b == 17;  //b is still inferred to be int.
-         * }
-         *
-         * can be called with f("17") and if we assume that b is an
-         * int and don't blow it up to an object in the comparison, we
-         * are screwed. I hate JavaScript.
-         *
-         * This check has to be done for any operation that might take
-         * objects as parameters, for example +, but not *, which is known
-         * to coerce types into doubles
-         */
-        if (node.getType().isUnknown() || (symbol.isParam() && !symbol.isSpecializedParam())) {
-            newType(symbol, Type.OBJECT);
-            symbol.setCanBeUndefined();
-         }
-    }
-
     private static Symbol pseudoSymbol(final String name) {
         return new Symbol(name, 0, Type.OBJECT);
     }
@@ -1618,15 +1735,6 @@
         return newInternal(lc.getCurrentFunction().uniqueName(EXCEPTION_PREFIX.symbolName()), Type.typeFor(EXCEPTION_PREFIX.type()));
     }
 
-    /**
-     * Return the type that arithmetic ops should use. Until we have implemented better type
-     * analysis (range based) or overflow checks that are fast enough for int arithmetic,
-     * this is the number type
-     * @return the arithetic type
-     */
-    private static Type arithType() {
-        return Compiler.shouldUseIntegerArithmetic() ? Type.INT : Type.NUMBER;
-    }
 
     /**
      * If types have changed, we can have failed to update vars. For example
@@ -1638,9 +1746,15 @@
      */
     private FunctionNode finalizeTypes(final FunctionNode functionNode) {
         final Set<Node> changed = new HashSet<>();
+        final Deque<Type> returnTypes = new ArrayDeque<>();
+
         FunctionNode currentFunctionNode = functionNode;
+        int fixedPointIterations = 0;
         do {
+            fixedPointIterations++;
+            assert fixedPointIterations < 0x100 : "too many fixed point iterations for " + functionNode.getName() + " -> most likely infinite loop";
             changed.clear();
+
             final FunctionNode newFunctionNode = (FunctionNode)currentFunctionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
 
                 private Expression widen(final Expression node, final Type to) {
@@ -1656,7 +1770,9 @@
                         }
                         newType(symbol, to);
                         final Expression newNode = node.setSymbol(lc, symbol);
-                        changed.add(newNode);
+                        if (node != newNode) {
+                            changed.add(newNode);
+                        }
                         return newNode;
                     }
                     return node;
@@ -1664,7 +1780,31 @@
 
                 @Override
                 public boolean enterFunctionNode(final FunctionNode node) {
-                    return !node.isLazy();
+                    returnTypes.push(Type.UNKNOWN);
+                    return true;
+                }
+
+                @Override
+                public Node leaveFunctionNode(final FunctionNode node) {
+                    Type returnType = returnTypes.pop();
+                    if (returnType.isUnknown()) {
+                        returnType = Type.OBJECT;
+                    }
+                    return node.setReturnType(lc, returnType);
+                }
+
+                @Override
+                public Node leaveReturnNode(final ReturnNode returnNode) {
+                    Type returnType = returnTypes.pop();
+                    if (returnNode.hasExpression()) {
+                        returnType = widestReturnType(returnType, returnNode.getExpression().getType()); //getSymbol().getSymbolType());
+                    } else {
+                        returnType = Type.OBJECT; //undefined
+                    }
+
+                    returnTypes.push(returnType);
+
+                    return returnNode;
                 }
 
                 //
@@ -1683,10 +1823,16 @@
                 @SuppressWarnings("fallthrough")
                 @Override
                 public Node leaveBinaryNode(final BinaryNode binaryNode) {
-                    final Type widest = Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType());
+                    Type widest = Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType());
                     BinaryNode newBinaryNode = binaryNode;
 
                     if (isAdd(binaryNode)) {
+                        if(widest.getTypeClass() == String.class) {
+                            // Erase "String" to "Object" as we have trouble with optimistically typed operands that
+                            // would be typed "String" in the code generator as they are always loaded using the type
+                            // of the operation.
+                            widest = Type.OBJECT;
+                        }
                         newBinaryNode = (BinaryNode)widen(newBinaryNode, widest);
                         if (newBinaryNode.getType().isObject() && !isAddString(newBinaryNode)) {
                             return new RuntimeNode(newBinaryNode, Request.ADD);
@@ -1728,6 +1874,11 @@
 
                 }
 
+                @Override
+                public Node leaveTernaryNode(TernaryNode ternaryNode) {
+                    return widen(ternaryNode, Type.widest(ternaryNode.getTrueExpression().getType(), ternaryNode.getFalseExpression().getType()));
+                }
+
                 private boolean isAdd(final Node node) {
                     return node.isTokenType(TokenType.ADD);
                 }
@@ -1773,24 +1924,145 @@
         return leaveSelfModifyingAssignmentNode(binaryNode, binaryNode.getWidestOperationType());
     }
 
-    private Node leaveSelfModifyingAssignmentNode(final BinaryNode binaryNode, final Type destType) {
+    private Node leaveSelfModifyingAssignmentNode(final BinaryNode binaryNode, final Type pessimisticType) {
         //e.g. for -=, Number, no wider, destType (binaryNode.getWidestOperationType())  is the coerce type
         final Expression lhs = binaryNode.lhs();
-
-        newType(lhs.getSymbol(), destType); //may not narrow if dest is already wider than destType
-
-        return end(ensureSymbol(destType, binaryNode));
+        final BinaryNode newBinaryNode = ensureSymbolTypeOverride(binaryNode, pessimisticType, Type.widest(lhs.getType(), binaryNode.rhs().getType()));
+        newType(lhs.getSymbol(), newBinaryNode.getType()); //may not narrow if dest is already wider than destType
+        return end(newBinaryNode);
     }
 
-    private Expression ensureSymbol(final Type type, final Expression expr) {
+    private Expression ensureSymbol(final Expression expr, final Type type) {
         LOG.info("New TEMPORARY added to ", lc.getCurrentFunction().getName(), " type=", type);
         return temporarySymbols.ensureSymbol(lc, type, expr);
     }
 
+    @Override
+    public boolean enterReturnNode(ReturnNode returnNode) {
+        tagOptimistic(returnNode.getExpression());
+        return true;
+    }
+
+    @Override
+    public boolean enterIfNode(IfNode ifNode) {
+        tagNeverOptimistic(ifNode.getTest());
+        return true;
+    }
+
+    @Override
+    public boolean enterWhileNode(WhileNode whileNode) {
+        tagNeverOptimistic(whileNode.getTest());
+        return true;
+    }
+
+    /**
+     * Used to signal that children should be optimistic. Otherwise every identnode
+     * in the entire program would basically start out as being guessed as an int
+     * and warmup would take an ENORMOUS time. This is also used while we get all
+     * the logic up and running, as we currently can't afford to debug every potential
+     * situtation that has to do with unwarranted optimism. We currently only tag
+     * type overrides, all other nodes are nops in this function
+     *
+     * @param expr an expression that is to be tagged as optimistic.
+     */
+    private long tag(final Optimistic expr) {
+        return ((long)lc.getCurrentFunction().getId() << 32) | expr.getProgramPoint();
+    }
+
+    /**
+     * This is used to guarantee that there are no optimistic setters, something that
+     * doesn't make sense in our current model, where only optimistic getters can exist.
+     * If we set something, we use the callSiteType. We might want to use dual fields
+     * though and incorporate this later for the option of putting something wider than
+     * is currently in the field causing an UnwarrantedOptimismException.
+     *
+     * @param expr expression to be tagged as never optimistic
+     */
+    private void tagNeverOptimistic(final Expression expr) {
+        if (expr instanceof Optimistic) {
+            LOG.info("Tagging TypeOverride node '" + expr + "' never optimistic");
+            neverOptimistic.add(tag((Optimistic)expr));
+        }
+    }
+
+    private void tagOptimistic(final Expression expr) {
+        if (expr instanceof Optimistic) {
+            LOG.info("Tagging TypeOverride node '" + expr + "' as optimistic");
+            optimistic.add(tag((Optimistic)expr));
+        }
+    }
+
+    private boolean isTaggedNeverOptimistic(final Optimistic expr) {
+        return neverOptimistic.contains(tag(expr));
+    }
+
+    private boolean isTaggedOptimistic(final Optimistic expr) {
+        return optimistic.contains(tag(expr));
+    }
+
+    private Type getOptimisticType(Optimistic expr) {
+        return useOptimisticTypes() ? env.getOptimisticType(expr) : expr.getMostPessimisticType();
+    }
+
+    /**
+     *  This is the base function for typing a TypeOverride as optimistic. For any expression that
+     *  can also be a type override (call, ident node (scope load), access node, index node) we use
+     *  the override type to communicate optimism.
+     *
+     *  @param pessimisticType conservative always guaranteed to work for this operation
+     *  @param to node to set type for
+     */
+    private <T extends Expression & Optimistic> T ensureSymbolTypeOverride(final T node, final Type pessimisticType) {
+        return ensureSymbolTypeOverride(node, pessimisticType, null);
+    }
+
+    @SuppressWarnings("unchecked")
+    private <T extends Expression & Optimistic> T ensureSymbolTypeOverride(final T node, final Type pessimisticType, final Type argumentsType) {
+        // check what the most optimistic type for this node should be
+        // if we are running with optimistic types, this starts out as e.g. int, and based on previous
+        // failed assumptions it can be wider, for example double if we have failed this assumption
+        // in a previous run
+        Type optimisticType = getOptimisticType(node);
+
+        if (argumentsType != null) {
+            optimisticType = Type.widest(optimisticType, argumentsType);
+        }
+
+        // the symbol of the expression is the pessimistic one, i.e. IndexNodes are always Object for consistency
+        // with the type system.
+        T expr = (T)ensureSymbol(node, pessimisticType);
+
+        if (optimisticType.isObject()) {
+            return expr;
+        }
+
+        if (isTaggedNeverOptimistic(expr)) {
+            return expr;
+        }
+
+        if(!(node instanceof FunctionCall && ((FunctionCall)node).isFunction())) {
+            // in the case that we have an optimistic type, set the type override (setType is inherited from TypeOverride)
+            // but maintain the symbol type set above. Also flag the function as optimistic. Don't do this for any
+            // expressions that are used as the callee of a function call.
+            if (optimisticType.narrowerThan(pessimisticType)) {
+                expr = (T)expr.setType(temporarySymbols, optimisticType);
+                expr = (T)Node.setIsOptimistic(expr, true);
+                if (optimisticType.isPrimitive()) {
+                    final Symbol symbol = expr.getSymbol();
+                    if (symbol.isShared()) {
+                        expr = (T)expr.setSymbol(lc, symbol.createUnshared(symbol.getName()));
+                    }
+                }
+                LOG.fine(expr, " turned optimistic with type=", optimisticType);
+                assert ((Optimistic)expr).isOptimistic();
+            }
+        }
+        return expr;
+    }
+
+
     private Symbol newInternal(final String name, final Type type) {
-        final Symbol iter = defineSymbol(lc.getCurrentBlock(), name, IS_VAR | IS_INTERNAL);
-        iter.setType(type); // NASHORN-73
-        return iter;
+        return defineSymbol(lc.getCurrentBlock(), name, IS_VAR | IS_INTERNAL).setType(type); //NASHORN-73
     }
 
     private static void newType(final Symbol symbol, final Type type) {
@@ -1845,34 +2117,46 @@
         localUses.peek().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 body body for the function node we are leaving
-     */
-    private static void objectifySymbols(final Block body) {
-        body.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
-            private void toObject(final Block block) {
-                for (final Symbol symbol : block.getSymbols()) {
-                    if (!symbol.isTemp()) {
-                        newType(symbol, Type.OBJECT);
-                    }
-                }
+    private  void inferParameter(final Expression node, final Type type) {
+        final Symbol symbol = node.getSymbol();
+        if (useOptimisticTypes() && symbol.isParam()) {
+            final Type symbolType = symbol.getSymbolType();
+            if(symbolType.isBoolean() && !(type.isBoolean() || type == Type.OBJECT)) {
+                // boolean parameters can only legally be widened to Object
+                return;
             }
+            if (symbolType != type) {
+               LOG.info("Infer parameter type " + symbol + " ==> " + type + " " + lc.getCurrentFunction().getSource().getName() + " " + lc.getCurrentFunction().getName());
+            }
+            symbol.setType(type); //will be overwritten by object later if pessimistic anyway
+            lc.logOptimisticAssumption(symbol, type);
+        }
+    }
 
-            @Override
-            public boolean enterBlock(final Block block) {
-                toObject(block);
-                return true;
+    private BinaryNode coerce(final BinaryNode binaryNode, final Type pessimisticType) {
+        return coerce(binaryNode, pessimisticType, null);
+    }
+
+    private BinaryNode coerce(final BinaryNode binaryNode, final Type pessimisticType, final Type argumentsType) {
+        BinaryNode newNode = ensureSymbolTypeOverride(binaryNode, pessimisticType, argumentsType);
+        inferParameter(binaryNode.lhs(), newNode.getType());
+        inferParameter(binaryNode.rhs(), newNode.getType());
+        return newNode;
+    }
+
+    private UnaryNode coerce(final UnaryNode unaryNode, final Type pessimisticType) {
+        return coerce(unaryNode, pessimisticType, null);
+    }
+
+    private UnaryNode coerce(final UnaryNode unaryNode, final Type pessimisticType, final Type argumentType) {
+        UnaryNode newNode = ensureSymbolTypeOverride(unaryNode, pessimisticType, argumentType);
+        if (newNode.isOptimistic()) {
+            if (unaryNode.getExpression() instanceof Optimistic) {
+               newNode = newNode.setExpression(Node.setIsOptimistic(unaryNode.getExpression(), true));
             }
-
-            @Override
-            public boolean enterFunctionNode(final FunctionNode node) {
-                return false;
-            }
-        });
+        }
+        inferParameter(unaryNode.getExpression(), newNode.getType());
+        return newNode;
     }
 
     private static String name(final Node node) {
--- a/src/jdk/nashorn/internal/codegen/BranchOptimizer.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/src/jdk/nashorn/internal/codegen/BranchOptimizer.java	Wed Feb 26 13:17:57 2014 +0100
@@ -57,7 +57,7 @@
     }
 
     private void branchOptimizer(final UnaryNode unaryNode, final Label label, final boolean state) {
-        final Expression rhs = unaryNode.rhs();
+        final Expression rhs = unaryNode.getExpression();
 
         switch (unaryNode.tokenType()) {
         case NOT:
--- a/src/jdk/nashorn/internal/codegen/ClassEmitter.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/src/jdk/nashorn/internal/codegen/ClassEmitter.java	Wed Feb 26 13:17:57 2014 +0100
@@ -54,19 +54,23 @@
 
 import java.io.ByteArrayOutputStream;
 import java.io.PrintWriter;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 import java.util.Arrays;
 import java.util.EnumSet;
 import java.util.HashSet;
 import java.util.Set;
-
-import jdk.internal.org.objectweb.asm.ClassReader;
 import jdk.internal.org.objectweb.asm.ClassWriter;
 import jdk.internal.org.objectweb.asm.MethodVisitor;
 import jdk.internal.org.objectweb.asm.util.TraceClassVisitor;
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.FunctionNode;
 import jdk.nashorn.internal.ir.SplitNode;
+import jdk.nashorn.internal.ir.debug.NashornClassReader;
+import jdk.nashorn.internal.ir.debug.NashornTextifier;
+import jdk.nashorn.internal.runtime.Context;
 import jdk.nashorn.internal.runtime.PropertyMap;
+import jdk.nashorn.internal.runtime.RewriteException;
 import jdk.nashorn.internal.runtime.ScriptEnvironment;
 import jdk.nashorn.internal.runtime.ScriptObject;
 import jdk.nashorn.internal.runtime.Source;
@@ -106,6 +110,8 @@
  * @see Compiler
  */
 public class ClassEmitter implements Emitter {
+    /** Default flags for class generation - public class */
+    private static final EnumSet<Flag> DEFAULT_METHOD_FLAGS = EnumSet.of(Flag.PUBLIC);
 
     /** Sanity check flag - have we started on a class? */
     private boolean classStarted;
@@ -125,9 +131,6 @@
     /** The script environment */
     protected final ScriptEnvironment env;
 
-    /** Default flags for class generation - oublic class */
-    private static final EnumSet<Flag> DEFAULT_METHOD_FLAGS = EnumSet.of(Flag.PUBLIC);
-
     /** Compile unit class name. */
     private String unitClassName;
 
@@ -376,9 +379,19 @@
     static String disassemble(final byte[] bytecode) {
         final ByteArrayOutputStream baos = new ByteArrayOutputStream();
         try (final PrintWriter pw = new PrintWriter(baos)) {
-            new ClassReader(bytecode).accept(new TraceClassVisitor(pw), 0);
+            final NashornClassReader cr = new NashornClassReader(bytecode);
+            final Context ctx = AccessController.doPrivileged(new PrivilegedAction<Context>() {
+                @Override
+                public Context run() {
+                    return Context.getContext();
+                }
+            });
+            TraceClassVisitor tcv = new TraceClassVisitor(null, new NashornTextifier(ctx.getEnv(), cr), pw);
+            cr.accept(tcv, 0);
         }
-        return new String(baos.toByteArray());
+
+        final String str = new String(baos.toByteArray());
+        return str;
     }
 
     /**
@@ -475,17 +488,40 @@
      * @return method emitter to use for weaving this method
      */
     MethodEmitter method(final FunctionNode functionNode) {
+        final FunctionSignature signature = new FunctionSignature(functionNode);
         final MethodVisitor mv = cw.visitMethod(
             ACC_PUBLIC | ACC_STATIC | (functionNode.isVarArg() ? ACC_VARARGS : 0),
             functionNode.getName(),
-            new FunctionSignature(functionNode).toString(),
+            signature.toString(),
             null,
             null);
 
-        return new MethodEmitter(this, mv, functionNode);
+        final MethodEmitter method = new MethodEmitter(this, mv, functionNode);
+        method.setParameterTypes(signature.getParamTypes());
+        return method;
     }
 
     /**
+     * Add a new method to the class, representing a rest-of version of the function node
+     *
+     * @param functionNode the function node to generate a method for
+     * @return method emitter to use for weaving this method
+     */
+    MethodEmitter restOfMethod(final FunctionNode functionNode) {
+        final MethodVisitor mv = cw.visitMethod(
+            ACC_PUBLIC | ACC_STATIC,
+            functionNode.getName(),
+            Type.getMethodDescriptor(functionNode.getReturnType().getTypeClass(), RewriteException.class),
+            null,
+            null);
+
+        final MethodEmitter method = new MethodEmitter(this, mv, functionNode);
+        method.setParameterTypes(new FunctionSignature(functionNode).getParamTypes());
+        return method;
+    }
+
+
+    /**
      * Start generating the <clinit> method in the class
      *
      * @return method emitter to use for weaving <clinit>
--- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Wed Feb 26 13:17:57 2014 +0100
@@ -29,6 +29,7 @@
 import static jdk.nashorn.internal.codegen.ClassEmitter.Flag.STATIC;
 import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
 import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
+import static jdk.nashorn.internal.codegen.CompilerConstants.CREATE_PROGRAM_FUNCTION;
 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.QUICK_PREFIX;
@@ -45,20 +46,34 @@
 import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
 import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor;
 import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
+import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY;
+import static jdk.nashorn.internal.codegen.ObjectClassGenerator.getClassName;
+import static jdk.nashorn.internal.codegen.ObjectClassGenerator.getPaddedFieldCount;
 import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL;
 import static jdk.nashorn.internal.ir.Symbol.IS_TEMP;
+import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
+import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
 import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_FAST_SCOPE;
+import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_OPTIMISTIC;
+import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_PROGRAM_POINT_SHIFT;
 import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_SCOPE;
 import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_STRICT;
 
 import java.io.PrintWriter;
+import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Deque;
 import java.util.EnumSet;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
+import java.util.RandomAccess;
 import java.util.Set;
 import java.util.TreeMap;
 import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
@@ -94,6 +109,7 @@
 import jdk.nashorn.internal.ir.LoopNode;
 import jdk.nashorn.internal.ir.Node;
 import jdk.nashorn.internal.ir.ObjectNode;
+import jdk.nashorn.internal.ir.Optimistic;
 import jdk.nashorn.internal.ir.PropertyNode;
 import jdk.nashorn.internal.ir.ReturnNode;
 import jdk.nashorn.internal.ir.RuntimeNode;
@@ -120,17 +136,20 @@
 import jdk.nashorn.internal.runtime.DebugLogger;
 import jdk.nashorn.internal.runtime.ECMAException;
 import jdk.nashorn.internal.runtime.JSType;
-import jdk.nashorn.internal.runtime.Property;
+import jdk.nashorn.internal.runtime.OptimisticReturnFilters;
 import jdk.nashorn.internal.runtime.PropertyMap;
 import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
+import jdk.nashorn.internal.runtime.RewriteException;
 import jdk.nashorn.internal.runtime.Scope;
 import jdk.nashorn.internal.runtime.ScriptFunction;
 import jdk.nashorn.internal.runtime.ScriptObject;
 import jdk.nashorn.internal.runtime.ScriptRuntime;
 import jdk.nashorn.internal.runtime.Source;
 import jdk.nashorn.internal.runtime.Undefined;
+import jdk.nashorn.internal.runtime.UnwarrantedOptimismException;
 import jdk.nashorn.internal.runtime.arrays.ArrayData;
 import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
+import jdk.nashorn.internal.runtime.options.Options;
 
 /**
  * This is the lowest tier of the code generator. It takes lowered ASTs emitted
@@ -153,9 +172,24 @@
  */
 final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContext> {
 
+    private static final Type SCOPE_TYPE = Type.typeFor(ScriptObject.class);
+
     private static final String GLOBAL_OBJECT = Type.getInternalName(Global.class);
 
-    private static final String SCRIPTFUNCTION_IMPL_OBJECT = Type.getInternalName(ScriptFunctionImpl.class);
+    private static final String SCRIPTFUNCTION_IMPL_NAME = Type.getInternalName(ScriptFunctionImpl.class);
+    private static final Type   SCRIPTFUNCTION_IMPL_TYPE   = Type.typeFor(ScriptFunction.class);
+
+    private static final Call INIT_REWRITE_EXCEPTION = CompilerConstants.specialCallNoLookup(RewriteException.class,
+            "<init>", void.class, UnwarrantedOptimismException.class, Object[].class);
+    private static final Call INIT_REWRITE_EXCEPTION_REST_OF = CompilerConstants.specialCallNoLookup(RewriteException.class,
+            "<init>", void.class, UnwarrantedOptimismException.class, Object[].class, int[].class);
+
+    private static final Call ENSURE_INT = CompilerConstants.staticCallNoLookup(OptimisticReturnFilters.class,
+            "ensureInt", int.class, Object.class, int.class);
+    private static final Call ENSURE_LONG = CompilerConstants.staticCallNoLookup(OptimisticReturnFilters.class,
+            "ensureLong", long.class, Object.class, int.class);
+    private static final Call ENSURE_NUMBER = CompilerConstants.staticCallNoLookup(OptimisticReturnFilters.class,
+            "ensureNumber", double.class, Object.class, int.class);
 
     /** Constant data & installation. The only reason the compiler keeps this is because it is assigned
      *  by reflection in class installation */
@@ -183,10 +217,18 @@
     private static final DebugLogger LOG   = new DebugLogger("codegen", "nashorn.codegen.debug");
 
     /** From what size should we use spill instead of fields for JavaScript objects? */
-    private static final int OBJECT_SPILL_THRESHOLD = 300;
+    private static final int OBJECT_SPILL_THRESHOLD = Options.getIntProperty("nashorn.spill.threshold", 256);
 
     private final Set<String> emittedMethods = new HashSet<>();
 
+    // Function Id -> ContinuationInfo. Used by compilation of rest-of function only.
+    private final Map<Integer, ContinuationInfo> fnIdToContinuationInfo = new HashMap<>();
+
+    // Function Id -> (Function Id -> Function Data)). Used by compilation of most-optimistic function only.
+    private final Map<Integer, Map<Integer, RecompilableScriptFunctionData>> fnIdToNestedFunctions = new HashMap<>();
+
+    private final Deque<Label> scopeEntryLabels = new ArrayDeque<>();
+
     /**
      * Constructor.
      *
@@ -209,6 +251,26 @@
     }
 
     /**
+     * For an optimistic call site, we need to tag the callsite optimistic and
+     * encode the program point of the callsite into it
+     *
+     * @param node node that can be optimistic
+     * @return
+     */
+    private int getCallSiteFlagsOptimistic(final Optimistic node) {
+        int flags = getCallSiteFlags();
+        if (node.isOptimistic()) {
+            flags |= CALLSITE_OPTIMISTIC;
+            flags |= node.getProgramPoint() << CALLSITE_PROGRAM_POINT_SHIFT; //encode program point in high bits
+        }
+        return flags;
+    }
+
+    private static boolean isOptimistic(final int flags) {
+        return (flags & CALLSITE_OPTIMISTIC) != 0;
+    }
+
+    /**
      * Load an identity node
      *
      * @param identNode an identity node to load
@@ -222,30 +284,77 @@
             return method.load(symbol).convert(type);
         }
 
-        final String name   = symbol.getName();
-        final Source source = lc.getCurrentFunction().getSource();
-
+        // If this is either __FILE__, __DIR__, or __LINE__ then load the property initially as Object as we'd convert
+        // it anyway for replaceLocationPropertyPlaceholder.
+        final boolean isCompileTimePropertyName = identNode.isCompileTimePropertyName();
+
+        assert identNode.getSymbol().isScope() : identNode + " is not in scope!";
+        final int flags = CALLSITE_SCOPE | getCallSiteFlagsOptimistic(identNode);
+        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 && !isOptimisticOrRestOf()) {
+                method.loadCompilerConstant(SCOPE);
+                loadSharedScopeVar(type, symbol, flags);
+            } else {
+                loadFastScopeVar(identNode, type, flags, isCompileTimePropertyName);
+            }
+        } else {
+            new OptimisticOperation() {
+                @Override
+                void loadStack() {
+                    method.loadCompilerConstant(SCOPE);
+                }
+                @Override
+                void consumeStack() {
+                    dynamicGet(method, identNode, isCompileTimePropertyName ? Type.OBJECT : type, identNode.getName(), flags, identNode.isFunction());
+                    if(isCompileTimePropertyName) {
+                        replaceCompileTimeProperty(identNode, type);
+                    }
+                }
+            }.emit(identNode, type);
+        }
+
+        return method;
+    }
+
+    private void replaceCompileTimeProperty(final IdentNode identNode, final Type type) {
+        final String name = identNode.getSymbol().getName();
         if (CompilerConstants.__FILE__.name().equals(name)) {
-            return method.load(source.getName());
+            replaceCompileTimeProperty(identNode, type, getCurrentSource().getName());
         } else if (CompilerConstants.__DIR__.name().equals(name)) {
-            return method.load(source.getBase());
+            replaceCompileTimeProperty(identNode, type, getCurrentSource().getBase());
         } else if (CompilerConstants.__LINE__.name().equals(name)) {
-            return method.load(source.getLine(identNode.position())).convert(Type.OBJECT);
+            replaceCompileTimeProperty(identNode, type, getCurrentSource().getLine(identNode.position()));
+        }
+    }
+
+    /**
+     * When an ident with name __FILE__, __DIR__, or __LINE__ is loaded, we'll try to look it up as any other
+     * identifier. However, if it gets all the way up to the Global object, it will send back a special value that
+     * represents a placeholder for these compile-time location properties. This method will generate code that loads
+     * the value of the compile-time location property and then invokes a method in Global that will replace the
+     * placeholder with the value. Effectively, if the symbol for these properties is defined anywhere in the lexical
+     * scope, they take precedence, but if they aren't, then they resolve to the compile-time location property.
+     * @param identNode the ident node
+     * @param type the desired return type for the ident node
+     * @param propertyValue the actual value of the property
+     */
+    private void replaceCompileTimeProperty(final IdentNode identNode, final Type type, final Object propertyValue) {
+        assert method.peekType().isObject();
+        if(propertyValue instanceof String) {
+            method.load((String)propertyValue);
+        } else if(propertyValue instanceof Integer) {
+            method.load(((Integer)propertyValue).intValue());
+            method.convert(Type.OBJECT);
         } else {
-            assert identNode.getSymbol().isScope() : identNode + " is not in scope!";
-
-            final int flags = CALLSITE_SCOPE | getCallSiteFlags();
-            method.loadCompilerConstant(SCOPE);
-
-            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(type, symbol, flags);
-                }
-                return loadFastScopeVar(type, symbol, flags, identNode.isFunction());
-            }
-            return method.dynamicGet(type, identNode.getName(), flags, identNode.isFunction());
+            throw new AssertionError();
         }
+        globalReplaceLocationPropertyPlaceholder();
+        convertOptimisticReturnValue(identNode, type);
+    }
+
+    private boolean isOptimisticOrRestOf() {
+        return useOptimisticTypes() || compiler.getCompilationEnvironment().isCompileRestOf();
     }
 
     /**
@@ -301,13 +410,25 @@
 
     private MethodEmitter loadSharedScopeVar(final Type valueType, final Symbol symbol, final int flags) {
         method.load(isFastScope(symbol) ? getScopeProtoDepth(lc.getCurrentBlock(), symbol) : -1);
-        final SharedScopeCall scopeCall = lc.getScopeGet(unit, valueType, symbol, flags | CALLSITE_FAST_SCOPE);
+        final SharedScopeCall scopeCall = lc.getScopeGet(unit, symbol, valueType, flags | CALLSITE_FAST_SCOPE);
         return scopeCall.generateInvoke(method);
     }
 
-    private MethodEmitter loadFastScopeVar(final Type valueType, final Symbol symbol, final int flags, final boolean isMethod) {
-        loadFastScopeProto(symbol, false);
-        return method.dynamicGet(valueType, symbol.getName(), flags | CALLSITE_FAST_SCOPE, isMethod);
+    private MethodEmitter loadFastScopeVar(final IdentNode identNode, final Type type, final int flags, final boolean isCompileTimePropertyName) {
+        return new OptimisticOperation() {
+            @Override
+            void loadStack() {
+                method.loadCompilerConstant(SCOPE);
+                loadFastScopeProto(identNode.getSymbol(), false);
+            }
+            @Override
+            void consumeStack() {
+                dynamicGet(method, identNode, isCompileTimePropertyName ? Type.OBJECT : type, identNode.getSymbol().getName(), flags | CALLSITE_FAST_SCOPE, identNode.isFunction());
+                if(isCompileTimePropertyName) {
+                    replaceCompileTimeProperty(identNode, type);
+                }
+            }
+        }.emit(identNode, type);
     }
 
     private MethodEmitter storeFastScopeVar(final Symbol symbol, final int flags) {
@@ -333,7 +454,7 @@
 
     private void loadFastScopeProto(final Symbol symbol, final boolean swap) {
         final int depth = getScopeProtoDepth(lc.getCurrentBlock(), symbol);
-        assert depth != -1;
+        assert depth != -1 : "Couldn't find scope depth for symbol " + symbol.getName();
         if (depth > 0) {
             if (swap) {
                 method.swap();
@@ -356,12 +477,12 @@
      * @return the method emitter used
      */
     MethodEmitter load(final Expression node) {
-        return load(node, node.hasType() ? node.getType() : null, false);
+        return load(node, node.hasType() ? node.getType() : null);
     }
 
     // Test whether conversion from source to target involves a call of ES 9.1 ToPrimitive
     // with possible side effects from calling an object's toString or valueOf methods.
-    private boolean noToPrimitiveConversion(final Type source, final Type target) {
+    private static boolean noToPrimitiveConversion(final Type source, final Type target) {
         // Object to boolean conversion does not cause ToPrimitive call
         return source.isJSPrimitive() || !target.isJSPrimitive() || target.isBoolean();
     }
@@ -421,7 +542,7 @@
          */
         final CodeGenerator codegen = this;
 
-        node.accept(new NodeVisitor<LexicalContext>(lc) {
+        node.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
             @Override
             public boolean enterIdentNode(final IdentNode identNode) {
                 loadIdent(identNode, type);
@@ -430,21 +551,39 @@
 
             @Override
             public boolean enterAccessNode(final AccessNode accessNode) {
-                if (!baseAlreadyOnStack) {
-                    load(accessNode.getBase(), Type.OBJECT);
-                }
-                assert method.peekType().isObject();
-                method.dynamicGet(type, accessNode.getProperty().getName(), getCallSiteFlags(), accessNode.isFunction());
+                new OptimisticOperation() {
+                    @Override
+                    void loadStack() {
+                        if (!baseAlreadyOnStack) {
+                            load(accessNode.getBase(), Type.OBJECT);
+                        }
+                        assert method.peekType().isObject();
+                    }
+                    @Override
+                    void consumeStack() {
+                        final int flags = getCallSiteFlagsOptimistic(accessNode);
+                        dynamicGet(method, accessNode, type, accessNode.getProperty().getName(), flags, accessNode.isFunction());
+                    }
+                }.emit(accessNode, baseAlreadyOnStack ? 1 : 0);
                 return false;
             }
 
             @Override
             public boolean enterIndexNode(final IndexNode indexNode) {
-                if (!baseAlreadyOnStack) {
-                    load(indexNode.getBase(), Type.OBJECT);
-                    load(indexNode.getIndex());
-                }
-                method.dynamicGetIndex(type, getCallSiteFlags(), indexNode.isFunction());
+                new OptimisticOperation() {
+                    @Override
+                    void loadStack() {
+                        if (!baseAlreadyOnStack) {
+                            load(indexNode.getBase(), Type.OBJECT);
+                            load(indexNode.getIndex());
+                        }
+                    }
+                    @Override
+                    void consumeStack() {
+                        final int flags = getCallSiteFlagsOptimistic(indexNode);
+                        dynamicGetIndex(method, indexNode, type, flags, indexNode.isFunction());
+                    }
+                }.emit(indexNode, baseAlreadyOnStack ? 2 : 0);
                 return false;
             }
 
@@ -505,6 +644,7 @@
     private void initSymbols(final Iterable<Symbol> symbols) {
         final LinkedList<Symbol> numbers = new LinkedList<>();
         final LinkedList<Symbol> objects = new LinkedList<>();
+        final boolean useOptimistic = useOptimisticTypes();
 
         for (final Symbol symbol : symbols) {
             /*
@@ -514,14 +654,21 @@
              * Otherwise we must, unless we perform control/escape analysis,
              * assign them undefined.
              */
-            final boolean isInternal = symbol.isParam() || symbol.isInternal() || symbol.isThis() || !symbol.canBeUndefined();
-
-            if (symbol.hasSlot() && !isInternal) {
-                assert symbol.getSymbolType().isNumber() || symbol.getSymbolType().isObject() : "no potentially undefined narrower local vars than doubles are allowed: " + symbol + " in " + lc.getCurrentFunction();
-                if (symbol.getSymbolType().isNumber()) {
-                    numbers.add(symbol);
-                } else if (symbol.getSymbolType().isObject()) {
-                    objects.add(symbol);
+            final boolean isInternal = symbol.isParam() || symbol.isInternal() || symbol.isThis();
+
+            if (symbol.hasSlot()) {
+                final Type type = symbol.getSymbolType();
+                if(symbol.canBeUndefined() && !isInternal) {
+                    if (type.isNumber()) {
+                        numbers.add(symbol);
+                    } else if (type.isObject()) {
+                        objects.add(symbol);
+                    } else {
+                        throw new AssertionError("no potentially undefined narrower local vars than doubles are allowed: " + symbol + " in " + lc.getCurrentFunction());
+                    }
+                } else if(useOptimistic && !symbol.isAlwaysDefined()) {
+                    method.loadForcedInitializer(type);
+                    method.store(symbol);
                 }
             }
         }
@@ -570,46 +717,82 @@
         return true;
     }
 
+    private boolean useOptimisticTypes() {
+        return !lc.inSplitNode() && compiler.getCompilationEnvironment().useOptimisticTypes();
+    }
+
     @Override
     public Node leaveBlock(final Block block) {
-        method.label(block.getBreakLabel());
+
+        popBlockScope(block);
+        lc.releaseBlockSlots(useOptimisticTypes());
+
         symbolInfo(block);
-
-        if (block.needsScope() && !block.isTerminal()) {
-            popBlockScope(block);
-        }
         return block;
     }
 
     private void popBlockScope(final Block block) {
-        final Label exitLabel     = new Label("block_exit");
-        final Label recoveryLabel = new Label("block_catch");
-        final Label skipLabel     = new Label("skip_catch");
+        if(!block.needsScope() || lc.isFunctionBody()) {
+            method.label(block.getBreakLabel());
+            return;
+        }
+
+        final Label entry = scopeEntryLabels.pop();
+        final Label afterCatchLabel;
+        final Label recoveryLabel = new Label("block_popscope_catch");
 
         /* pop scope a la try-finally */
+        if(block.isTerminal()) {
+            // Block is terminal; there's no normal-flow path for popping the scope. Label current position as the end
+            // of the try block, and mark after-catch to be the block's break label.
+            final Label endTryLabel = new Label("block_popscope_end_try");
+            method._try(entry, endTryLabel, recoveryLabel);
+            method.label(endTryLabel);
+            afterCatchLabel = block.getBreakLabel();
+        } else {
+            // Block is non-terminal; Label current position as the block's break label (as it'll need to execute the
+            // scope popping when it gets here) and as the end of the try block. Mark after-catch with a new label.
+            final Label endTryLabel = block.getBreakLabel();
+            method._try(entry, endTryLabel, recoveryLabel);
+            method.label(endTryLabel);
+            popScope();
+            afterCatchLabel = new Label("block_after_catch");
+            method._goto(afterCatchLabel);
+        }
+
+        method._catch(recoveryLabel);
+        popScope();
+        method.athrow();
+        method.label(afterCatchLabel);
+    }
+
+    private void popScope() {
+        popScopes(1);
+    }
+
+    private void popScopesUntil(final LexicalContextNode until) {
+        popScopes(lc.getScopeNestingLevelTo(until));
+    }
+
+    private void popScopes(final int count) {
+        if(count == 0) {
+            return;
+        }
+        assert count > 0; // together with count == 0 check, asserts nonnegative count
+        assert method.hasScope();
         method.loadCompilerConstant(SCOPE);
-        method.invoke(ScriptObject.GET_PROTO);
+        for(int i = 0; i < count; ++i) {
+            method.invoke(ScriptObject.GET_PROTO);
+        }
         method.storeCompilerConstant(SCOPE);
-        method._goto(skipLabel);
-        method.label(exitLabel);
-
-        method._catch(recoveryLabel);
-        method.loadCompilerConstant(SCOPE);
-        method.invoke(ScriptObject.GET_PROTO);
-        method.storeCompilerConstant(SCOPE);
-        method.athrow();
-        method.label(skipLabel);
-        method._try(block.getEntryLabel(), exitLabel, recoveryLabel, Throwable.class);
     }
 
     @Override
     public boolean enterBreakNode(final BreakNode breakNode) {
-        lineNumber(breakNode);
+        enterStatement(breakNode);
 
         final BreakableNode breakFrom = lc.getBreakable(breakNode.getLabel());
-        for (int i = 0; i < lc.getScopeNestingLevelTo(breakFrom); i++) {
-            closeWith();
-        }
+        popScopesUntil(breakFrom);
         method.splitAwareGoto(lc, breakFrom.getBreakLabel());
 
         return false;
@@ -668,68 +851,100 @@
 
             private MethodEmitter sharedScopeCall(final IdentNode identNode, final int flags) {
                 final Symbol symbol = identNode.getSymbol();
-                int    scopeCallFlags = flags;
-                method.loadCompilerConstant(SCOPE);
-                if (isFastScope(symbol)) {
-                    method.load(getScopeProtoDepth(currentBlock, symbol));
-                    scopeCallFlags |= CALLSITE_FAST_SCOPE;
-                } else {
-                    method.load(-1); // Bypass fast-scope code in shared callsite
-                }
-                loadArgs(args);
-                final Type[] paramTypes = method.getTypesFromStack(args.size());
-                final SharedScopeCall scopeCall = codegenLexicalContext.getScopeCall(unit, symbol, identNode.getType(), callNodeType, paramTypes, scopeCallFlags);
-                return scopeCall.generateInvoke(method);
+                final boolean isFastScope = isFastScope(symbol);
+                final int scopeCallFlags = flags | (isFastScope ? CALLSITE_FAST_SCOPE : 0);
+                new OptimisticOperation() {
+                    @Override
+                    void loadStack() {
+                        method.loadCompilerConstant(SCOPE);
+                        if (isFastScope) {
+                            method.load(getScopeProtoDepth(currentBlock, symbol));
+                        } else {
+                            method.load(-1); // Bypass fast-scope code in shared callsite
+                        }
+                        loadArgs(args);
+                    }
+                    @Override
+                    void consumeStack() {
+                        final Type[] paramTypes = method.getTypesFromStack(args.size());
+                        final SharedScopeCall scopeCall = codegenLexicalContext.getScopeCall(unit, symbol, identNode.getType(), callNodeType, paramTypes, scopeCallFlags);
+                        scopeCall.generateInvoke(method);
+                    }
+                }.emit(callNode);
+                return method;
             }
 
             private void scopeCall(final IdentNode node, final int flags) {
-                load(node, Type.OBJECT); // Type.OBJECT as foo() makes no sense if foo == 3
-                // ScriptFunction will see CALLSITE_SCOPE and will bind scope accordingly.
-                method.loadNull(); //the 'this'
-                method.dynamicCall(callNodeType, 2 + loadArgs(args), flags);
+                new OptimisticOperation() {
+                    int argsCount;
+                    @Override
+                    void loadStack() {
+                        load(node, Type.OBJECT); // foo() makes no sense if foo == 3
+                        // ScriptFunction will see CALLSITE_SCOPE and will bind scope accordingly.
+                        method.loadNull(); //the 'this'
+                        argsCount = loadArgs(args);
+                    }
+                    @Override
+                    void consumeStack() {
+                        dynamicCall(method, callNode, callNodeType, 2 + argsCount, flags);
+                    }
+                }.emit(callNode);
             }
 
             private void evalCall(final IdentNode node, final int flags) {
-                load(node, Type.OBJECT); // Type.OBJECT as foo() makes no sense if foo == 3
-
-                final Label not_eval  = new Label("not_eval");
+                final Label invoke_direct_eval  = new Label("invoke_direct_eval");
+                final Label is_not_eval  = new Label("is_not_eval");
                 final Label eval_done = new Label("eval_done");
 
-                // check if this is the real built-in eval
-                method.dup();
-                globalIsEval();
-
-                method.ifeq(not_eval);
-                // We don't need ScriptFunction object for 'eval'
-                method.pop();
-
-                method.loadCompilerConstant(SCOPE); // Load up self (scope).
-
-                final CallNode.EvalArgs evalArgs = callNode.getEvalArgs();
-                // load evaluated code
-                load(evalArgs.getCode(), Type.OBJECT);
-                // load second and subsequent args for side-effect
-                final List<Expression> args = callNode.getArgs();
-                final int numArgs = args.size();
-                for (int i = 1; i < numArgs; i++) {
-                    load(args.get(i)).pop();
-                }
-                // special/extra 'eval' arguments
-                load(evalArgs.getThis());
-                method.load(evalArgs.getLocation());
-                method.load(evalArgs.getStrictMode());
-                method.convert(Type.OBJECT);
-
-                // direct call to Global.directEval
-                globalDirectEval();
-                method.convert(callNodeType);
-                method._goto(eval_done);
-
-                method.label(not_eval);
-                // This is some scope 'eval' or global eval replaced by user
-                // but not the built-in ECMAScript 'eval' function call
-                method.loadNull();
-                method.dynamicCall(callNodeType, 2 + loadArgs(args), flags);
+                new OptimisticOperation() {
+                    int argsCount;
+                    @Override
+                    void loadStack() {
+                        load(node, Type.OBJECT); // Type.OBJECT as foo() makes no sense if foo == 3
+                        method.dup();
+                        globalIsEval();
+                        method.ifeq(is_not_eval);
+
+                        // We don't need ScriptFunction object for 'eval'
+                        method.pop();
+                        // Load up self (scope).
+                        method.loadCompilerConstant(SCOPE);
+                        final CallNode.EvalArgs evalArgs = callNode.getEvalArgs();
+                        // load evaluated code
+                        load(evalArgs.getCode(), Type.OBJECT);
+                        // load second and subsequent args for side-effect
+                        final List<Expression> callArgs = callNode.getArgs();
+                        final int numArgs = callArgs.size();
+                        for (int i = 1; i < numArgs; i++) {
+                            load(callArgs.get(i)).pop();
+                        }
+                        // special/extra 'eval' arguments
+                        load(evalArgs.getThis());
+                        method.load(evalArgs.getLocation());
+                        method.load(evalArgs.getStrictMode());
+                        method.convert(Type.OBJECT);
+                        method._goto(invoke_direct_eval);
+
+                        method.label(is_not_eval);
+                        // This is some scope 'eval' or global eval replaced by user
+                        // but not the built-in ECMAScript 'eval' function call
+                        method.loadNull();
+                        argsCount = loadArgs(callArgs);
+                    }
+
+                    @Override
+                    void consumeStack() {
+                        // Ordinary call
+                        dynamicCall(method, callNode, callNodeType, 2 + argsCount, flags);
+                        method._goto(eval_done);
+
+                        method.label(invoke_direct_eval);
+                        // direct call to Global.directEval
+                        globalDirectEval();
+                        convertOptimisticReturnValue(callNode, callNodeType);
+                        method.convert(callNodeType);
+                    }
+                }.emit(callNode);
 
                 method.label(eval_done);
             }
@@ -739,7 +954,7 @@
                 final Symbol symbol = node.getSymbol();
 
                 if (symbol.isScope()) {
-                    final int flags = getCallSiteFlags() | CALLSITE_SCOPE;
+                    final int flags = getCallSiteFlagsOptimistic(callNode) | CALLSITE_SCOPE;
                     final int useCount = symbol.getUseCount();
 
                     // Threshold for generating shared scope callsite is lower for fast scope symbols because we know
@@ -749,7 +964,8 @@
                         evalCall(node, flags);
                     } else if (useCount <= SharedScopeCall.FAST_SCOPE_CALL_THRESHOLD
                             || (!isFastScope(symbol) && useCount <= SharedScopeCall.SLOW_SCOPE_CALL_THRESHOLD)
-                            || CodeGenerator.this.lc.inDynamicScope()) {
+                            || CodeGenerator.this.lc.inDynamicScope()
+                            || isOptimisticOrRestOf()) {
                         scopeCall(node, flags);
                     } else {
                         sharedScopeCall(node, flags);
@@ -764,62 +980,104 @@
 
             @Override
             public boolean enterAccessNode(final AccessNode node) {
-                load(node.getBase(), Type.OBJECT);
-                method.dup();
-                method.dynamicGet(node.getType(), node.getProperty().getName(), getCallSiteFlags(), true);
-                method.swap();
-                method.dynamicCall(callNodeType, 2 + loadArgs(args), getCallSiteFlags());
+                new OptimisticOperation() {
+                    int argCount;
+                    @Override
+                    void loadStack() {
+                        load(node.getBase(), Type.OBJECT);
+                        method.dup();
+                        // NOTE: not using a nested OptimisticOperation on this dynamicGet, as we expect to get back
+                        // a callable object. Nobody in their right mind would optimistically type this call site.
+                        assert !node.isOptimistic();
+                        method.dynamicGet(node.getType(), node.getProperty().getName(), getCallSiteFlags(), true);
+                        method.swap();
+                        argCount = loadArgs(args);
+                    }
+                    @Override
+                    void consumeStack() {
+                        final int flags = getCallSiteFlagsOptimistic(callNode);
+                        dynamicCall(method, callNode, callNodeType, 2 + argCount, flags);
+                    }
+                }.emit(callNode);
 
                 return false;
             }
 
             @Override
             public boolean enterFunctionNode(final FunctionNode origCallee) {
-                // NOTE: visiting the callee will leave a constructed ScriptFunction object on the stack if
-                // callee.needsCallee() == true
-                final FunctionNode callee = (FunctionNode)origCallee.accept(CodeGenerator.this);
-
-                final boolean      isVarArg = callee.isVarArg();
-                final int          argCount = isVarArg ? -1 : callee.getParameters().size();
-
-                final String signature = new FunctionSignature(true, callee.needsCallee(), callee.getReturnType(), isVarArg ? null : callee.getParameters()).toString();
-
-                if (callee.isStrict()) { // self is undefined
-                    method.loadUndefined(Type.OBJECT);
-                } else { // get global from scope (which is the self)
-                    globalInstance();
-                }
-                loadArgs(args, signature, isVarArg, argCount);
-                assert callee.getCompileUnit() != null : "no compile unit for " + callee.getName() + " " + Debug.id(callee) + " " + callNode;
-                method.invokestatic(callee.getCompileUnit().getUnitClassName(), callee.getName(), signature);
-                assert method.peekType().equals(callee.getReturnType()) : method.peekType() + " != " + callee.getReturnType();
+                new OptimisticOperation() {
+                    FunctionNode callee;
+                    int argsCount;
+                    @Override
+                    void loadStack() {
+                        callee = (FunctionNode)origCallee.accept(CodeGenerator.this);
+                        if (callee.isStrict()) { // "this" is undefined
+                            method.loadUndefined(Type.OBJECT);
+                        } else { // get global from scope (which is the self)
+                            globalInstance();
+                        }
+                        argsCount = loadArgs(args);
+                    }
+
+                    @Override
+                    void consumeStack() {
+                        final int flags = getCallSiteFlagsOptimistic(callNode);
+                        //assert callNodeType.equals(callee.getReturnType()) : callNodeType + " != " + callee.getReturnType();
+                        dynamicCall(method, callNode, callNodeType, 2 + argsCount, flags);
+                        //assert method.peekType().equals(callee.getReturnType()) : method.peekType() + " != " + callee.getReturnType();
+                    }
+                }.emit(callNode);
                 method.convert(callNodeType);
                 return false;
             }
 
             @Override
             public boolean enterIndexNode(final IndexNode node) {
-                load(node.getBase(), Type.OBJECT);
-                method.dup();
-                final Type indexType = node.getIndex().getType();
-                if (indexType.isObject() || indexType.isBoolean()) {
-                    load(node.getIndex(), Type.OBJECT); //TODO
-                } else {
-                    load(node.getIndex());
-                }
-                method.dynamicGetIndex(node.getType(), getCallSiteFlags(), true);
-                method.swap();
-                method.dynamicCall(callNodeType, 2 + loadArgs(args), getCallSiteFlags());
-
+                new OptimisticOperation() {
+                    int argsCount;
+                    @Override
+                    void loadStack() {
+                        load(node.getBase(), Type.OBJECT);
+                        method.dup();
+                        final Type indexType = node.getIndex().getType();
+                        if (indexType.isObject() || indexType.isBoolean()) {
+                            load(node.getIndex(), Type.OBJECT); //TODO
+                        } else {
+                            load(node.getIndex());
+                        }
+                        // NOTE: not using a nested OptimisticOperation on this dynamicGetIndex, as we expect to get
+                        // back a callable object. Nobody in their right mind would optimistically type this call site.
+                        assert !node.isOptimistic();
+                        method.dynamicGetIndex(node.getType(), getCallSiteFlags(), true);
+                        method.swap();
+                        argsCount = loadArgs(args);
+                    }
+                    @Override
+                    void consumeStack() {
+                        final int flags = getCallSiteFlagsOptimistic(callNode);
+                        dynamicCall(method, callNode, callNodeType, 2 + argsCount, flags);
+                    }
+                }.emit(callNode);
                 return false;
             }
 
             @Override
             protected boolean enterDefault(final Node node) {
-                // Load up function.
-                load(function, Type.OBJECT); //TODO, e.g. booleans can be used as functions
-                method.loadNull(); // ScriptFunction will figure out the correct this when it sees CALLSITE_SCOPE
-                method.dynamicCall(callNodeType, 2 + loadArgs(args), getCallSiteFlags() | CALLSITE_SCOPE);
+                new OptimisticOperation() {
+                    int argsCount;
+                    @Override
+                    void loadStack() {
+                        // Load up function.
+                        load(function, Type.OBJECT); //TODO, e.g. booleans can be used as functions
+                        method.loadNull(); // ScriptFunction will figure out the correct this when it sees CALLSITE_SCOPE
+                        argsCount = loadArgs(args);
+                        }
+                        @Override
+                        void consumeStack() {
+                            final int flags = getCallSiteFlagsOptimistic(callNode) | CALLSITE_SCOPE;
+                            dynamicCall(method, callNode, callNodeType, 2 + argsCount, flags);
+                        }
+                }.emit(callNode);
 
                 return false;
             }
@@ -830,14 +1088,113 @@
         return false;
     }
 
+    private void convertOptimisticReturnValue(final Optimistic expr, final Type desiredType) {
+        if (expr.isOptimistic()) {
+            final Type optimisticType = getOptimisticCoercedType(desiredType, (Expression)expr);
+            if(!optimisticType.isObject()) {
+                method.load(expr.getProgramPoint());
+                if(optimisticType.isInteger()) {
+                    method.invoke(ENSURE_INT);
+                } else if(optimisticType.isLong()) {
+                    method.invoke(ENSURE_LONG);
+                } else if(optimisticType.isNumber()) {
+                    method.invoke(ENSURE_NUMBER);
+                } else {
+                    throw new AssertionError(optimisticType);
+                }
+            }
+        }
+        method.convert(desiredType);
+    }
+
+    /**
+     * Emits the correct dynamic getter code. Normally just delegates to method emitter, except when the target
+     * expression is optimistic, and the desired type is narrower than the optimistic type. In that case, it'll emit a
+     * dynamic getter with its original optimistic type, and explicitly insert a narrowing conversion. This way we can
+     * preserve the optimism of the values even if they're subsequently immediately coerced into a narrower type. This
+     * is beneficial because in this case we can still presume that since the original getter was optimistic, the
+     * conversion has no side effects.
+     * @param method the method emitter
+     * @param expr the expression that is being loaded through the getter
+     * @param desiredType the desired type for the loaded expression (coercible from its original type)
+     * @param name the name of the property being get
+     * @param flags call site flags
+     * @param isMethod whether we're preferrably retrieving a function
+     * @return the passed in method emitter
+     */
+    private static MethodEmitter dynamicGet(MethodEmitter method, Expression expr, Type desiredType, final String name, final int flags, boolean isMethod) {
+        final int finalFlags = maybeRemoveOptimisticFlags(desiredType, flags);
+        if(isOptimistic(finalFlags)) {
+            return method.dynamicGet(getOptimisticCoercedType(desiredType, expr), name, finalFlags, isMethod).convert(desiredType);
+        }
+        return method.dynamicGet(desiredType, name, finalFlags, isMethod);
+    }
+
+    private static MethodEmitter dynamicGetIndex(MethodEmitter method, Expression expr, Type desiredType, int flags, boolean isMethod) {
+        final int finalFlags = maybeRemoveOptimisticFlags(desiredType, flags);
+        if(isOptimistic(finalFlags)) {
+            return method.dynamicGetIndex(getOptimisticCoercedType(desiredType, expr), finalFlags, isMethod).convert(desiredType);
+        }
+        return method.dynamicGetIndex(desiredType, finalFlags, isMethod);
+    }
+
+    private static MethodEmitter dynamicCall(MethodEmitter method, Expression expr, Type desiredType, int argCount, int flags) {
+        final int finalFlags = maybeRemoveOptimisticFlags(desiredType, flags);
+        if(isOptimistic(finalFlags)) {
+            return method.dynamicCall(getOptimisticCoercedType(desiredType, expr), argCount, finalFlags).convert(desiredType);
+        }
+        return method.dynamicCall(desiredType, argCount, finalFlags);
+    }
+
+    /**
+     * Given an optimistic expression and a desired coercing type, returns the type that should be used as the return
+     * type of the dynamic invocation that is emitted as the code for the expression load. If the coercing type is
+     * either boolean or narrower than the expression's optimistic type, then the optimistic type is returned, otherwise
+     * the coercing type. Note that if you use this method to determine the return type of the code for the expression,
+     * you will need to add an explicit {@link MethodEmitter#convert(Type)} after it to make sure that any further
+     * coercing is done into the final type in case the returned type here was the optimistic type. Effectively, this
+     * method allows for moving the coercion into the optimistic type when it won't adversely affect the optimistic
+     * evaluation semantics, and for preserving the optimistic type and doing a separate coercion when it would affect
+     * it.
+     * @param coercingType the type into which the expression will ultimately be coerced
+     * @param optimisticExpr the optimistic expression that will be coerced after evaluation.
+     * @return
+     */
+    private static Type getOptimisticCoercedType(final Type coercingType, final Expression optimisticExpr) {
+        assert optimisticExpr instanceof Optimistic && ((Optimistic)optimisticExpr).isOptimistic();
+        final Type optimisticType = optimisticExpr.getType();
+        if(coercingType.isBoolean() || coercingType.narrowerThan(optimisticType)) {
+            return optimisticType;
+        }
+        return coercingType;
+    }
+
+    /**
+     * If given an object type, ensures that the flags have their optimism removed (object return valued expressions are
+     * never optimistic).
+     * @param type the return value type
+     * @param flags original flags
+     * @return either the original flags, or flags with optimism stripped, if the return value type is object
+     */
+    private static int maybeRemoveOptimisticFlags(Type type, int flags) {
+        return type.isObject() ? nonOptimisticFlags(flags) : flags;
+    }
+
+    /**
+     * Returns the flags with optimistic flag and program point removed.
+     * @param flags the flags that need optimism stripped from them.
+     * @return flags without optimism
+     */
+    static int nonOptimisticFlags(int flags) {
+        return flags & ~(CALLSITE_OPTIMISTIC | (-1 << CALLSITE_PROGRAM_POINT_SHIFT));
+    }
+
     @Override
     public boolean enterContinueNode(final ContinueNode continueNode) {
-        lineNumber(continueNode);
+        enterStatement(continueNode);
 
         final LoopNode continueTo = lc.getContinueTo(continueNode.getLabel());
-        for (int i = 0; i < lc.getScopeNestingLevelTo(continueTo); i++) {
-            closeWith();
-        }
+        popScopesUntil(continueTo);
         method.splitAwareGoto(lc, continueTo.getContinueLabel());
 
         return false;
@@ -845,23 +1202,25 @@
 
     @Override
     public boolean enterEmptyNode(final EmptyNode emptyNode) {
-        lineNumber(emptyNode);
+        enterStatement(emptyNode);
 
         return false;
     }
 
     @Override
     public boolean enterExpressionStatement(final ExpressionStatement expressionStatement) {
-        lineNumber(expressionStatement);
-
-        expressionStatement.getExpression().accept(this);
+        enterStatement(expressionStatement);
+
+        final Expression expr = expressionStatement.getExpression();
+        assert expr.isTokenType(TokenType.DISCARD);
+        expr.accept(this);
 
         return false;
     }
 
     @Override
     public boolean enterBlockStatement(final BlockStatement blockStatement) {
-        lineNumber(blockStatement);
+        enterStatement(blockStatement);
 
         blockStatement.getBlock().accept(this);
 
@@ -870,7 +1229,7 @@
 
     @Override
     public boolean enterForNode(final ForNode forNode) {
-        lineNumber(forNode);
+        enterStatement(forNode);
 
         if (forNode.isForIn()) {
             enterForIn(forNode);
@@ -899,6 +1258,8 @@
         body.accept(this);
         method.label(forNode.getContinueLabel());
 
+        lineNumber(forNode);
+
         if (!body.isTerminal() && modify != null) {
             load(modify);
         }
@@ -931,8 +1292,9 @@
         new Store<Expression>(init) {
             @Override
             protected void storeNonDiscard() {
-                return;
+                //empty
             }
+
             @Override
             protected void evaluate() {
                 method.load(iter);
@@ -961,7 +1323,7 @@
 
         final FunctionNode function = lc.getCurrentFunction();
         if (isFunctionBody) {
-            if(method.hasScope()) {
+            if (method.hasScope()) {
                 if (function.needsParentScope()) {
                     method.loadCompilerConstant(CALLEE);
                     method.invoke(ScriptFunction.GET_SCOPE);
@@ -974,6 +1336,15 @@
             if (function.needsArguments()) {
                 initArguments(function);
             }
+            final Symbol returnSymbol = block.getExistingSymbol(RETURN.symbolName());
+            if(returnSymbol.hasSlot() && useOptimisticTypes() &&
+               // NOTE: a program that has no declared functions will assign ":return = UNDEFINED" first thing as it
+               // starts to run, so we don't have to force initialize :return (see Lower.enterBlock()).
+               !(function.isProgram() && !function.hasDeclaredFunctions()))
+            {
+                method.loadForcedInitializer(returnSymbol.getSymbolType());
+                method.store(returnSymbol);
+            }
         }
 
         /*
@@ -988,61 +1359,79 @@
 
             // TODO for LET we can do better: if *block* does not contain any eval/with, we don't need its vars in scope.
 
-            final List<String> nameList = new ArrayList<>();
-            final List<Symbol> locals   = new ArrayList<>();
-
-            // Initalize symbols and values
-            final List<Symbol> newSymbols = new ArrayList<>();
-            final List<Symbol> values     = new ArrayList<>();
-
+            final List<Symbol> localsToInitialize = new ArrayList<>();
             final boolean hasArguments = function.needsArguments();
+            final List<MapTuple<Symbol>> tuples = new ArrayList<>();
 
             for (final Symbol symbol : block.getSymbols()) {
-
-                if (symbol.isInternal() || symbol.isThis() || symbol.isTemp()) {
+                if (symbol.isInternal() && !symbol.isThis()) {
+                    if (symbol.hasSlot()) {
+                        localsToInitialize.add(symbol);
+                    }
                     continue;
                 }
 
+                if (symbol.isThis() || symbol.isTemp()) {
+                    continue;
+                }
+
                 if (symbol.isVar()) {
+                    assert !varsInScope || symbol.isScope();
                     if (varsInScope || symbol.isScope()) {
-                        nameList.add(symbol.getName());
-                        newSymbols.add(symbol);
-                        values.add(null);
                         assert symbol.isScope()   : "scope for " + symbol + " should have been set in Lower already " + function.getName();
                         assert !symbol.hasSlot()  : "slot for " + symbol + " should have been removed in Lower already" + function.getName();
+                        tuples.add(new MapTuple<Symbol>(symbol.getName(), symbol) {
+                            //this tuple will not be put fielded, as it has no value, just a symbol
+                            @Override
+                            public boolean isPrimitive() {
+                                return symbol.getSymbolType().isPrimitive();
+                            }
+                        });
                     } else {
                         assert symbol.hasSlot() : symbol + " should have a slot only, no scope";
-                        locals.add(symbol);
+                        localsToInitialize.add(symbol);
                     }
                 } else if (symbol.isParam() && (varsInScope || hasArguments || symbol.isScope())) {
-                    nameList.add(symbol.getName());
-                    newSymbols.add(symbol);
-                    values.add(hasArguments ? null : symbol);
                     assert symbol.isScope()   : "scope for " + symbol + " should have been set in Lower already " + function.getName() + " varsInScope="+varsInScope+" hasArguments="+hasArguments+" symbol.isScope()=" + symbol.isScope();
                     assert !(hasArguments && symbol.hasSlot())  : "slot for " + symbol + " should have been removed in Lower already " + function.getName();
+                    tuples.add(new MapTuple<Symbol>(symbol.getName(), symbol, hasArguments ? null : symbol) {
+                        //this symbol will be put fielded, we can't initialize it as undefined with a known type
+                        @Override
+                        public Class<?> getValueType() {
+                            return (OBJECT_FIELDS_ONLY || value == null || value.getSymbolType().isBoolean()) ? Object.class : value.getSymbolType().getTypeClass();
+                            //return OBJECT_FIELDS_ONLY ? Object.class : symbol.getSymbolType().getTypeClass();
+                        }
+                    });
                 }
             }
 
             // we may have locals that need to be initialized
-            initSymbols(locals);
+            initSymbols(localsToInitialize);
 
             /*
              * Create a new object based on the symbols and values, generate
              * bootstrap code for object
              */
-            new FieldObjectCreator<Symbol>(this, nameList, newSymbols, values, true, hasArguments) {
+            new FieldObjectCreator<Symbol>(this, tuples, true, hasArguments) {
                 @Override
                 protected void loadValue(final Symbol value) {
                     method.load(value);
                 }
             }.makeObject(method);
-
-            // runScript(): merge scope into global
+            // program function: merge scope into global
             if (isFunctionBody && function.isProgram()) {
                 method.invoke(ScriptRuntime.MERGE_SCOPE);
             }
 
             method.storeCompilerConstant(SCOPE);
+            if(!isFunctionBody) {
+                // Function body doesn't need a try/catch to restore scope, as it'd be a dead store anyway. Allowing it
+                // actually causes issues with UnwarrantedOptimismException handlers as ASM will sort this handler to
+                // the top of the exception handler table, so it'll be triggered instead of the UOE handlers.
+                final Label scopeEntryLabel = new Label("");
+                scopeEntryLabels.push(scopeEntryLabel);
+                method.label(scopeEntryLabel);
+            }
         } else {
             // Since we don't have a scope, parameters didn't get assigned array indices by the FieldObjectCreator, so
             // we need to assign them separately here.
@@ -1075,15 +1464,33 @@
         method.storeCompilerConstant(ARGUMENTS);
     }
 
+    /**
+     * Should this code generator skip generating code for inner functions? If lazy compilation is on, or we're
+     * doing an on-demand ("just-in-time") compilation, then we aren't generating code for inner functions.
+     */
+    private boolean compileOutermostOnly() {
+        return RecompilableScriptFunctionData.LAZY_COMPILATION || compiler.getCompilationEnvironment().isOnDemandCompilation();
+    }
+
     @Override
     public boolean enterFunctionNode(final FunctionNode functionNode) {
-        if (functionNode.isLazy()) {
-            // Must do it now; can't postpone it until leaveFunctionNode()
-            newFunctionObject(functionNode, functionNode);
+        final int fnId = functionNode.getId();
+        Map<Integer, RecompilableScriptFunctionData> nestedFunctions = fnIdToNestedFunctions.get(fnId);
+        if (nestedFunctions == null) {
+            nestedFunctions = new HashMap<>();
+            fnIdToNestedFunctions.put(fnId, nestedFunctions);
+        }
+
+        // Nested functions are not visited when we either recompile or lazily compile, only the outermost function is.
+        if (compileOutermostOnly() && (lc.getOutermostFunction() != functionNode)) {
+            // In case we are not generating code for the function, we must create or retrieve the function object and
+            // load it on the stack here.
+            newFunctionObject(functionNode, false);
             return false;
         }
 
         final String fnName = functionNode.getName();
+
         // NOTE: we only emit the method for a function with the given name once. We can have multiple functions with
         // the same name as a result of inlining finally blocks. However, in the future -- with type specialization,
         // notably -- we might need to check for both name *and* signature. Of course, even that might not be
@@ -1092,18 +1499,31 @@
         // to decide to either generate a unique method for each inlined copy of the function, maybe figure out its
         // exact type closure and deduplicate based on that, or just decide that functions in finally blocks aren't
         // worth it, and generate one method with most generic type closure.
-        if(!emittedMethods.contains(fnName)) {
+        if (!emittedMethods.contains(fnName)) {
             LOG.info("=== BEGIN ", fnName);
 
             assert functionNode.getCompileUnit() != null : "no compile unit for " + fnName + " " + Debug.id(functionNode);
             unit = lc.pushCompileUnit(functionNode.getCompileUnit());
             assert lc.hasCompileUnits();
 
-            method = lc.pushMethodEmitter(unit.getClassEmitter().method(functionNode));
+            final CompilationEnvironment compEnv = compiler.getCompilationEnvironment();
+            final boolean isRestOf = compEnv.isCompileRestOf();
+            final ClassEmitter classEmitter = unit.getClassEmitter();
+            method = lc.pushMethodEmitter(isRestOf ? classEmitter.restOfMethod(functionNode) : classEmitter.method(functionNode));
+            if(useOptimisticTypes()) {
+                lc.pushUnwarrantedOptimismHandlers();
+            }
+
             // new method - reset last line number
             lastLineNumber = -1;
             // Mark end for variable tables.
             method.begin();
+
+            if (isRestOf) {
+                final ContinuationInfo ci = new ContinuationInfo();
+                fnIdToContinuationInfo.put(fnId, ci);
+                method._goto(ci.handlerLabel);
+            }
         }
 
         return true;
@@ -1112,15 +1532,24 @@
     @Override
     public Node leaveFunctionNode(final FunctionNode functionNode) {
         try {
-            if(emittedMethods.add(functionNode.getName())) {
+            final boolean markOptimistic;
+            if (emittedMethods.add(functionNode.getName())) {
+                markOptimistic = generateUnwarrantedOptimismExceptionHandlers();
+                generateContinuationHandler();
                 method.end(); // wrap up this method
                 unit   = lc.popCompileUnit(functionNode.getCompileUnit());
                 method = lc.popMethodEmitter(method);
                 LOG.info("=== END ", functionNode.getName());
+            } else {
+                markOptimistic = false;
             }
 
-            final FunctionNode newFunctionNode = functionNode.setState(lc, CompilationState.EMITTED);
-            newFunctionObject(newFunctionNode, functionNode);
+            FunctionNode newFunctionNode = functionNode.setState(lc, CompilationState.EMITTED);
+            if(markOptimistic) {
+                newFunctionNode = newFunctionNode.setFlag(lc, FunctionNode.IS_OPTIMISTIC);
+            }
+
+            newFunctionObject(newFunctionNode, true);
             return newFunctionNode;
         } catch (final Throwable t) {
             Context.printStackTrace(t);
@@ -1137,7 +1566,7 @@
 
     @Override
     public boolean enterIfNode(final IfNode ifNode) {
-        lineNumber(ifNode);
+        enterStatement(ifNode);
 
         final Expression test = ifNode.getTest();
         final Block pass = ifNode.getPass();
@@ -1178,6 +1607,10 @@
         return false;
     }
 
+    private void enterStatement(final Statement statement) {
+        lineNumber(statement);
+    }
+
     private void lineNumber(final Statement statement) {
         lineNumber(statement.getLineNumber());
     }
@@ -1212,6 +1645,7 @@
         final Type elementType = arrayType.getElementType();
 
         if (units != null) {
+            lc.enterSplitNode();
             final MethodEmitter savedMethod     = method;
             final FunctionNode  currentFunction = lc.getCurrentFunction();
 
@@ -1251,6 +1685,7 @@
 
                 unit = lc.popCompileUnit(unit);
             }
+            lc.exitSplitNode();
 
             return method;
         }
@@ -1314,28 +1749,32 @@
      * @param object object to load
      */
     void loadConstant(final Object object) {
-        final String       unitClassName = unit.getUnitClassName();
-        final ClassEmitter classEmitter  = unit.getClassEmitter();
+        loadConstant(object, unit, method);
+    }
+
+    private void loadConstant(final Object object, final CompileUnit compileUnit, final MethodEmitter methodEmitter) {
+        final String       unitClassName = compileUnit.getUnitClassName();
+        final ClassEmitter classEmitter  = compileUnit.getClassEmitter();
         final int          index         = compiler.getConstantData().add(object);
         final Class<?>     cls           = object.getClass();
 
         if (cls == PropertyMap.class) {
-            method.load(index);
-            method.invokestatic(unitClassName, GET_MAP.symbolName(), methodDescriptor(PropertyMap.class, int.class));
+            methodEmitter.load(index);
+            methodEmitter.invokestatic(unitClassName, GET_MAP.symbolName(), methodDescriptor(PropertyMap.class, int.class));
             classEmitter.needGetConstantMethod(PropertyMap.class);
         } else if (cls.isArray()) {
-            method.load(index);
+            methodEmitter.load(index);
             final String methodName = ClassEmitter.getArrayMethodName(cls);
-            method.invokestatic(unitClassName, methodName, methodDescriptor(cls, int.class));
+            methodEmitter.invokestatic(unitClassName, methodName, methodDescriptor(cls, int.class));
             classEmitter.needGetConstantMethod(cls);
         } else {
-            method.loadConstants().load(index).arrayload();
+            methodEmitter.loadConstants().load(index).arrayload();
             if (object instanceof ArrayData) {
                 // avoid cast to non-public ArrayData subclass
-                method.checkcast(ArrayData.class);
-                method.invoke(virtualCallNoLookup(ArrayData.class, "copy", ArrayData.class));
+                methodEmitter.checkcast(ArrayData.class);
+                methodEmitter.invoke(virtualCallNoLookup(ArrayData.class, "copy", ArrayData.class));
             } else if (cls != Object.class) {
-                method.checkcast(cls);
+                methodEmitter.checkcast(cls);
             }
         }
     }
@@ -1382,7 +1821,7 @@
             loadArray(arrayLiteral, atype);
             globalAllocateArray(atype);
         } else {
-            assert false : "Unknown literal for " + node.getClass() + " " + value.getClass() + " " + value;
+            throw new UnsupportedOperationException("Unknown literal for " + node.getClass() + " " + value.getClass() + " " + value);
         }
 
         return method;
@@ -1437,61 +1876,58 @@
     public boolean enterObjectNode(final ObjectNode objectNode) {
         final List<PropertyNode> elements = objectNode.getElements();
 
-        final List<String>     keys    = new ArrayList<>();
-        final List<Symbol>     symbols = new ArrayList<>();
-        final List<Expression> values  = new ArrayList<>();
-
-        boolean hasGettersSetters = false;
+        final List<MapTuple<Expression>> tuples = new ArrayList<>();
+        final List<PropertyNode> gettersSetters = new ArrayList<>();
         Expression protoNode = null;
 
-        for (PropertyNode propertyNode: elements) {
-            final Expression   value        = propertyNode.getValue();
-            final String       key          = propertyNode.getKeyName();
-            final Symbol       symbol       = value == null ? null : propertyNode.getKey().getSymbol();
+        boolean restOfProperty = false;
+        final CompilationEnvironment env = compiler.getCompilationEnvironment();
+        final int ccp = env.getCurrentContinuationEntryPoint();
+
+        for (final PropertyNode propertyNode : elements) {
+            final Expression   value  = propertyNode.getValue();
+            final String       key    = propertyNode.getKeyName();
+            final Symbol       symbol = value == null ? null : propertyNode.getKey().getSymbol();
 
             if (value == null) {
-                hasGettersSetters = true;
+                gettersSetters.add(propertyNode);
             } else if (key.equals(ScriptObject.PROTO_PROPERTY_NAME)) {
                 protoNode = value;
                 continue;
             }
 
-            keys.add(key);
-            symbols.add(symbol);
-            values.add(value);
+            restOfProperty |=
+                value != null &&
+                isValid(ccp) &&
+                value instanceof Optimistic &&
+                ((Optimistic)value).getProgramPoint() == ccp;
+
+            //for literals, a value of null means object type, i.e. the value null or getter setter function
+            //(I think)
+            tuples.add(new MapTuple<Expression>(key, symbol, value) {
+                @Override
+                public Class<?> getValueType() {
+                    return (OBJECT_FIELDS_ONLY || value == null || value.getType().isBoolean()) ? Object.class : value.getType().getTypeClass();
+                }
+            });
         }
 
+        final ObjectCreator<?> oc;
         if (elements.size() > OBJECT_SPILL_THRESHOLD) {
-            new SpillObjectCreator(this, keys, symbols, values).makeObject(method);
+            oc = new SpillObjectCreator(this, tuples);
         } else {
-            new FieldObjectCreator<Expression>(this, keys, symbols, values) {
+            oc = new FieldObjectCreator<Expression>(this, tuples) {
                 @Override
                 protected void loadValue(final Expression node) {
                     load(node);
-                }
-
-                /**
-                 * Ensure that the properties start out as object types so that
-                 * we can do putfield initializations instead of dynamicSetIndex
-                 * which would be the case to determine initial property type
-                 * otherwise.
-                 *
-                 * Use case, it's very expensive to do a million var x = {a:obj, b:obj}
-                 * just to have to invalidate them immediately on initialization
-                 *
-                 * see NASHORN-594
-                 */
-                @Override
-                protected MapCreator newMapCreator(final Class<?> fieldObjectClass) {
-                    return new MapCreator(fieldObjectClass, keys, symbols) {
-                        @Override
-                        protected int getPropertyFlags(final Symbol symbol, final boolean hasArguments) {
-                            return super.getPropertyFlags(symbol, hasArguments) | Property.IS_ALWAYS_OBJECT;
-                        }
-                    };
-                }
-
-            }.makeObject(method);
+                }};
+        }
+        oc.makeObject(method);
+        //if this is a rest of method and our continuation point was found as one of the values
+        //in the properties above, we need to reset the map to oc.getMap() in the continuation
+        //handler
+        if (restOfProperty) {
+            getContinuationInfo().objectLiteralMap = oc.getMap();
         }
 
         method.dup();
@@ -1503,31 +1939,26 @@
             method.invoke(ScriptObject.SET_PROTO);
         }
 
-        if (hasGettersSetters) {
-            for (final PropertyNode propertyNode : elements) {
-                final FunctionNode getter       = propertyNode.getGetter();
-                final FunctionNode setter       = propertyNode.getSetter();
-
-                if (getter == null && setter == null) {
-                    continue;
-                }
-
-                method.dup().loadKey(propertyNode.getKey());
-
-                if (getter == null) {
-                    method.loadNull();
-                } else {
-                    getter.accept(this);
-                }
-
-                if (setter == null) {
-                    method.loadNull();
-                } else {
-                    setter.accept(this);
-                }
-
-                method.invoke(ScriptObject.SET_USER_ACCESSORS);
+        for (final PropertyNode propertyNode : gettersSetters) {
+            final FunctionNode getter = propertyNode.getGetter();
+            final FunctionNode setter = propertyNode.getSetter();
+
+            assert getter != null || setter != null;
+
+            method.dup().loadKey(propertyNode.getKey());
+            if (getter == null) {
+                method.loadNull();
+            } else {
+                getter.accept(this);
             }
+
+            if (setter == null) {
+                method.loadNull();
+            } else {
+                setter.accept(this);
+            }
+
+            method.invoke(ScriptObject.SET_USER_ACCESSORS);
         }
 
         method.store(objectNode.getSymbol());
@@ -1536,7 +1967,7 @@
 
     @Override
     public boolean enterReturnNode(final ReturnNode returnNode) {
-        lineNumber(returnNode);
+        enterStatement(returnNode);
 
         method.registerReturn();
 
@@ -1558,7 +1989,7 @@
         return node instanceof LiteralNode<?> && ((LiteralNode<?>) node).isNull();
     }
 
-    private boolean nullCheck(final RuntimeNode runtimeNode, final List<Expression> args, final String signature) {
+    private boolean nullCheck(final RuntimeNode runtimeNode, final List<Expression> args) {
         final Request request = runtimeNode.getRequest();
 
         if (!Request.isEQ(request) && !Request.isNE(request)) {
@@ -1576,56 +2007,71 @@
             rhs = tmp;
         }
 
+        if (!isNullLiteral(rhs)) {
+            return false;
+        }
+
+        if (!lhs.getType().isObject()) {
+            return false;
+        }
+
         // this is a null literal check, so if there is implicit coercion
         // involved like {D}x=null, we will fail - this is very rare
-        if (isNullLiteral(rhs) && lhs.getType().isObject()) {
-            final Label trueLabel  = new Label("trueLabel");
-            final Label falseLabel = new Label("falseLabel");
-            final Label endLabel   = new Label("end");
-
-            load(lhs);
-            method.dup();
-            if (Request.isEQ(request)) {
-                method.ifnull(trueLabel);
-            } else if (Request.isNE(request)) {
-                method.ifnonnull(trueLabel);
-            } else {
-                assert false : "Invalid request " + request;
+        final Label trueLabel  = new Label("trueLabel");
+        final Label falseLabel = new Label("falseLabel");
+        final Label endLabel   = new Label("end");
+
+        load(lhs);    //lhs
+        final Label popLabel;
+        if (!Request.isStrict(request)) {
+            method.dup(); //lhs lhs
+            popLabel = new Label("pop");
+        } else {
+            popLabel = null;
+        }
+
+        if (Request.isEQ(request)) {
+            method.ifnull(!Request.isStrict(request) ? popLabel : trueLabel);
+            if (!Request.isStrict(request)) {
+                method.loadUndefined(Type.OBJECT);
+                method.if_acmpeq(trueLabel);
             }
-
             method.label(falseLabel);
-            load(rhs);
-            method.invokestatic(CompilerConstants.className(ScriptRuntime.class), request.toString(), signature);
+            method.load(false);
             method._goto(endLabel);
-
+            if (!Request.isStrict(request)) {
+                method.label(popLabel);
+                method.pop();
+            }
             method.label(trueLabel);
-            // if NE (not strict) this can be "undefined != null" which is supposed to be false
-            if (request == Request.NE) {
+            method.load(true);
+            method.label(endLabel);
+        } else if (Request.isNE(request)) {
+            method.ifnull(!Request.isStrict(request) ? popLabel : falseLabel);
+            if (!Request.isStrict(request)) {
                 method.loadUndefined(Type.OBJECT);
-                final Label isUndefined = new Label("isUndefined");
-                final Label afterUndefinedCheck = new Label("afterUndefinedCheck");
-                method.if_acmpeq(isUndefined);
-                // not undefined
-                method.load(true);
-                method._goto(afterUndefinedCheck);
-                method.label(isUndefined);
-                method.load(false);
-                method.label(afterUndefinedCheck);
-            } else {
+                method.if_acmpeq(falseLabel);
+            }
+            method.label(trueLabel);
+            method.load(true);
+            method._goto(endLabel);
+            if (!Request.isStrict(request)) {
+                method.label(popLabel);
                 method.pop();
-                method.load(true);
             }
+            method.label(falseLabel);
+            method.load(false);
             method.label(endLabel);
-            method.convert(runtimeNode.getType());
-            method.store(runtimeNode.getSymbol());
-
-            return true;
         }
 
-        return false;
+        assert runtimeNode.getType().isBoolean();
+        method.convert(runtimeNode.getType());
+        method.store(runtimeNode.getSymbol());
+
+        return true;
     }
 
-    private boolean specializationCheck(final RuntimeNode.Request request, final Expression node, final List<Expression> args) {
+    private boolean specializationCheck(final RuntimeNode.Request request, final RuntimeNode node, final List<Expression> args) {
         if (!request.canSpecialize()) {
             return false;
         }
@@ -1633,32 +2079,41 @@
         assert args.size() == 2;
         final Type returnType = node.getType();
 
-        load(args.get(0));
-        load(args.get(1));
-
-        Request finalRequest = request;
-
-        //if the request is a comparison, i.e. one that can be reversed
-        //it keeps its semantic, but make sure that the object comes in
-        //last
-        final Request reverse = Request.reverse(request);
-        if (method.peekType().isObject() && reverse != null) { //rhs is object
-            if (!method.peekType(1).isObject()) { //lhs is not object
-                method.swap(); //prefer object as lhs
-                finalRequest = reverse;
+        new OptimisticOperation() {
+            private Request finalRequest = request;
+
+            @Override
+            void loadStack() {
+                load(args.get(0));
+                load(args.get(1));
+
+
+                //if the request is a comparison, i.e. one that can be reversed
+                //it keeps its semantic, but make sure that the object comes in
+                //last
+                final Request reverse = Request.reverse(request);
+                if (method.peekType().isObject() && reverse != null) { //rhs is object
+                    if (!method.peekType(1).isObject()) { //lhs is not object
+                        method.swap(); //prefer object as lhs
+                        finalRequest = reverse;
+                    }
+                }
             }
-        }
-
-        method.dynamicRuntimeCall(
-                new SpecializedRuntimeNode(
-                    finalRequest,
-                    new Type[] {
-                        method.peekType(1),
-                        method.peekType()
-                    },
-                    returnType).getInitialName(),
-                returnType,
-                finalRequest);
+            @Override
+            void consumeStack() {
+                method.dynamicRuntimeCall(
+                        new SpecializedRuntimeNode(
+                            finalRequest,
+                            new Type[] {
+                                method.peekType(1),
+                                method.peekType()
+                            },
+                            returnType).getInitialName(),
+                        returnType,
+                        finalRequest);
+
+            }
+        }.emit(node);
 
         method.convert(node.getType());
         method.store(node.getSymbol());
@@ -1681,8 +2136,6 @@
         final List<Expression> args = runtimeNode.getArgs();
         if (runtimeNode.isPrimitive() && !runtimeNode.isFinal() && isReducible(runtimeNode.getRequest())) {
             final Expression lhs = args.get(0);
-            assert args.size() > 1 : runtimeNode + " must have two args";
-            final Expression rhs = args.get(1);
 
             final Type   type   = runtimeNode.getType();
             final Symbol symbol = runtimeNode.getSymbol();
@@ -1690,23 +2143,33 @@
             switch (runtimeNode.getRequest()) {
             case EQ:
             case EQ_STRICT:
-                return enterCmp(lhs, rhs, Condition.EQ, type, symbol);
+                return enterCmp(lhs, args.get(1), Condition.EQ, type, symbol);
             case NE:
             case NE_STRICT:
-                return enterCmp(lhs, rhs, Condition.NE, type, symbol);
+                return enterCmp(lhs, args.get(1), Condition.NE, type, symbol);
             case LE:
-                return enterCmp(lhs, rhs, Condition.LE, type, symbol);
+                return enterCmp(lhs, args.get(1), Condition.LE, type, symbol);
             case LT:
-                return enterCmp(lhs, rhs, Condition.LT, type, symbol);
+                return enterCmp(lhs, args.get(1), Condition.LT, type, symbol);
             case GE:
-                return enterCmp(lhs, rhs, Condition.GE, type, symbol);
+                return enterCmp(lhs, args.get(1), Condition.GE, type, symbol);
             case GT:
-                return enterCmp(lhs, rhs, Condition.GT, type, symbol);
+                return enterCmp(lhs, args.get(1), Condition.GT, type, symbol);
             case ADD:
-                Type widest = Type.widest(lhs.getType(), rhs.getType());
-                load(lhs, widest);
-                load(rhs, widest);
-                method.add();
+                final Expression rhs = args.get(1);
+                final Type widest = Type.widest(lhs.getType(), rhs.getType());
+                new OptimisticOperation() {
+                    @Override
+                    void loadStack() {
+                        load(lhs, widest);
+                        load(rhs, widest);
+                    }
+
+                    @Override
+                    void consumeStack() {
+                        method.add(runtimeNode.getProgramPoint());
+                    }
+                }.emit(runtimeNode);
                 method.convert(type);
                 method.store(symbol);
                 return false;
@@ -1717,7 +2180,7 @@
             }
         }
 
-        if (nullCheck(runtimeNode, args, new FunctionSignature(false, false, runtimeNode.getType(), args).toString())) {
+        if (nullCheck(runtimeNode, args)) {
            return false;
         }
 
@@ -1725,18 +2188,26 @@
            return false;
         }
 
-        for (final Expression arg : args) {
-            load(arg, Type.OBJECT);
-        }
-
-        method.invokestatic(
-            CompilerConstants.className(ScriptRuntime.class),
-            runtimeNode.getRequest().toString(),
-            new FunctionSignature(
-                false,
-                false,
-                runtimeNode.getType(),
-                args.size()).toString());
+        new OptimisticOperation() {
+            @Override
+            void loadStack() {
+                for (final Expression arg : args) {
+                    load(arg, Type.OBJECT);
+                }
+            }
+            @Override
+            void consumeStack() {
+                method.invokestatic(
+                        CompilerConstants.className(ScriptRuntime.class),
+                        runtimeNode.getRequest().toString(),
+                        new FunctionSignature(
+                            false,
+                            false,
+                            runtimeNode.getType(),
+                            args.size()).toString());
+            }
+        }.emit(runtimeNode);
+
         method.convert(runtimeNode.getType());
         method.store(runtimeNode.getSymbol());
 
@@ -1754,7 +2225,7 @@
         final Class<?>   rtype          = fn.getReturnType().getTypeClass();
         final boolean    needsArguments = fn.needsArguments();
         final Class<?>[] ptypes         = needsArguments ?
-                new Class<?>[] {ScriptFunction.class, Object.class, ScriptObject.class, Object.class} :
+                new Class<?>[] {ScriptFunction.class, Object.class, ScriptObject.class, ScriptObject.class} :
                 new Class<?>[] {ScriptFunction.class, Object.class, ScriptObject.class};
 
         final MethodEmitter caller = method;
@@ -1798,7 +2269,7 @@
     private void fixScopeSlot(final FunctionNode functionNode) {
         // TODO hack to move the scope to the expected slot (needed because split methods reuse the same slots as the root method)
         if (functionNode.compilerConstant(SCOPE).getSlot() != SCOPE.slot()) {
-            method.load(Type.typeFor(ScriptObject.class), SCOPE.slot());
+            method.load(SCOPE_TYPE, SCOPE.slot());
             method.storeCompilerConstant(SCOPE);
         }
     }
@@ -1821,7 +2292,7 @@
 
         } catch (final Throwable t) {
             Context.printStackTrace(t);
-            final VerifyError e = new VerifyError("Code generation bug in \"" + splitNode.getName() + "\": likely stack misaligned: " + t + " " + lc.getCurrentFunction().getSource().getName());
+            final VerifyError e = new VerifyError("Code generation bug in \"" + splitNode.getName() + "\": likely stack misaligned: " + t + " " + getCurrentSource().getName());
             e.initCause(t);
             throw e;
         }
@@ -1889,7 +2360,7 @@
 
     @Override
     public boolean enterSwitchNode(final SwitchNode switchNode) {
-        lineNumber(switchNode);
+        enterStatement(switchNode);
 
         final Expression     expression  = switchNode.getExpression();
         final Symbol         tag         = switchNode.getTag();
@@ -1964,10 +2435,9 @@
             }
 
             // If reasonable size and not too sparse (80%), use table otherwise use lookup.
-            if (range > 0 && range < 4096 && range < (size * 5 / 4)) {
+            if (range > 0 && range < 4096 && range <= (size * 5 / 4)) {
                 final Label[] table = new Label[range];
                 Arrays.fill(table, defaultLabel);
-
                 for (int i = 0; i < size; i++) {
                     final int value = values[i];
                     table[value - lo] = labels[i];
@@ -2014,7 +2484,7 @@
 
     @Override
     public boolean enterThrowNode(final ThrowNode throwNode) {
-        lineNumber(throwNode);
+        enterStatement(throwNode);
 
         if (throwNode.isSyntheticRethrow()) {
             //do not wrap whatever this is in an ecma exception, just rethrow it
@@ -2023,13 +2493,17 @@
             return false;
         }
 
-        final Source source     = lc.getCurrentFunction().getSource();
-
+        final Source     source     = getCurrentSource();
         final Expression expression = throwNode.getExpression();
         final int        position   = throwNode.position();
         final int        line       = throwNode.getLineNumber();
         final int        column     = source.getColumn(position);
 
+        // NOTE: we first evaluate the expression, and only after it was evaluated do we create the new ECMAException
+        // object and then somewhat cumbersomely move it beneath the evaluated expression on the stack. The reason for
+        // this is that if expression is optimistic (or contains an optimistic subexpression), we'd potentially access
+        // the not-yet-<init>ialized object on the stack from the UnwarrantedOptimismException handler, and bytecode
+        // verifier forbids that.
         load(expression, Type.OBJECT);
 
         method.load(source.getName());
@@ -2042,9 +2516,13 @@
         return false;
     }
 
+    private Source getCurrentSource() {
+        return lc.getCurrentFunction().getSource();
+    }
+
     @Override
     public boolean enterTryNode(final TryNode tryNode) {
-        lineNumber(tryNode);
+        enterStatement(tryNode);
 
         final Block       body        = tryNode.getBody();
         final List<Block> catchBlocks = tryNode.getCatchBlocks();
@@ -2053,7 +2531,6 @@
         final Label       recovery    = new Label("catch");
         final Label       exit        = tryNode.getExit();
         final Label       skip        = new Label("skip");
-
         method.label(entry);
 
         body.accept(this);
@@ -2062,12 +2539,14 @@
             method._goto(skip);
         }
 
+        method._try(entry, exit, recovery, Throwable.class);
         method.label(exit);
 
         method._catch(recovery);
         method.store(symbol);
 
-        for (int i = 0; i < catchBlocks.size(); i++) {
+        final int catchBlockCount = catchBlocks.size();
+        for (int i = 0; i < catchBlockCount; i++) {
             final Block catchBlock = catchBlocks.get(i);
 
             //TODO this is very ugly - try not to call enter/leave methods directly
@@ -2084,7 +2563,7 @@
             new Store<IdentNode>(exception) {
                 @Override
                 protected void storeNonDiscard() {
-                    return;
+                    //empty
                 }
 
                 @Override
@@ -2106,38 +2585,41 @@
                 }
             }.store();
 
-            final Label next;
-
-            if (exceptionCondition != null) {
-                next = new Label("next");
-                load(exceptionCondition, Type.BOOLEAN).ifeq(next);
-            } else {
-                next = null;
+            final boolean isConditionalCatch = exceptionCondition != null;
+            if (isConditionalCatch) {
+                load(exceptionCondition, Type.BOOLEAN);
+                // If catch body doesn't terminate the flow, then when we reach its break label, we could've come in
+                // through either true or false branch, so we'll need a copy of the boolean evaluation on the stack to
+                // know which path we took. On the other hand, if it does terminate the flow, then we won't have the
+                // boolean on the top of the stack at the jump join point, so we must not push it on the stack.
+                if(!catchBody.hasTerminalFlags()) {
+                    method.dup();
+                }
+                method.ifeq(catchBlock.getBreakLabel());
             }
 
             catchBody.accept(this);
 
-            if (i + 1 != catchBlocks.size() && !catchBody.hasTerminalFlags()) {
-                method._goto(skip);
-            }
-
-            if (next != null) {
-                if (i + 1 == catchBlocks.size()) {
-                    // no next catch block - rethrow if condition failed
-                    method._goto(skip);
-                    method.label(next);
-                    method.load(symbol).athrow();
-                } else {
-                    method.label(next);
-                }
-            }
-
             leaveBlock(catchBlock);
             lc.pop(catchBlock);
+
+            if(isConditionalCatch) {
+                if(!catchBody.hasTerminalFlags()) {
+                    // If it was executed, skip. Note the dup() above that left us this value on stack. On the other
+                    // hand, if the catch body terminates the flow, we can reach here only if it was not executed, so
+                    // IFEQ is implied.
+                    method.ifne(skip);
+                }
+                if(i + 1 == catchBlockCount) {
+                    // No next catch block - rethrow if condition failed
+                    method.load(symbol).athrow();
+                }
+            } else {
+                assert i + 1 == catchBlockCount;
+            }
         }
 
         method.label(skip);
-        method._try(entry, exit, recovery, Throwable.class);
 
         // Finally body is always inlined elsewhere so it doesn't need to be emitted
 
@@ -2153,7 +2635,7 @@
             return false;
         }
 
-        lineNumber(varNode);
+        enterStatement(varNode);
 
         final IdentNode identNode = varNode.getName();
         final Symbol identSymbol = identNode.getSymbol();
@@ -2199,7 +2681,7 @@
         body.accept(this);
         if (!whileNode.isTerminal()) {
             method.label(continueLabel);
-            lineNumber(whileNode);
+            enterStatement(whileNode);
             new BranchOptimizer(this, method).execute(test, loopLabel, true);
             method.label(breakLabel);
         }
@@ -2207,14 +2689,6 @@
         return false;
     }
 
-    private void closeWith() {
-        if (method.hasScope()) {
-            method.loadCompilerConstant(SCOPE);
-            method.invoke(ScriptRuntime.CLOSE_WITH);
-            method.storeCompilerConstant(SCOPE);
-        }
-    }
-
     @Override
     public boolean enterWithNode(final WithNode withNode) {
         final Expression expression = withNode.getExpression();
@@ -2226,28 +2700,26 @@
         // for its side effect and visit the body, and not bother opening and closing a WithObject.
         final boolean hasScope = method.hasScope();
 
+        if (hasScope) {
+            method.loadCompilerConstant(SCOPE);
+        }
+
+        load(expression, Type.OBJECT);
+
         final Label tryLabel;
         if (hasScope) {
-            tryLabel = new Label("with_try");
-            method.label(tryLabel);
-            method.loadCompilerConstant(SCOPE);
-        } else {
-            tryLabel = null;
-        }
-
-        load(expression, Type.OBJECT);
-
-        if (hasScope) {
             // Construct a WithObject if we have a scope
             method.invoke(ScriptRuntime.OPEN_WITH);
             method.storeCompilerConstant(SCOPE);
+            tryLabel = new Label("with_try");
+            method.label(tryLabel);
         } else {
             // We just loaded the expression for its side effect and to check
             // for null or undefined value.
             globalCheckObjectCoercible();
+            tryLabel = null;
         }
 
-
         // Always process body
         body.accept(this);
 
@@ -2258,26 +2730,26 @@
             final Label exitLabel  = new Label("with_exit");
 
             if (!body.isTerminal()) {
-                closeWith();
+                popScope();
                 method._goto(exitLabel);
             }
 
+            method._try(tryLabel, endLabel, catchLabel);
             method.label(endLabel);
 
             method._catch(catchLabel);
-            closeWith();
+            popScope();
             method.athrow();
 
             method.label(exitLabel);
 
-            method._try(tryLabel, endLabel, catchLabel);
         }
         return false;
     }
 
     @Override
     public boolean enterADD(final UnaryNode unaryNode) {
-        load(unaryNode.rhs(), unaryNode.getType());
+        load(unaryNode.getExpression(), unaryNode.getType());
         assert unaryNode.getType().isNumeric();
         method.store(unaryNode.getSymbol());
         return false;
@@ -2285,13 +2757,13 @@
 
     @Override
     public boolean enterBIT_NOT(final UnaryNode unaryNode) {
-        load(unaryNode.rhs(), Type.INT).load(-1).xor().store(unaryNode.getSymbol());
+        load(unaryNode.getExpression(), Type.INT).load(-1).xor().store(unaryNode.getSymbol());
         return false;
     }
 
     @Override
     public boolean enterDECINC(final UnaryNode unaryNode) {
-        final Expression rhs         = unaryNode.rhs();
+        final Expression rhs         = unaryNode.getExpression();
         final Type       type        = unaryNode.getType();
         final TokenType  tokenType   = unaryNode.tokenType();
         final boolean    isPostfix   = tokenType == TokenType.DECPOSTFIX || tokenType == TokenType.INCPOSTFIX;
@@ -2301,18 +2773,26 @@
 
         new SelfModifyingStore<UnaryNode>(unaryNode, rhs) {
 
+            private void loadRhs() {
+                load(rhs, type, true);
+            }
+
             @Override
             protected void evaluate() {
-                load(rhs, type, true);
-                if (!isPostfix) {
-                    if (type.isInteger()) {
-                        method.load(isIncrement ? 1 : -1);
-                    } else if (type.isLong()) {
-                        method.load(isIncrement ? 1L : -1L);
-                    } else {
-                        method.load(isIncrement ? 1.0 : -1.0);
-                    }
-                    method.add();
+                if(isPostfix) {
+                    loadRhs();
+                } else {
+                    new OptimisticOperation() {
+                        @Override
+                        void loadStack() {
+                            loadRhs();
+                            loadMinusOne();
+                        }
+                        @Override
+                        void consumeStack() {
+                            doDecInc();
+                        }
+                    }.emit(unaryNode, getOptimisticIgnoreCountForSelfModifyingExpression(rhs));
                 }
             }
 
@@ -2320,24 +2800,44 @@
             protected void storeNonDiscard() {
                 super.storeNonDiscard();
                 if (isPostfix) {
-                    if (type.isInteger()) {
-                        method.load(isIncrement ? 1 : -1);
-                    } else if (type.isLong()) {
-                        method.load(isIncrement ? 1L : 1L);
-                    } else {
-                        method.load(isIncrement ? 1.0 : -1.0);
-                    }
-                    method.add();
+                    new OptimisticOperation() {
+                        @Override
+                        void loadStack() {
+                            loadMinusOne();
+                        }
+                        @Override
+                        void consumeStack() {
+                            doDecInc();
+                        }
+                    }.emit(unaryNode, 1); // 1 for non-incremented result on the top of the stack pushed in evaluate()
                 }
             }
+
+            private void loadMinusOne() {
+                if (type.isInteger()) {
+                    method.load(isIncrement ? 1 : -1);
+                } else if (type.isLong()) {
+                    method.load(isIncrement ? 1L : -1L);
+                } else {
+                    method.load(isIncrement ? 1.0 : -1.0);
+                }
+            }
+
+            private void doDecInc() {
+                method.add(unaryNode.getProgramPoint());
+            }
         }.store();
 
         return false;
     }
 
+    private static int getOptimisticIgnoreCountForSelfModifyingExpression(final Expression target) {
+        return target instanceof AccessNode ? 1 : target instanceof IndexNode ? 2 : 0;
+    }
+
     @Override
     public boolean enterDISCARD(final UnaryNode unaryNode) {
-        final Expression rhs = unaryNode.rhs();
+        final Expression rhs = unaryNode.getExpression();
 
         lc.pushDiscard(rhs);
         load(rhs);
@@ -2353,7 +2853,7 @@
 
     @Override
     public boolean enterNEW(final UnaryNode unaryNode) {
-        final CallNode callNode = (CallNode)unaryNode.rhs();
+        final CallNode callNode = (CallNode)unaryNode.getExpression();
         final List<Expression> args   = callNode.getArgs();
 
         // Load function reference.
@@ -2367,7 +2867,7 @@
 
     @Override
     public boolean enterNOT(final UnaryNode unaryNode) {
-        final Expression rhs = unaryNode.rhs();
+        final Expression rhs = unaryNode.getExpression();
 
         load(rhs, Type.BOOLEAN);
 
@@ -2388,22 +2888,41 @@
     @Override
     public boolean enterSUB(final UnaryNode unaryNode) {
         assert unaryNode.getType().isNumeric();
-        load(unaryNode.rhs(), unaryNode.getType()).neg().store(unaryNode.getSymbol());
+        new OptimisticOperation() {
+            @Override
+            void loadStack() {
+                load(unaryNode.getExpression(), unaryNode.getType());
+            }
+            @Override
+            void consumeStack() {
+                method.neg(unaryNode.getProgramPoint());
+            }
+        }.emit(unaryNode);
+        method.store(unaryNode.getSymbol());
+
         return false;
     }
 
     @Override
     public boolean enterVOID(final UnaryNode unaryNode) {
-        load(unaryNode.rhs()).pop();
+        load(unaryNode.getExpression()).pop();
         method.loadUndefined(Type.OBJECT);
 
         return false;
     }
 
-    private void enterNumericAdd(final Expression lhs, final Expression rhs, final Type type, final Symbol symbol) {
-        loadBinaryOperands(lhs, rhs, type);
-        method.add(); //if the symbol is optimistic, it always needs to be written, not on the stack?
-        method.store(symbol);
+    private void enterNumericAdd(final BinaryNode binaryNode, final Expression lhs, final Expression rhs, final Type type) {
+        new OptimisticOperation() {
+            @Override
+            void loadStack() {
+                loadBinaryOperands(lhs, rhs, type);
+            }
+            @Override
+            void consumeStack() {
+                method.add(binaryNode.getProgramPoint()); //if the symbol is optimistic, it always needs to be written, not on the stack?
+           }
+        }.emit(binaryNode);
+        method.store(binaryNode.getSymbol());
     }
 
     @Override
@@ -2413,10 +2932,10 @@
 
         final Type type = binaryNode.getType();
         if (type.isNumeric()) {
-            enterNumericAdd(lhs, rhs, type, binaryNode.getSymbol());
+            enterNumericAdd(binaryNode, lhs, rhs, type);
         } else {
             loadBinaryOperands(binaryNode);
-            method.add();
+            method.add(INVALID_PROGRAM_POINT);
             method.store(binaryNode.getSymbol());
         }
 
@@ -2508,8 +3027,17 @@
 
         @Override
         protected void evaluate() {
-            loadBinaryOperands(assignNode.lhs(), assignNode.rhs(), opType, true);
-            op();
+            final Expression lhs = assignNode.lhs();
+            new OptimisticOperation() {
+                @Override
+                void loadStack() {
+                    loadBinaryOperands(lhs, assignNode.rhs(), opType, true);
+                }
+                @Override
+                void consumeStack() {
+                    op();
+                }
+            }.emit(assignNode, getOptimisticIgnoreCountForSelfModifyingExpression(lhs));
             method.convert(assignNode.getType());
         }
     }
@@ -2537,7 +3065,7 @@
                             Type.OBJECT,
                             Request.ADD);
                 } else {
-                    method.add();
+                    method.add(binaryNode.getProgramPoint());
                 }
             }
 
@@ -2591,7 +3119,7 @@
         new AssignOp(binaryNode) {
             @Override
             protected void op() {
-                method.div();
+                method.div(binaryNode.getProgramPoint());
             }
         }.store();
 
@@ -2615,7 +3143,7 @@
         new AssignOp(binaryNode) {
             @Override
             protected void op() {
-                method.mul();
+                method.mul(binaryNode.getProgramPoint());
             }
         }.store();
 
@@ -2664,7 +3192,7 @@
         new AssignOp(binaryNode) {
             @Override
             protected void op() {
-                method.sub();
+                method.sub(binaryNode.getProgramPoint());
             }
         }.store();
 
@@ -2679,8 +3207,16 @@
         protected abstract void op();
 
         protected void evaluate(final BinaryNode node) {
-            loadBinaryOperands(node);
-            op();
+            new OptimisticOperation() {
+                @Override
+                void loadStack() {
+                    loadBinaryOperands(node);
+                }
+                @Override
+                void consumeStack() {
+                    op();
+                }
+            }.emit(node);
             method.store(node.getSymbol());
         }
     }
@@ -2725,6 +3261,7 @@
         final Expression lhs = binaryNode.lhs();
         final Expression rhs = binaryNode.rhs();
 
+        assert lhs.isTokenType(TokenType.DISCARD);
         load(lhs);
         load(rhs);
         method.store(binaryNode.getSymbol());
@@ -2747,7 +3284,7 @@
         new BinaryArith() {
             @Override
             protected void op() {
-                method.div();
+                method.div(binaryNode.getProgramPoint());
             }
         }.evaluate(binaryNode);
 
@@ -2830,7 +3367,7 @@
         new BinaryArith() {
             @Override
             protected void op() {
-                method.mul();
+                method.mul(binaryNode.getProgramPoint());
             }
         }.evaluate(binaryNode);
 
@@ -2900,7 +3437,7 @@
         new BinaryArith() {
             @Override
             protected void op() {
-                method.sub();
+                method.sub(binaryNode.getProgramPoint());
             }
         }.evaluate(binaryNode);
 
@@ -2942,7 +3479,7 @@
     /**
      * Generate all shared scope calls generated during codegen.
      */
-    protected void generateScopeCalls() {
+    void generateScopeCalls() {
         for (final SharedScopeCall scopeAccess : lc.getScopeCalls()) {
             scopeAccess.generateScopeCall();
         }
@@ -3055,6 +3592,7 @@
                     if (targetSymbol.isScope()) {
                         method.load(scopeSymbol);
                         depth++;
+                        assert depth == 1;
                     }
                     return false;
                 }
@@ -3066,6 +3604,7 @@
 
                     load(base, Type.OBJECT);
                     depth += Type.OBJECT.getSlots();
+                    assert depth == 1;
 
                     if (isSelfModifying()) {
                         method.dup();
@@ -3167,10 +3706,11 @@
                     final Symbol symbol = node.getSymbol();
                     assert symbol != null;
                     if (symbol.isScope()) {
+                        final int flags = CALLSITE_SCOPE | getCallSiteFlags();
                         if (isFastScope(symbol)) {
-                            storeFastScopeVar(symbol, CALLSITE_SCOPE | getCallSiteFlags());
+                            storeFastScopeVar(symbol, flags);
                         } else {
-                            method.dynamicSet(node.getName(), CALLSITE_SCOPE | getCallSiteFlags());
+                            method.dynamicSet(node.getName(), flags);
                         }
                     } else {
                         method.convert(node.getType());
@@ -3210,35 +3750,64 @@
         }
     }
 
-    private void newFunctionObject(final FunctionNode functionNode, final FunctionNode originalFunctionNode) {
+    private void newFunctionObject(final FunctionNode functionNode, final boolean addInitializer) {
         assert lc.peek() == functionNode;
-        // We don't emit a ScriptFunction on stack for:
-        // 1. the outermost compiled function (as there's no code being generated in its outer context that'd need it
-        //    as a callee), and
-        // 2. for functions that are immediately called upon definition and they don't need a callee, e.g. (function(){})().
-        //    Such immediately-called functions are invoked using INVOKESTATIC (see enterFunctionNode() of the embedded
-        //    visitor of enterCallNode() for details), and if they don't need a callee, they don't have it on their
-        //    static method's parameter list.
-        if (lc.getOutermostFunction() == functionNode ||
-                (!functionNode.needsCallee()) && lc.isFunctionDefinedInCurrentCall(originalFunctionNode)) {
+
+        final int fnId = functionNode.getId();
+        final CompilationEnvironment env = compiler.getCompilationEnvironment();
+        RecompilableScriptFunctionData data = env.getScriptFunctionData(fnId);
+        // data != null => compileOutermostOnly()
+        assert data == null || compileOutermostOnly() : functionNode.getName() + " isRecompile=" + env.isOnDemandCompilation() + " data=" + data;
+        if(data == null) {
+            final Map<Integer, RecompilableScriptFunctionData> nestedFunctions = fnIdToNestedFunctions.get(fnId);
+            assert nestedFunctions != null;
+            // Generate the object class and property map in case this function is ever used as constructor
+            final int         fieldCount         = getPaddedFieldCount(functionNode.countThisProperties());
+            final String      allocatorClassName = Compiler.binaryName(getClassName(fieldCount));
+            final PropertyMap allocatorMap       = PropertyMap.newMap(null, 0, fieldCount, 0);
+
+            data = new RecompilableScriptFunctionData(functionNode, compiler.getCodeInstaller(), allocatorClassName, allocatorMap, nestedFunctions, compiler.getSourceURL());
+
+            final FunctionNode parentFn = lc.getParentFunction(functionNode);
+            if(parentFn == null) {
+                if(functionNode.isProgram()) {
+                    // Emit the "public static ScriptFunction createScriptFunction(ScriptObject scope)" method
+                    final CompileUnit fnUnit = functionNode.getCompileUnit();
+                    final MethodEmitter createFunction = fnUnit.getClassEmitter().method(
+                            EnumSet.of(Flag.PUBLIC, Flag.STATIC), CREATE_PROGRAM_FUNCTION.symbolName(),
+                            ScriptFunction.class, ScriptObject.class);
+                    createFunction.begin();
+                    createFunction._new(SCRIPTFUNCTION_IMPL_NAME, SCRIPTFUNCTION_IMPL_TYPE).dup();
+                    loadConstant(data, fnUnit, createFunction);
+                    createFunction.load(SCOPE_TYPE, 0);
+                    createFunction.invoke(constructorNoLookup(SCRIPTFUNCTION_IMPL_NAME, RecompilableScriptFunctionData.class, ScriptObject.class));
+                    createFunction._return();
+                    createFunction.end();
+                }
+            } else {
+                fnIdToNestedFunctions.get(parentFn.getId()).put(fnId, data);
+            }
+        }
+
+        if(addInitializer && !env.isOnDemandCompilation()) {
+            functionNode.getCompileUnit().addFunctionInitializer(data, functionNode);
+        }
+
+        // We don't emit a ScriptFunction on stack for the outermost compiled function (as there's no code being
+        // generated in its outer context that'd need it as a callee).
+        if (lc.getOutermostFunction() == functionNode) {
             return;
         }
 
-        // Generate the object class and property map in case this function is ever used as constructor
-        final String      className          = SCRIPTFUNCTION_IMPL_OBJECT;
-        final int         fieldCount         = ObjectClassGenerator.getPaddedFieldCount(functionNode.countThisProperties());
-        final String      allocatorClassName = Compiler.binaryName(ObjectClassGenerator.getClassName(fieldCount));
-        final PropertyMap allocatorMap       = PropertyMap.newMap(null, 0, fieldCount, 0);
-
-        method._new(className).dup();
-        loadConstant(new RecompilableScriptFunctionData(functionNode, compiler.getCodeInstaller(), allocatorClassName, allocatorMap));
-
-        if (functionNode.isLazy() || functionNode.needsParentScope()) {
+        method._new(SCRIPTFUNCTION_IMPL_NAME, SCRIPTFUNCTION_IMPL_TYPE).dup();
+        loadConstant(data);
+
+        if (functionNode.needsParentScope()) {
             method.loadCompilerConstant(SCOPE);
         } else {
             method.loadNull();
         }
-        method.invoke(constructorNoLookup(className, RecompilableScriptFunctionData.class, ScriptObject.class));
+        method.invoke(constructorNoLookup(SCRIPTFUNCTION_IMPL_NAME, RecompilableScriptFunctionData.class, ScriptObject.class));
     }
 
     // calls on Global class.
@@ -3271,6 +3840,10 @@
         return method.invokestatic(GLOBAL_OBJECT, "isEval", methodDescriptor(boolean.class, Object.class));
     }
 
+    private MethodEmitter globalReplaceLocationPropertyPlaceholder() {
+        return method.invokestatic(GLOBAL_OBJECT, "replaceLocationPropertyPlaceholder", methodDescriptor(Object.class, Object.class, Object.class));
+    }
+
     private MethodEmitter globalCheckObjectCoercible() {
         return method.invokestatic(GLOBAL_OBJECT, "checkObjectCoercible", methodDescriptor(void.class, Object.class));
     }
@@ -3279,4 +3852,593 @@
         return method.invokestatic(GLOBAL_OBJECT, "directEval",
                 methodDescriptor(Object.class, Object.class, Object.class, Object.class, Object.class, Object.class));
     }
+
+    private abstract class OptimisticOperation {
+        MethodEmitter emit(final Optimistic optimistic) {
+            return emit(optimistic, 0);
+        }
+
+        MethodEmitter emit(final Optimistic optimistic, final Type desiredType) {
+            return emit(optimistic, desiredType, 0);
+        }
+
+        MethodEmitter emit(final Optimistic optimistic, final Type desiredType, final int ignoredArgCount) {
+            return emit(optimistic.isOptimistic() && !desiredType.isObject(), optimistic.getProgramPoint(), ignoredArgCount);
+        }
+
+        MethodEmitter emit(final Optimistic optimistic, final int ignoredArgCount) {
+            return emit(optimistic.isOptimistic(), optimistic.getProgramPoint(), ignoredArgCount);
+        }
+
+        MethodEmitter emit(final boolean isOptimistic, final int programPoint, final int ignoredArgCount) {
+            final CompilationEnvironment env = compiler.getCompilationEnvironment();
+            final boolean reallyOptimistic = isOptimistic && useOptimisticTypes();
+            final boolean optimisticOrContinuation = reallyOptimistic || env.isContinuationEntryPoint(programPoint);
+            final boolean currentContinuationEntryPoint = env.isCurrentContinuationEntryPoint(programPoint);
+            final int stackSizeOnEntry = method.getStackSize() - ignoredArgCount;
+
+            // First store the values on the stack opportunistically into local variables. Doing it before loadStack()
+            // allows us to not have to pop/load any arguments that are pushed onto it by loadStack() in the second
+            // storeStack().
+            storeStack(ignoredArgCount, optimisticOrContinuation);
+
+            // Now, load the stack
+            loadStack();
+
+            // Now store the values on the stack ultimately into local variables . In vast majority of cases, this is
+            // (aside from creating the local types map) a no-op, as the first opportunistic stack store will already
+            // store all variables. However, there can be operations in the loadStack() that invalidate some of the
+            // stack stores, e.g. in "x[i] = x[++i]", "++i" will invalidate the already stored value for "i". In such
+            // unfortunate cases this second storeStack() will restore the invariant that everything on the stack is
+            // stored into a local variable, although at the cost of doing a store/load on the loaded arguments as well.
+            final int liveLocalsCount = storeStack(method.getStackSize() - stackSizeOnEntry, optimisticOrContinuation);
+            assert optimisticOrContinuation == (liveLocalsCount != -1);
+            assert !optimisticOrContinuation || everyTypeIsKnown(method.getLocalVariableTypes(), liveLocalsCount);
+
+            final Label beginTry;
+            final Label catchLabel;
+            final Label afterConsumeStack = reallyOptimistic || currentContinuationEntryPoint ? new Label("") : null;
+            if(reallyOptimistic) {
+                beginTry = new Label("");
+                catchLabel = new Label("");
+                method.label(beginTry);
+            } else {
+                beginTry = catchLabel = null;
+            }
+
+            consumeStack();
+
+            if(reallyOptimistic) {
+                method._try(beginTry, afterConsumeStack, catchLabel, UnwarrantedOptimismException.class);
+            }
+
+            if(reallyOptimistic || currentContinuationEntryPoint) {
+                method.label(afterConsumeStack);
+
+                final int[] localLoads = method.getLocalLoadsOnStack(0, stackSizeOnEntry);
+                assert everyStackValueIsLocalLoad(localLoads) : Arrays.toString(localLoads) + ", " + stackSizeOnEntry + ", " + ignoredArgCount;
+                final List<Type> localTypesList = method.getLocalVariableTypes();
+                final int usedLocals = getUsedSlotsWithLiveTemporaries(localTypesList, localLoads);
+                final Type[] localTypes = localTypesList.subList(0, usedLocals).toArray(new Type[usedLocals]);
+                assert everyLocalLoadIsValid(localLoads, usedLocals) : Arrays.toString(localLoads) + " ~ " + Arrays.toString(localTypes);
+
+                if(reallyOptimistic) {
+                    addUnwarrantedOptimismHandlerLabel(localTypes, catchLabel);
+                }
+                if(currentContinuationEntryPoint) {
+                    final ContinuationInfo ci = getContinuationInfo();
+                    assert ci.targetLabel == null; // No duplicate program points
+                    ci.targetLabel = afterConsumeStack;
+                    ci.localVariableTypes = localTypes;
+
+                    ci.stackStoreSpec = localLoads;
+
+                    ci.stackTypes = Arrays.copyOf(method.getTypesFromStack(method.getStackSize()), stackSizeOnEntry);
+                    assert ci.stackStoreSpec.length == ci.stackTypes.length;
+                    ci.returnValueType = method.peekType();
+                }
+            }
+            return method;
+        }
+
+        /**
+         * Stores the current contents of the stack into local variables so they are not lost before invoking something that
+         * can result in an {@code UnwarantedOptimizationException}.
+         * @param ignoreArgCount the number of topmost arguments on stack to ignore when deciding on the shape of the catch
+         * block. Those are used in the situations when we could not place the call to {@code storeStack} early enough
+         * (before emitting code for pushing the arguments that the optimistic call will pop). This is admittedly a
+         * deficiency in the design of the code generator when it deals with self-assignments and we should probably look
+         * into fixing it.
+         * @return types of the significant local variables after the stack was stored (types for local variables used
+         * for temporary storage of ignored arguments are not returned).
+         * @param optimisticOrContinuation if false, this method should not execute
+         * a label for a catch block for the {@code UnwarantedOptimizationException}, suitable for capturing the
+         * currently live local variables, tailored to their types.
+         */
+        private int storeStack(final int ignoreArgCount, final boolean optimisticOrContinuation) {
+            if(!optimisticOrContinuation) {
+                return -1; // NOTE: correct value to return is lc.getUsedSlotCount(), but it wouldn't be used anyway
+            }
+
+            final int stackSize = method.getStackSize();
+            final Type[] stackTypes = method.getTypesFromStack(stackSize);
+            final int[] localLoadsOnStack = method.getLocalLoadsOnStack(0, stackSize);
+            final int usedSlots = getUsedSlotsWithLiveTemporaries(method.getLocalVariableTypes(), localLoadsOnStack);
+
+            final int firstIgnored = stackSize - ignoreArgCount;
+            // Find the first value on the stack (from the bottom) that is not a load from a local variable.
+            int firstNonLoad = 0;
+            while(firstNonLoad < firstIgnored && localLoadsOnStack[firstNonLoad] != Label.Stack.NON_LOAD) {
+                firstNonLoad++;
+            }
+
+            // Only do the store/load if first non-load is not an ignored argument. Otherwise, do nothing and return
+            // the number of used slots as the number of live local variables.
+            if(firstNonLoad >= firstIgnored) {
+                return usedSlots;
+            }
+
+            // Find the number of new temporary local variables that we need; it's the number of values on the stack that
+            // are not direct loads of existing local variables.
+            int tempSlotsNeeded = 0;
+            for(int i = firstNonLoad; i < stackSize; ++i) {
+                if(localLoadsOnStack[i] == Label.Stack.NON_LOAD) {
+                    tempSlotsNeeded += stackTypes[i].getSlots();
+                }
+            }
+
+            // Ensure all values on the stack that weren't directly loaded from a local variable are stored in a local
+            // variable. We're starting from highest local variable index, so that in case ignoreArgCount > 0 the ignored
+            // ones end up at the end of the local variable table.
+            int lastTempSlot = usedSlots + tempSlotsNeeded;
+            int ignoreSlotCount = 0;
+            for(int i = stackSize; i -- > firstNonLoad;) {
+                final int loadSlot = localLoadsOnStack[i];
+                if(loadSlot == Label.Stack.NON_LOAD) {
+                    final Type type = stackTypes[i];
+                    final int slots = type.getSlots();
+                    lastTempSlot -= slots;
+                    if(i >= firstIgnored) {
+                        ignoreSlotCount += slots;
+                    }
+                    method.store(type, lastTempSlot);
+                } else {
+                    method.pop();
+                }
+            }
+            assert lastTempSlot == usedSlots; // used all temporary locals
+
+            final List<Type> localTypesList = method.getLocalVariableTypes();
+
+            // Load values back on stack.
+            for(int i = firstNonLoad; i < stackSize; ++i) {
+                final int loadSlot = localLoadsOnStack[i];
+                final Type stackType = stackTypes[i];
+                final boolean isLoad = loadSlot != Label.Stack.NON_LOAD;
+                final int lvarSlot = isLoad ? loadSlot : lastTempSlot;
+                final Type lvarType = localTypesList.get(lvarSlot);
+                method.load(lvarType, lvarSlot);
+                if(isLoad) {
+                    // Conversion operators (I2L etc.) preserve "load"-ness of the value despite the fact that, in the
+                    // strict sense they are creating a derived value from the loaded value. This special behavior of
+                    // on-stack conversion operators is necessary to accommodate for differences in local variable types
+                    // after deoptimization; having a conversion operator throw away "load"-ness would create different
+                    // local variable table shapes between optimism-failed code and its deoptimized rest-of method).
+                    // After we load the value back, we need to redo the conversion to the stack type if stack type is
+                    // different.
+                    // NOTE: this would only strictly be necessary for widening conversions (I2L, L2D, I2D), and not for
+                    // narrowing ones (L2I, D2L, D2I) as only widening conversions are the ones that can get eliminated
+                    // in a deoptimized method, as their original input argument got widened. Maybe experiment with
+                    // throwing away "load"-ness for narrowing conversions in MethodEmitter.convert()?
+                    method.convert(stackType);
+                } else {
+                    // temporary stores never needs a convert, as their type is always the same as the stack type.
+                    assert lvarType == stackType;
+                    lastTempSlot += lvarType.getSlots();
+                }
+            }
+            // used all temporaries
+            assert lastTempSlot == usedSlots + tempSlotsNeeded;
+
+            return lastTempSlot - ignoreSlotCount;
+        }
+
+        private void addUnwarrantedOptimismHandlerLabel(final Type[] localTypes, final Label label) {
+            final String lvarTypesDescriptor = getLvarTypesDescriptor(localTypes);
+            final Map<String, Collection<Label>> unwarrantedOptimismHandlers = lc.getUnwarrantedOptimismHandlers();
+            Collection<Label> labels = unwarrantedOptimismHandlers.get(lvarTypesDescriptor);
+            if(labels == null) {
+                labels = new LinkedList<>();
+                unwarrantedOptimismHandlers.put(lvarTypesDescriptor, labels);
+            }
+            labels.add(label);
+        }
+
+        /**
+         * Returns the number of used local variable slots, including all live stack-store temporaries.
+         * @param localVariableTypes the current local variable types
+         * @param localLoadsOnStack the current local variable loads on the stack
+         * @return the number of used local variable slots, including all live stack-store temporaries.
+         */
+        private int getUsedSlotsWithLiveTemporaries(List<Type> localVariableTypes, int[] localLoadsOnStack) {
+            // There are at least as many as are declared by the current blocks.
+            int usedSlots = lc.getUsedSlotCount();
+            // Look at every load on the stack, and bump the number of used slots up by the temporaries seen there.
+            for(int i = 0; i < localLoadsOnStack.length; ++i) {
+                final int slot = localLoadsOnStack[i];
+                if(slot != Label.Stack.NON_LOAD) {
+                    int afterSlot = slot + localVariableTypes.get(slot).getSlots();
+                    if(afterSlot > usedSlots) {
+                        usedSlots = afterSlot;
+                    }
+                }
+            }
+            return usedSlots;
+        }
+
+        abstract void loadStack();
+
+        // Make sure that whatever indy call site you emit from this method uses {@code getCallSiteFlagsOptimistic(node)}
+        // or otherwise ensure optimistic flag is correctly set in the call site, otherwise it doesn't make much sense
+        // to use OptimisticExpression for emitting it.
+        abstract void consumeStack();
+    }
+
+    private static boolean everyLocalLoadIsValid(final int[] loads, int localCount) {
+        for(int i = 0; i < loads.length; ++i) {
+            if(loads[i] < 0 || loads[i] >= localCount) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private static boolean everyTypeIsKnown(final List<Type> types, final int liveLocalsCount) {
+        assert types instanceof RandomAccess;
+        for(int i = 0; i < liveLocalsCount;) {
+            final Type t = types.get(i);
+            if(t == Type.UNKNOWN) {
+                return false;
+            }
+            i += t.getSlots();
+        }
+        return true;
+    }
+
+    private static boolean everyStackValueIsLocalLoad(final int[] loads) {
+        for(int i = 0; i < loads.length; ++i) {
+            if(loads[i] == Label.Stack.NON_LOAD) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private static String getLvarTypesDescriptor(final Type[] localVarTypes) {
+        final StringBuilder desc = new StringBuilder(localVarTypes.length);
+        for(int i = 0; i < localVarTypes.length;) {
+            i += appendType(desc, localVarTypes[i]);
+        }
+        // Trailing unknown types are unnecessary. (These don't actually occur though as long as we conservatively
+        // force-initialize all potentially-top values.)
+        for(int l = desc.length(); l-- > 0;) {
+            if(desc.charAt(l) != 'U') {
+                desc.setLength(l + 1);
+                break;
+            }
+        }
+        return desc.toString();
+    }
+
+    private static int appendType(final StringBuilder b, final Type t) {
+        b.append(t.getBytecodeStackType());
+        return t.getSlots();
+    }
+
+    /**
+     * Generates all the required {@code UnwarrantedOptimismException} handlers for the current function. The employed
+     * strategy strives to maximize code reuse. Every handler constructs an array to hold the local variables, then
+     * fills in some trailing part of the local variables (those for which it has a unique suffix in the descriptor),
+     * then jumps to a handler for a prefix that's shared with other handlers. A handler that fills up locals up to
+     * position 0 will not jump to a prefix handler (as it has no prefix), but instead end with constructing and
+     * throwing a {@code RewriteException}. Since we lexicographically sort the entries, we only need to check every
+     * entry to its immediately preceding one for longest matching prefix.
+     * @return true if there is at least one exception handler
+     */
+    private boolean generateUnwarrantedOptimismExceptionHandlers() {
+        if(!useOptimisticTypes()) {
+            return false;
+        }
+
+        // Take the mapping of lvarSpecs -> labels, and turn them into a descending lexicographically sorted list of
+        // handler specifications.
+        final Map<String, Collection<Label>> unwarrantedOptimismHandlers = lc.popUnwarrantedOptimismHandlers();
+        if(unwarrantedOptimismHandlers.isEmpty()) {
+            return false;
+        }
+        final List<OptimismExceptionHandlerSpec> handlerSpecs = new ArrayList<>(unwarrantedOptimismHandlers.size() * 4/3);
+        for(final String spec: unwarrantedOptimismHandlers.keySet()) {
+            handlerSpecs.add(new OptimismExceptionHandlerSpec(spec, true));
+        }
+        Collections.sort(handlerSpecs, Collections.reverseOrder());
+
+        // Map of local variable specifications to labels for populating the array for that local variable spec.
+        final Map<String, Label> delegationLabels = new HashMap<>();
+
+        // Do everything in a single pass over the handlerSpecs list. Note that the list can actually grow as we're
+        // passing through it as we might add new prefix handlers into it, so can't hoist size() outside of the loop.
+        for(int handlerIndex = 0; handlerIndex < handlerSpecs.size(); ++handlerIndex) {
+            final OptimismExceptionHandlerSpec spec = handlerSpecs.get(handlerIndex);
+            final String lvarSpec = spec.lvarSpec;
+            if(spec.catchTarget) {
+                // Start a catch block and assign the labels for this lvarSpec with it.
+                method._catch(unwarrantedOptimismHandlers.get(lvarSpec));
+                // This spec is a catch target, so emit array creation code
+                method.load(spec.lvarSpec.length());
+                method.newarray(Type.OBJECT_ARRAY);
+            }
+            if(spec.delegationTarget) {
+                // If another handler can delegate to this handler as its prefix, then put a jump target here for the
+                // shared code (after the array creation code, which is never shared).
+                method.label(delegationLabels.get(lvarSpec)); // label must exist
+            }
+
+            final boolean lastHandler = handlerIndex == handlerSpecs.size() - 1;
+
+            int lvarIndex;
+            final int firstArrayIndex;
+            Label delegationLabel;
+            final String commonLvarSpec;
+            if(lastHandler) {
+                // Last handler block, doesn't delegate to anything.
+                lvarIndex = 0;
+                firstArrayIndex = 0;
+                delegationLabel = null;
+                commonLvarSpec = null;
+            } else {
+                // Not yet the last handler block, will definitely delegate to another handler; let's figure out which
+                // one. It can be an already declared handler further down the list, or it might need to declare a new
+                // prefix handler.
+
+                // Since we're lexicographically ordered, the common prefix handler is defined by the common prefix of
+                // this handler and the next handler on the list.
+                final int nextHandlerIndex = handlerIndex + 1;
+                final String nextLvarSpec = handlerSpecs.get(nextHandlerIndex).lvarSpec;
+                commonLvarSpec = commonPrefix(lvarSpec, nextLvarSpec);
+
+                // Let's find if we already have a declaration for such handler, or we need to insert it.
+                {
+                    boolean addNewHandler = true;
+                    int commonHandlerIndex = nextHandlerIndex;
+                    for(; commonHandlerIndex < handlerSpecs.size(); ++commonHandlerIndex) {
+                        final OptimismExceptionHandlerSpec forwardHandlerSpec = handlerSpecs.get(commonHandlerIndex);
+                        final String forwardLvarSpec = forwardHandlerSpec.lvarSpec;
+                        if(forwardLvarSpec.equals(commonLvarSpec)) {
+                            // We already have a handler for the common prefix.
+                            addNewHandler = false;
+                            // Make sure we mark it as a delegation target.
+                            forwardHandlerSpec.delegationTarget = true;
+                            break;
+                        } else if(!forwardLvarSpec.startsWith(commonLvarSpec)) {
+                            break;
+                        }
+                    }
+                    if(addNewHandler) {
+                        // We need to insert a common prefix handler. Note handlers created with catchTarget == false
+                        // will automatically have delegationTarget == true (because that's the only reason for their
+                        // existence).
+                        handlerSpecs.add(commonHandlerIndex, new OptimismExceptionHandlerSpec(commonLvarSpec, false));
+                    }
+                }
+
+                // Calculate the local variable index at the end of the common prefix
+                firstArrayIndex = commonLvarSpec.length();
+                lvarIndex = 0;
+                for(int j = 0; j < firstArrayIndex; ++j) {
+                    lvarIndex += CodeGeneratorLexicalContext.getTypeForSlotDescriptor(commonLvarSpec.charAt(j)).getSlots();
+                }
+
+                // Create a delegation label if not already present
+                delegationLabel = delegationLabels.get(commonLvarSpec);
+                if(delegationLabel == null) {
+                    // uo_pa == "unwarranted optimism, populate array"
+                    delegationLabel = new Label("uo_pa_" + commonLvarSpec);
+                    delegationLabels.put(commonLvarSpec, delegationLabel);
+                }
+            }
+
+            // Load local variables handled by this handler on stack
+            int args = 0;
+            for(int arrayIndex = firstArrayIndex; arrayIndex < lvarSpec.length(); ++arrayIndex) {
+                final Type lvarType = CodeGeneratorLexicalContext.getTypeForSlotDescriptor(lvarSpec.charAt(arrayIndex));
+                if (!lvarType.isUnknown()) {
+                    method.load(lvarType, lvarIndex);
+                    args++;
+                }
+                lvarIndex += lvarType.getSlots();
+            }
+            // Delegate actual storing into array to an array populator utility method. These are reused within a
+            // compilation unit.
+            //on the stack:
+            // object array to be populated
+            // start index
+            // a lot of types
+            method.dynamicArrayPopulatorCall(args + 1, firstArrayIndex);
+
+            if(delegationLabel != null) {
+                // We cascade to a prefix handler to fill out the rest of the local variables and throw the
+                // RewriteException.
+                assert !lastHandler;
+                assert commonLvarSpec != null;
+                final OptimismExceptionHandlerSpec nextSpec = handlerSpecs.get(handlerIndex + 1);
+                // If the delegate immediately follows, and it's not a catch target (so it doesn't have array setup
+                // code) don't bother emitting a jump, as we'd just jump to the next instruction.
+                if(!nextSpec.lvarSpec.equals(commonLvarSpec) || nextSpec.catchTarget) {
+                    method._goto(delegationLabel);
+                }
+            } else {
+                assert lastHandler;
+                // Nothing to delegate to, so this handler must create and throw the RewriteException.
+                // At this point we have the UnwarrantedOptimismException and the Object[] with local variables on
+                // stack. We need to create a RewriteException, push two references to it below the constructor
+                // arguments, invoke the constructor, and throw the exception.
+                method._new(RewriteException.class);
+                method.dup(2);
+                method.dup(2);
+                method.pop();
+                final CompilationEnvironment env = compiler.getCompilationEnvironment();
+                if(env.isCompileRestOf()) {
+                    loadConstant(env.getContinuationEntryPoints());
+                    method.invoke(INIT_REWRITE_EXCEPTION_REST_OF);
+                } else {
+                    method.invoke(INIT_REWRITE_EXCEPTION);
+                }
+                method.athrow();
+            }
+        }
+        return true;
+    }
+
+    private static String commonPrefix(String s1, String s2) {
+        final int l1 = s1.length();
+        final int l = Math.min(l1, s2.length());
+        for(int i = 0; i < l; ++i) {
+            if(s1.charAt(i) != s2.charAt(i)) {
+                return s1.substring(0, i);
+            }
+        }
+        return l == l1 ? s1 : s2;
+    }
+
+    private static class OptimismExceptionHandlerSpec implements Comparable<OptimismExceptionHandlerSpec> {
+        private final String lvarSpec;
+        private final boolean catchTarget;
+        private boolean delegationTarget;
+
+        OptimismExceptionHandlerSpec(final String lvarSpec, boolean catchTarget) {
+            this.lvarSpec = lvarSpec;
+            this.catchTarget = catchTarget;
+            if(!catchTarget) {
+                delegationTarget = true;
+            }
+        }
+
+        @Override
+        public int compareTo(OptimismExceptionHandlerSpec o) {
+            return lvarSpec.compareTo(o.lvarSpec);
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder b = new StringBuilder(64).append("[HandlerSpec ").append(lvarSpec);
+            if(catchTarget) {
+                b.append(", catchTarget");
+            }
+            if(delegationTarget) {
+                b.append(", delegationTarget");
+            }
+            return b.append("]").toString();
+        }
+    }
+
+    private static class ContinuationInfo {
+        final Label handlerLabel;
+        Label targetLabel; // Label for the target instruction.
+        // Types the local variable slots have to have when this node completes
+        Type[] localVariableTypes;
+        // Indices of local variables that need to be loaded on the stack when this node completes
+        int[] stackStoreSpec;
+        // Types of values loaded on the stack
+        Type[] stackTypes;
+        // If non-null, this node should perform the requisite type conversion
+        Type returnValueType;
+        // If we are in the middle of an object literal initialization, we need to update
+        // the map
+        PropertyMap objectLiteralMap;
+
+        ContinuationInfo() {
+            this.handlerLabel = new Label("continuation_handler");
+        }
+
+        @Override
+        public String toString() {
+            return "[localVariableTypes=" + Arrays.toString(localVariableTypes) + ", stackStoreSpec=" +
+                    Arrays.toString(stackStoreSpec) + ", returnValueType=" + returnValueType + "]";
+        }
+    }
+
+    private ContinuationInfo getContinuationInfo() {
+        return fnIdToContinuationInfo.get(lc.getCurrentFunction().getId());
+    }
+
+    private void generateContinuationHandler() {
+        if (!compiler.getCompilationEnvironment().isCompileRestOf()) {
+            return;
+        }
+
+        final ContinuationInfo ci = getContinuationInfo();
+        method.label(ci.handlerLabel);
+
+        // There should never be an exception thrown from the continuation handler, but in case there is (meaning,
+        // Nashorn has a bug), then line number 0 will be an indication of where it came from (line numbers are Uint16).
+        method.lineNumber(0);
+
+        final Type[] lvarTypes = ci.localVariableTypes;
+        final int lvarCount = lvarTypes.length;
+
+        final Type exceptionType = Type.typeFor(RewriteException.class);
+        method.load(exceptionType, 0);
+        method.dup();
+        // Get local variable array
+        method.invoke(RewriteException.GET_BYTECODE_SLOTS);
+        // Store local variables
+        for(int lvarIndex = 0, arrayIndex = 0; lvarIndex < lvarCount; ++arrayIndex) {
+            final Type lvarType = lvarTypes[lvarIndex];
+            final int nextLvarIndex = lvarIndex + lvarType.getSlots();
+            if(nextLvarIndex < lvarCount) {
+                // keep local variable array on the stack unless this is the last lvar
+                method.dup();
+            }
+            method.load(arrayIndex).arrayload();
+            method.convert(lvarType);
+            method.store(lvarType, lvarIndex);
+            lvarIndex = nextLvarIndex;
+        }
+
+        final int[] stackStoreSpec = ci.stackStoreSpec;
+        final Type[] stackTypes = ci.stackTypes;
+        final boolean isStackEmpty = stackStoreSpec.length == 0;
+        if(!isStackEmpty) {
+            // Store the RewriteException into an unused local variable slot.
+            method.store(exceptionType, lvarCount);
+            // Load arguments on the stack
+            for(int i = 0; i < stackStoreSpec.length; ++i) {
+                final int slot = stackStoreSpec[i];
+                method.load(lvarTypes[slot], slot);
+                method.convert(stackTypes[i]);
+            }
+
+            // stack: s0=object literal being initialized
+            // change map of s0 so that the property we are initilizing when we failed
+            // is now ci.returnValueType
+            if (ci.objectLiteralMap != null) {
+                method.dup(); //dup script object
+                assert ScriptObject.class.isAssignableFrom(method.peekType().getTypeClass()) : method.peekType().getTypeClass() + " is not a script object";
+                loadConstant(ci.objectLiteralMap);
+                method.invoke(ScriptObject.SET_MAP);
+            }
+
+            // Load RewriteException back; get rid of the stored reference.
+            method.load(Type.OBJECT, lvarCount);
+            method.loadNull();
+            method.store(Type.OBJECT, lvarCount);
+        }
+
+        // Load return value on the stack
+        method.invoke(RewriteException.GET_RETURN_VALUE);
+        method.convert(ci.returnValueType);
+
+        // Jump to continuation point
+        method._goto(ci.targetLabel);
+    }
 }
--- a/src/jdk/nashorn/internal/codegen/CodeGeneratorLexicalContext.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/src/jdk/nashorn/internal/codegen/CodeGeneratorLexicalContext.java	Wed Feb 26 13:17:57 2014 +0100
@@ -31,13 +31,14 @@
 import java.util.Deque;
 import java.util.HashMap;
 import java.util.Map;
-
+import jdk.nashorn.internal.IntDeque;
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.Block;
 import jdk.nashorn.internal.ir.FunctionNode;
 import jdk.nashorn.internal.ir.LexicalContext;
 import jdk.nashorn.internal.ir.LexicalContextNode;
 import jdk.nashorn.internal.ir.Node;
+import jdk.nashorn.internal.ir.SplitNode;
 import jdk.nashorn.internal.ir.Symbol;
 import jdk.nashorn.internal.ir.WithNode;
 
@@ -63,6 +64,10 @@
      *  i.e. should we keep it or throw it away */
     private final Deque<Node> discard = new ArrayDeque<>();
 
+    private final Deque<Map<String, Collection<Label>>> unwarrantedOptimismHandlers = new ArrayDeque<>();
+    private final Deque<StringBuilder> slotTypesDescriptors = new ArrayDeque<>();
+    private final IntDeque splitNodes = new IntDeque();
+
     /** A stack tracking the next free local variable slot in the blocks. There's one entry for every block
      *  currently on the lexical context stack. */
     private int[] nextFreeSlots = new int[16];
@@ -75,17 +80,33 @@
         if (isDynamicScopeBoundary(node)) {
             ++dynamicScopeCount;
         }
+        if(node instanceof FunctionNode) {
+            splitNodes.push(0);
+        } else if(node instanceof SplitNode) {
+            enterSplitNode();
+        }
         return super.push(node);
     }
 
+    void enterSplitNode() {
+        splitNodes.getAndIncrement();
+    }
+
+    void exitSplitNode() {
+        splitNodes.decrementAndGet();
+    }
+
     @Override
     public <T extends LexicalContextNode> T pop(final T node) {
         final T popped = super.pop(node);
         if (isDynamicScopeBoundary(popped)) {
             --dynamicScopeCount;
         }
-        if (node instanceof Block) {
-            --nextFreeSlotsSize;
+        if(node instanceof FunctionNode) {
+            assert splitNodes.peek() == 0;
+            splitNodes.pop();
+        } else if(node instanceof SplitNode) {
+            exitSplitNode();
         }
         return popped;
     }
@@ -108,6 +129,10 @@
         return dynamicScopeCount > 0;
     }
 
+    boolean inSplitNode() {
+        return !splitNodes.isEmpty() && splitNodes.peek() > 0;
+    }
+
     static boolean isFunctionDynamicScope(FunctionNode fn) {
         return fn.hasEval() && !fn.isStrict();
     }
@@ -123,6 +148,20 @@
         return methodEmitters.isEmpty() ? null : methodEmitters.peek();
     }
 
+    void pushUnwarrantedOptimismHandlers() {
+        unwarrantedOptimismHandlers.push(new HashMap<String, Collection<Label>>());
+        slotTypesDescriptors.push(new StringBuilder());
+    }
+
+    Map<String, Collection<Label>> getUnwarrantedOptimismHandlers() {
+        return unwarrantedOptimismHandlers.peek();
+    }
+
+    Map<String, Collection<Label>> popUnwarrantedOptimismHandlers() {
+        slotTypesDescriptors.pop();
+        return unwarrantedOptimismHandlers.pop();
+    }
+
     CompileUnit pushCompileUnit(final CompileUnit newUnit) {
         compileUnits.push(newUnit);
         return newUnit;
@@ -167,33 +206,18 @@
      * Get a shared static method representing a dynamic scope get access.
      *
      * @param unit current compile unit
-     * @param type the type of the variable
      * @param symbol the symbol
+     * @param valueType the type of the variable
      * @param flags the callsite flags
      * @return an object representing a shared scope call
      */
-    SharedScopeCall getScopeGet(final CompileUnit unit, final Type type, final Symbol symbol, final int flags) {
-        final SharedScopeCall scopeCall = new SharedScopeCall(symbol, type, type, null, flags);
-        if (scopeCalls.containsKey(scopeCall)) {
-            return scopeCalls.get(scopeCall);
-        }
-        scopeCall.setClassAndName(unit, getCurrentFunction().uniqueName(":scopeCall"));
-        scopeCalls.put(scopeCall, scopeCall);
-        return scopeCall;
+    SharedScopeCall getScopeGet(final CompileUnit unit, final Symbol symbol, final Type valueType, final int flags) {
+        return getScopeCall(unit, symbol, valueType, valueType, null, flags);
     }
 
 
     void nextFreeSlot(final Block block) {
-        final boolean isFunctionBody = isFunctionBody();
-
-        final int nextFreeSlot;
-        if (isFunctionBody) {
-            // On entry to function, start with slot 0
-            nextFreeSlot = 0;
-        } else {
-            // Otherwise, continue from previous block's first free slot
-            nextFreeSlot = nextFreeSlots[nextFreeSlotsSize - 1];
-        }
+        final int nextFreeSlot = isFunctionBody() ? 0 : getUsedSlotCount();
         if (nextFreeSlotsSize == nextFreeSlots.length) {
             final int[] newNextFreeSlots = new int[nextFreeSlotsSize * 2];
             System.arraycopy(nextFreeSlots, 0, newNextFreeSlots, 0, nextFreeSlotsSize);
@@ -202,7 +226,18 @@
         nextFreeSlots[nextFreeSlotsSize++] = assignSlots(block, nextFreeSlot);
     }
 
-    private static int assignSlots(final Block block, final int firstSlot) {
+    int getUsedSlotCount() {
+        return nextFreeSlots[nextFreeSlotsSize - 1];
+    }
+
+    void releaseBlockSlots(boolean optimistic) {
+        --nextFreeSlotsSize;
+        if(optimistic) {
+            slotTypesDescriptors.peek().setLength(nextFreeSlots[nextFreeSlotsSize]);
+        }
+    }
+
+    private int assignSlots(final Block block, final int firstSlot) {
         int nextSlot = firstSlot;
         for (final Symbol symbol : block.getSymbols()) {
             if (symbol.hasSlot()) {
@@ -210,9 +245,33 @@
                 nextSlot += symbol.slotCount();
             }
         }
+        methodEmitters.peek().ensureLocalVariableCount(nextSlot);
         return nextSlot;
     }
 
+    static Type getTypeForSlotDescriptor(final char typeDesc) {
+        switch(typeDesc) {
+            case 'I': {
+                return Type.INT;
+            }
+            case 'J': {
+                return Type.LONG;
+            }
+            case 'D': {
+                return Type.NUMBER;
+            }
+            case 'A': {
+                return Type.OBJECT;
+            }
+            case 'U': {
+                return Type.UNKNOWN;
+            }
+            default: {
+                throw new AssertionError();
+            }
+        }
+    }
+
     void pushDiscard(final Node node) {
         discard.push(node);
     }
@@ -228,6 +287,7 @@
     int quickSlot(final Symbol symbol) {
         final int quickSlot = nextFreeSlots[nextFreeSlotsSize - 1];
         nextFreeSlots[nextFreeSlotsSize - 1] = quickSlot + symbol.slotCount();
+        methodEmitters.peek().ensureLocalVariableCount(quickSlot);
         return quickSlot;
     }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/codegen/CompilationEnvironment.java	Wed Feb 26 13:17:57 2014 +0100
@@ -0,0 +1,363 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.codegen;
+
+import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.ir.Optimistic;
+import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
+
+/**
+ * Class for managing metadata during a compilation, e.g. which phases
+ * should be run, should we use optimistic types, is this a lazy compilation
+ * and various parameter types known to the runtime system
+ */
+public final class CompilationEnvironment {
+    private final CompilationPhases phases;
+    private final boolean optimistic;
+
+    private final ParamTypeMap paramTypes;
+
+    private final RecompilableScriptFunctionData compiledFunction;
+
+    private boolean strict;
+
+    /**
+     * If this is a recompilation, this is how we pass in the invalidations, e.g. programPoint=17, Type == int means
+     * that using whatever was at program point 17 as an int failed.
+     */
+    private final Map<Integer, Type> invalidatedProgramPoints;
+
+    /**
+     * Contains the program point that should be used as the continuation entry point, as well as all previous
+     * continuation entry points executed as part of a single logical invocation of the function. In practical terms, if
+     * we execute a rest-of method from the program point 17, but then we hit deoptimization again during it at program
+     * point 42, and execute a rest-of method from the program point 42, and then we hit deoptimization again at program
+     * point 57 and are compiling a rest-of method for it, the values in the array will be [57, 42, 17]. This is only
+     * set when compiling a rest-of method. If this method is a rest-of for a non-rest-of method, the array will have
+     * one element. If it is a rest-of for a rest-of, the array will have two elements, and so on.
+     */
+    private final int[] continuationEntryPoints;
+
+    /**
+     * Compilation phases that a compilation goes through
+     */
+    public static final class CompilationPhases implements Iterable<CompilationPhase> {
+
+        /**
+         * Standard (non-lazy) compilation, that basically will take an entire script
+         * and JIT it at once. This can lead to long startup time and fewer type
+         * specializations
+         */
+        final static CompilationPhase[] SEQUENCE_EAGER_ARRAY = new CompilationPhase[] {
+            CompilationPhase.CONSTANT_FOLDING_PHASE,
+            CompilationPhase.LOWERING_PHASE,
+            CompilationPhase.SPLITTING_PHASE,
+            CompilationPhase.ATTRIBUTION_PHASE,
+            CompilationPhase.RANGE_ANALYSIS_PHASE,
+            CompilationPhase.TYPE_FINALIZATION_PHASE,
+            CompilationPhase.BYTECODE_GENERATION_PHASE
+        };
+
+        private final static List<CompilationPhase> SEQUENCE_EAGER;
+        static {
+            LinkedList<CompilationPhase> eager = new LinkedList<>();
+            for (final CompilationPhase phase : SEQUENCE_EAGER_ARRAY) {
+                eager.add(phase);
+            }
+            SEQUENCE_EAGER = Collections.unmodifiableList(eager);
+        }
+
+        /** Singleton that describes a standard eager compilation */
+        public static CompilationPhases EAGER = new CompilationPhases(SEQUENCE_EAGER);
+
+        private final List<CompilationPhase> phases;
+
+        private CompilationPhases(final List<CompilationPhase> phases) {
+            this.phases = phases;
+        }
+
+        @SuppressWarnings("unused")
+        private CompilationPhases addFirst(final CompilationPhase phase) {
+            if (phases.contains(phase)) {
+                return this;
+            }
+            final LinkedList<CompilationPhase> list = new LinkedList<>(phases);
+            list.addFirst(phase);
+            return new CompilationPhases(Collections.unmodifiableList(list));
+        }
+
+        private CompilationPhases addAfter(final CompilationPhase phase, final CompilationPhase newPhase) {
+            final LinkedList<CompilationPhase> list = new LinkedList<>();
+            for (CompilationPhase p : phases) {
+                list.add(p);
+                if (p == phase) {
+                    list.add(newPhase);
+                }
+            }
+            return new CompilationPhases(Collections.unmodifiableList(list));
+        }
+
+        /**
+         * Turn a CompilationPhases into an optimistic one. NOP if already optimistic
+         * @param isOptimistic should this be optimistic
+         * @return new CompilationPhases that is optimistic or same if already optimistic
+         */
+        public CompilationPhases makeOptimistic(final boolean isOptimistic) {
+            return isOptimistic ? addAfter(CompilationPhase.LOWERING_PHASE, CompilationPhase.PROGRAM_POINT_PHASE) : this;
+        }
+
+        /**
+         * Turn a CompilationPhases into an optimistic one. NOP if already optimistic
+         * @return new CompilationPhases that is optimistic or same if already optimistic
+         */
+        public CompilationPhases makeOptimistic() {
+            return makeOptimistic(true);
+        }
+
+        private boolean contains(final CompilationPhase phase) {
+            return phases.contains(phase);
+        }
+
+        @Override
+        public Iterator<CompilationPhase> iterator() {
+            return phases.iterator();
+        }
+
+    }
+
+    /**
+     * Constructor
+     * @param phases compilation phases
+     * @param strict strict mode
+     */
+    public CompilationEnvironment(
+        final CompilationPhases phases,
+        final boolean strict) {
+        this(phases, null, null, null, null, strict);
+    }
+
+    /**
+     * Constructor for compilation environment of the rest-of method
+     * @param phases compilation phases
+     * @param strict strict mode
+     * @param recompiledFunction recompiled function
+     * @param continuationEntryPoint program points used as the continuation entry points in the current rest-of sequence
+     * @param invalidatedProgramPoints map of invalidated program points to their type
+     * @param paramTypeMap known parameter types if any exist
+     */
+    public CompilationEnvironment(
+        final CompilationPhases phases,
+        final boolean strict,
+        final RecompilableScriptFunctionData recompiledFunction,
+        final ParamTypeMap paramTypeMap,
+        final Map<Integer, Type> invalidatedProgramPoints,
+        final int[] continuationEntryPoint) {
+            this(phases, paramTypeMap, invalidatedProgramPoints, recompiledFunction, continuationEntryPoint, strict);
+    }
+
+    /**
+     * Constructor
+     * @param phases compilation phases
+     * @param strict strict mode
+     * @param recompiledFunction recompiled function
+     * @param paramTypeMap known parameter types
+     * @param invalidatedProgramPoints map of invalidated program points to their type
+     */
+    public CompilationEnvironment(
+        final CompilationPhases phases,
+        final boolean strict,
+        final RecompilableScriptFunctionData recompiledFunction,
+        final ParamTypeMap paramTypeMap,
+        final Map<Integer, Type> invalidatedProgramPoints) {
+        this(phases, paramTypeMap, invalidatedProgramPoints, recompiledFunction, null, strict);
+    }
+
+    @SuppressWarnings("null")
+    private CompilationEnvironment(
+            final CompilationPhases phases,
+            final ParamTypeMap paramTypes,
+            final Map<Integer, Type> invalidatedProgramPoints,
+            final RecompilableScriptFunctionData compiledFunction,
+            final int[] continuationEntryPoints,
+            final boolean strict) {
+        this.phases                   = phases;
+        this.paramTypes               = paramTypes;
+        this.continuationEntryPoints = continuationEntryPoints;
+        this.invalidatedProgramPoints =
+            invalidatedProgramPoints == null ?
+                Collections.unmodifiableMap(new HashMap<Integer, Type>()) :
+                invalidatedProgramPoints;
+        this.compiledFunction       = compiledFunction;
+        this.strict                   = strict;
+        this.optimistic               = phases.contains(CompilationPhase.PROGRAM_POINT_PHASE);
+
+        // If entry point array is passed, it must have at least one element
+        assert continuationEntryPoints == null || continuationEntryPoints.length > 0;
+        assert !isCompileRestOf() || isOnDemandCompilation(); // isCompileRestOf => isRecompilation
+        // continuation entry points must be among the invalidated program points
+        assert !isCompileRestOf() || (invalidatedProgramPoints != null && containsAll(invalidatedProgramPoints.keySet(), continuationEntryPoints));
+    }
+
+    private static boolean containsAll(Set<Integer> set, final int[] array) {
+        for(int i = 0; i < array.length; ++i) {
+            if(!set.contains(array[i])) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    boolean isStrict() {
+        return strict;
+    }
+
+    void setIsStrict(final boolean strict) {
+        this.strict = strict;
+    }
+
+    CompilationPhases getPhases() {
+        return phases;
+    }
+
+    /**
+     * Check if a program point was invalidated during a previous run
+     * of this method, i.e. we did an optimistic assumption that now is wrong.
+     * This is the basis of generating a wider type. getOptimisticType
+     * in this class will query for invalidation and suggest a wider type
+     * upon recompilation if this info exists.
+     * @param programPoint program point to check
+     * @return true if it was invalidated during a previous run
+     */
+    boolean isInvalidated(final int programPoint) {
+        return invalidatedProgramPoints.get(programPoint) != null;
+    }
+
+    /**
+     * Get the parameter type at a parameter position if known from previous runtime calls
+     * or optimistic profiles.
+     *
+     * @param functionNode function node to query
+     * @param pos parameter position
+     * @return known type of parameter 'pos' or null if no information exists
+     */
+    Type getParamType(final FunctionNode functionNode, final int pos) {
+        return paramTypes == null ? null : paramTypes.get(functionNode, pos);
+    }
+
+    /**
+     * Is this a compilation that generates the rest of method
+     * @return true if rest of generation
+     */
+    boolean isCompileRestOf() {
+        return continuationEntryPoints != null;
+    }
+
+    /**
+     * Is this an on-demand compilation triggered by a {@code RecompilableScriptFunctionData} - either a type
+     * specializing compilation, a deoptimizing recompilation, or a rest-of method compilation.
+     * @return true if this is an on-demand compilation, false if this is an eager compilation.
+     */
+    boolean isOnDemandCompilation() {
+        return compiledFunction != null;
+    }
+
+    /**
+     * Is this program point one of the continuation entry points for the rest-of method being compiled?
+     * @param programPoint program point
+     * @return true if it is a continuation entry point
+     */
+    boolean isContinuationEntryPoint(final int programPoint) {
+        if(continuationEntryPoints != null) {
+            for(int i = 0; i < continuationEntryPoints.length; ++i) {
+                if(continuationEntryPoints[i] == programPoint) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    int[] getContinuationEntryPoints() {
+        return continuationEntryPoints;
+    }
+
+    /**
+     * Is this program point the continuation entry points for the current rest-of method being compiled?
+     * @param programPoint program point
+     * @return true if it is the current continuation entry point
+     */
+    boolean isCurrentContinuationEntryPoint(final int programPoint) {
+        return hasCurrentContinuationEntryPoint() && getCurrentContinuationEntryPoint() == programPoint;
+    }
+
+    boolean hasCurrentContinuationEntryPoint() {
+        return continuationEntryPoints != null;
+    }
+
+    int getCurrentContinuationEntryPoint() {
+        // NOTE: we assert in the constructor that if the array is non-null, it has at least one element
+        return hasCurrentContinuationEntryPoint() ? continuationEntryPoints[0] : INVALID_PROGRAM_POINT;
+    }
+
+    /**
+     * Are optimistic types enabled ?
+     * @param node get the optimistic type for a node
+     * @return most optimistic type in current environment
+     */
+    Type getOptimisticType(final Optimistic node) {
+        assert useOptimisticTypes();
+        final Type invalidType = invalidatedProgramPoints.get(node.getProgramPoint());
+        if (invalidType != null) {
+            return invalidType;//.nextWider();
+        }
+        return node.getMostOptimisticType();
+    }
+
+    /**
+     * Should this compilation use optimistic types in general.
+     * If this is false we will only set non-object types to things that can
+     * be statically proven to be true.
+     * @return true if optimistic types should be used.
+     */
+    boolean useOptimisticTypes() {
+        return optimistic;
+    }
+
+    RecompilableScriptFunctionData getScriptFunctionData(final int functionId) {
+        return compiledFunction == null ? null : compiledFunction.getScriptFunctionData(functionId);
+    }
+
+}
--- a/src/jdk/nashorn/internal/codegen/CompilationPhase.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/src/jdk/nashorn/internal/codegen/CompilationPhase.java	Wed Feb 26 13:17:57 2014 +0100
@@ -8,19 +8,13 @@
 import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.PARSED;
 import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SPLIT;
 
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Deque;
 import java.util.EnumSet;
-import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
 import jdk.nashorn.internal.codegen.types.Range;
 import jdk.nashorn.internal.codegen.types.Type;
-import jdk.nashorn.internal.ir.CallNode;
 import jdk.nashorn.internal.ir.Expression;
 import jdk.nashorn.internal.ir.FunctionNode;
 import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
@@ -32,7 +26,6 @@
 import jdk.nashorn.internal.ir.debug.ASTWriter;
 import jdk.nashorn.internal.ir.debug.PrintVisitor;
 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;
 
@@ -41,102 +34,7 @@
  * FunctionNode into bytecode. It has an optional return value.
  */
 enum CompilationPhase {
-
-    /*
-     * Lazy initialization - tag all function nodes not the script as lazy as
-     * default policy. The will get trampolines and only be generated when
-     * called
-     */
-    LAZY_INITIALIZATION_PHASE(EnumSet.of(INITIALIZED, PARSED)) {
-        @Override
-        FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
-
-            /*
-             * For lazy compilation, we might be given a node previously marked
-             * as lazy to compile as the outermost function node in the
-             * compiler. Unmark it so it can be compiled and not cause
-             * recursion. Make sure the return type is unknown so it can be
-             * correctly deduced. Return types are always Objects in Lazy nodes
-             * as we haven't got a change to generate code for them and decude
-             * its parameter specialization
-             *
-             * TODO: in the future specializations from a callsite will be
-             * passed here so we can generate a better non-lazy version of a
-             * function from a trampoline
-             */
-
-            final FunctionNode outermostFunctionNode = fn;
-
-            final Set<FunctionNode> neverLazy = new HashSet<>();
-            final Set<FunctionNode> lazy      = new HashSet<>();
-
-            FunctionNode newFunctionNode = outermostFunctionNode;
-
-            newFunctionNode = (FunctionNode)newFunctionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
-                // self references are done with invokestatic and thus cannot
-                // have trampolines - never lazy
-                @Override
-                public boolean enterCallNode(final CallNode node) {
-                    final Node callee = node.getFunction();
-                    if (callee instanceof FunctionNode) {
-                        neverLazy.add(((FunctionNode)callee));
-                        return false;
-                    }
-                    return true;
-                }
-
-                //any function that isn't the outermost one must be marked as lazy
-                @Override
-                public boolean enterFunctionNode(final FunctionNode node) {
-                    assert compiler.isLazy();
-                    lazy.add(node);
-                    return true;
-                }
-            });
-
-            //at least one method is non lazy - the outermost one
-            neverLazy.add(newFunctionNode);
-
-            for (final FunctionNode node : neverLazy) {
-                Compiler.LOG.fine(
-                        "Marking ",
-                        node.getName(),
-                        " as non lazy, as it's a self reference");
-                lazy.remove(node);
-            }
-
-            newFunctionNode = (FunctionNode)newFunctionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
-                @Override
-                public Node leaveFunctionNode(final FunctionNode functionNode) {
-                    if (lazy.contains(functionNode)) {
-                        Compiler.LOG.fine(
-                                "Marking ",
-                                functionNode.getName(),
-                                " as lazy");
-                        final FunctionNode parent = lc.getParentFunction(functionNode);
-                        assert parent != null;
-                        lc.setFlag(parent, FunctionNode.HAS_LAZY_CHILDREN);
-                        lc.setBlockNeedsScope(parent.getBody());
-                        lc.setFlag(functionNode, FunctionNode.IS_LAZY);
-                        return functionNode;
-                    }
-
-                    return functionNode.
-                        clearFlag(lc, FunctionNode.IS_LAZY).
-                        setReturnType(lc, Type.UNKNOWN);
-                }
-            });
-
-            return newFunctionNode;
-        }
-
-        @Override
-        public String toString() {
-            return "[Lazy JIT Initialization]";
-        }
-    },
-
-    /*
+    /**
      * Constant folding pass Simple constant folding that will make elementary
      * constructs go away
      */
@@ -152,7 +50,7 @@
         }
     },
 
-    /*
+    /**
      * Lower (Control flow pass) Finalizes the control flow. Clones blocks for
      * finally constructs and similar things. Establishes termination criteria
      * for nodes Guarantee return instructions to method making sure control
@@ -171,14 +69,58 @@
         }
     },
 
-    /*
+    /**
+     * Phase used only when doing optimistic code generation. It assigns all potentially
+     * optimistic ops a program point so that an UnwarrantedException knows from where
+     * a guess went wrong when creating the continuation to roll back this execution
+     */
+    PROGRAM_POINT_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED)) {
+        @Override
+        FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
+            return (FunctionNode)fn.accept(new ProgramPoints());
+        }
+
+        @Override
+        public String toString() {
+            return "[Program Point Calculation]";
+        }
+    },
+
+    /**
+     * Splitter Split the AST into several compile units based on a heuristic size calculation.
+     * Split IR can lead to scope information being changed.
+     */
+    SPLITTING_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED)) {
+        @Override
+        FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
+            final CompileUnit outermostCompileUnit = compiler.addCompileUnit(compiler.firstCompileUnitName());
+
+            final FunctionNode newFunctionNode = new Splitter(compiler, fn, outermostCompileUnit).split(fn, true);
+
+            assert newFunctionNode.getCompileUnit() == outermostCompileUnit : "fn=" + fn.getName() + ", fn.compileUnit (" + newFunctionNode.getCompileUnit() + ") != " + outermostCompileUnit;
+
+            if (newFunctionNode.isStrict()) {
+                assert compiler.getCompilationEnvironment().isStrict();
+                compiler.getCompilationEnvironment().setIsStrict(true);
+            }
+
+            return newFunctionNode;
+        }
+
+        @Override
+        public String toString() {
+            return "[Code Splitting]";
+        }
+    },
+
+    /**
      * Attribution Assign symbols and types to all nodes.
      */
-    ATTRIBUTION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED)) {
+    ATTRIBUTION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, SPLIT)) {
         @Override
         FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
             final TemporarySymbols ts = compiler.getTemporarySymbols();
-            final FunctionNode newFunctionNode = (FunctionNode)enterAttr(fn, ts).accept(new Attr(ts));
+            final FunctionNode newFunctionNode = (FunctionNode)enterAttr(fn, ts).accept(new Attr(compiler.getCompilationEnvironment(), ts));
             if (compiler.getEnv()._print_mem_usage) {
                 Compiler.LOG.info("Attr temporary symbol count: " + ts.getTotalSymbolCount());
             }
@@ -194,12 +136,6 @@
             return (FunctionNode)functionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
                 @Override
                 public Node leaveFunctionNode(final FunctionNode node) {
-                    if (node.isLazy()) {
-                        FunctionNode newNode = node.setReturnType(lc, Type.OBJECT);
-                        return ts.ensureSymbol(lc, Type.OBJECT, newNode);
-                    }
-                    //node may have a reference here that needs to be nulled if it was referred to by
-                    //its outer context, if it is lazy and not attributed
                     return node.setReturnType(lc, Type.UNKNOWN).setSymbol(lc, null);
                 }
             });
@@ -211,12 +147,12 @@
         }
     },
 
-    /*
+    /**
      * Range analysis
      *    Conservatively prove that certain variables can be narrower than
      *    the most generic number type
      */
-    RANGE_ANALYSIS_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR)) {
+    RANGE_ANALYSIS_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, SPLIT, ATTR)) {
         @Override
         FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
             if (!compiler.getEnv()._range_analysis) {
@@ -261,13 +197,15 @@
                         final Expression expr = (Expression)node;
                         final Symbol symbol = expr.getSymbol();
                         if (symbol != null) {
-                            final Range range  = symbol.getRange();
+                            final Range range      = symbol.getRange();
                             final Type  symbolType = symbol.getSymbolType();
-                            if (!symbolType.isNumeric()) {
+
+                            if (!symbolType.isUnknown() && !symbolType.isNumeric()) {
                                 return expr;
                             }
-                            final Type  rangeType  = range.getType();
-                            if (!Type.areEquivalent(symbolType, rangeType) && Type.widest(symbolType, rangeType) == symbolType) { //we can narrow range
+
+                            final Type rangeType  = range.getType();
+                            if (!rangeType.isUnknown() && !Type.areEquivalent(symbolType, rangeType) && Type.widest(symbolType, rangeType) == symbolType) { //we can narrow range
                                 RangeAnalyzer.LOG.info("[", lc.getCurrentFunction().getName(), "] ", symbol, " can be ", range.getType(), " ", symbol.getRange());
                                 return expr.setSymbol(lc, symbol.setTypeOverrideShared(range.getType(), compiler.getTemporarySymbols()));
                             }
@@ -296,37 +234,7 @@
         }
     },
 
-
-    /*
-     * Splitter Split the AST into several compile units based on a size
-     * heuristic Splitter needs attributed AST for weight calculations (e.g. is
-     * a + b a ScriptRuntime.ADD with call overhead or a dadd with much less).
-     * Split IR can lead to scope information being changed.
-     */
-    SPLITTING_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR)) {
-        @Override
-        FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
-            final CompileUnit outermostCompileUnit = compiler.addCompileUnit(compiler.firstCompileUnitName());
-
-            final FunctionNode newFunctionNode = new Splitter(compiler, fn, outermostCompileUnit).split(fn);
-
-            assert newFunctionNode.getCompileUnit() == outermostCompileUnit : "fn.compileUnit (" + newFunctionNode.getCompileUnit() + ") != " + outermostCompileUnit;
-
-            if (newFunctionNode.isStrict()) {
-                assert compiler.getStrictMode();
-                compiler.setStrictMode(true);
-            }
-
-            return newFunctionNode;
-        }
-
-        @Override
-        public String toString() {
-            return "[Code Splitting]";
-        }
-    },
-
-    /*
+    /**
      * FinalizeTypes
      *
      * This pass finalizes the types for nodes. If Attr created wider types than
@@ -344,7 +252,7 @@
         FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
             final ScriptEnvironment env = compiler.getEnv();
 
-            final FunctionNode newFunctionNode = (FunctionNode)fn.accept(new FinalizeTypes(compiler.getTemporarySymbols()));
+            final FunctionNode newFunctionNode = (FunctionNode)fn.accept(new FinalizeTypes());
 
             if (env._print_lower_ast) {
                 env.getErr().println(new ASTWriter(newFunctionNode));
@@ -363,7 +271,7 @@
         }
     },
 
-    /*
+    /**
      * Bytecode generation:
      *
      * Generate the byte code class(es) resulting from the compiled FunctionNode
@@ -400,50 +308,12 @@
 
                 compiler.addClass(className, bytecode);
 
-                // should could be printed to stderr for generate class?
-                if (env._print_code) {
-                    final StringBuilder sb = new StringBuilder();
-                    sb.append("class: " + className).append('\n')
-                            .append(ClassEmitter.disassemble(bytecode))
-                            .append("=====");
-                    env.getErr().println(sb);
-                }
-
                 // should we verify the generated code?
                 if (env._verify_code) {
                     compiler.getCodeInstaller().verify(bytecode);
                 }
 
-                // should code be dumped to disk - only valid in compile_only mode?
-                if (env._dest_dir != null && env._compile_only) {
-                    final String fileName = className.replace('.', File.separatorChar) + ".class";
-                    final int    index    = fileName.lastIndexOf(File.separatorChar);
-
-                    final File dir;
-                    if (index != -1) {
-                        dir = new File(env._dest_dir, fileName.substring(0, index));
-                    } else {
-                        dir = new File(env._dest_dir);
-                    }
-
-                    try {
-                        if (!dir.exists() && !dir.mkdirs()) {
-                            throw new IOException(dir.toString());
-                        }
-                        final File file = new File(env._dest_dir, fileName);
-                        try (final FileOutputStream fos = new FileOutputStream(file)) {
-                            fos.write(bytecode);
-                        }
-                        Compiler.LOG.info("Wrote class to '" + file.getAbsolutePath() + '\'');
-                    } catch (final IOException e) {
-                        Compiler.LOG.warning("Skipping class dump for ",
-                                className,
-                                ": ",
-                                ECMAErrors.getMessage(
-                                    "io.error.cant.write",
-                                    dir.toString()));
-                    }
-                }
+                DumpBytecode.dumpBytecode(env, bytecode, className);
             }
 
             return newFunctionNode;
@@ -468,6 +338,11 @@
         return functionNode.hasState(pre);
     }
 
+    /**
+     * Start a compilation phase
+     * @param functionNode function to compile
+     * @return function node
+     */
     protected FunctionNode begin(final FunctionNode functionNode) {
         if (pre != null) {
             // check that everything in pre is present
@@ -484,6 +359,11 @@
         return functionNode;
     }
 
+    /**
+     * End a compilation phase
+     * @param functionNode function node to compile
+     * @return fucntion node
+     */
     protected FunctionNode end(final FunctionNode functionNode) {
         endTime = System.currentTimeMillis();
         Timing.accumulateTime(toString(), endTime - startTime);
--- a/src/jdk/nashorn/internal/codegen/CompileUnit.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/src/jdk/nashorn/internal/codegen/CompileUnit.java	Wed Feb 26 13:17:57 2014 +0100
@@ -25,10 +25,16 @@
 
 package jdk.nashorn.internal.codegen;
 
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
+
 /**
  * Used to track split class compilation.
  */
-public class CompileUnit implements Comparable<CompileUnit> {
+public final class CompileUnit implements Comparable<CompileUnit> {
     /** Current class name */
     private final String className;
 
@@ -39,6 +45,38 @@
 
     private Class<?> clazz;
 
+    private Set<FunctionInitializer> functionInitializers = new LinkedHashSet<>();
+
+    private static class FunctionInitializer {
+        final RecompilableScriptFunctionData data;
+        final FunctionNode functionNode;
+
+        FunctionInitializer(final RecompilableScriptFunctionData data, final FunctionNode functionNode) {
+            this.data = data;
+            this.functionNode = functionNode;
+        }
+
+        void initializeCode() {
+            data.initializeCode(functionNode);
+        }
+
+        @Override
+        public int hashCode() {
+            return data.hashCode() + 31 * functionNode.hashCode();
+        }
+
+        @Override
+        public boolean equals(final Object obj) {
+            if(obj == null || obj.getClass() != FunctionInitializer.class) {
+                return false;
+            }
+            final FunctionInitializer other = (FunctionInitializer)obj;
+            return data == other.data && functionNode == other.functionNode;
+        }
+
+
+    }
+
     CompileUnit(final String className, final ClassEmitter classEmitter) {
         this(className, classEmitter, 0L);
     }
@@ -71,6 +109,29 @@
         this.classEmitter = null;
     }
 
+    void addFunctionInitializer(final RecompilableScriptFunctionData data, final FunctionNode functionNode) {
+        functionInitializers.add(new FunctionInitializer(data, functionNode));
+    }
+
+    /**
+     * Returns true if this compile unit is responsible for initializing the specified function data with specified
+     * function node.
+     * @param data the function data to check
+     * @param functionNode the function node to check
+     * @return true if this unit is responsible for initializing the function data with the function node, otherwise
+     * false
+     */
+    public boolean isInitializing(final RecompilableScriptFunctionData data, final FunctionNode functionNode) {
+        return functionInitializers.contains(new FunctionInitializer(data, functionNode));
+    }
+
+    void initializeFunctionsCode() {
+        for(final FunctionInitializer init: functionInitializers) {
+            init.initializeCode();
+        }
+        functionInitializers = Collections.emptySet();
+    }
+
     /**
      * Add weight to this compile unit
      * @param w weight to add
--- a/src/jdk/nashorn/internal/codegen/Compiler.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/src/jdk/nashorn/internal/codegen/Compiler.java	Wed Feb 26 13:17:57 2014 +0100
@@ -28,8 +28,6 @@
 import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
 import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
 import static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS;
-import static jdk.nashorn.internal.codegen.CompilerConstants.DEFAULT_SCRIPT_NAME;
-import static jdk.nashorn.internal.codegen.CompilerConstants.LAZY;
 import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
 import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
 import static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE;
@@ -41,13 +39,12 @@
 import java.security.AccessController;
 import java.security.PrivilegedActionException;
 import java.security.PrivilegedExceptionAction;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.EnumSet;
 import java.util.HashMap;
+import java.util.IdentityHashMap;
 import java.util.LinkedHashMap;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -56,6 +53,7 @@
 import java.util.logging.Level;
 import jdk.internal.dynalink.support.NameCodec;
 import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
+import jdk.nashorn.internal.codegen.CompilationEnvironment.CompilationPhases;
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.FunctionNode;
 import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
@@ -64,10 +62,10 @@
 import jdk.nashorn.internal.ir.debug.ObjectSizeCalculator;
 import jdk.nashorn.internal.runtime.CodeInstaller;
 import jdk.nashorn.internal.runtime.DebugLogger;
+import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
 import jdk.nashorn.internal.runtime.ScriptEnvironment;
 import jdk.nashorn.internal.runtime.Source;
 import jdk.nashorn.internal.runtime.Timing;
-import jdk.nashorn.internal.runtime.options.Options;
 
 /**
  * Responsible for converting JavaScripts to java byte code. Main entry
@@ -86,6 +84,7 @@
     private Source source;
 
     private String sourceName;
+    private String sourceURL;
 
     private final Map<String, byte[]> bytecode;
 
@@ -93,14 +92,12 @@
 
     private final ConstantData constantData;
 
-    private final CompilationSequence sequence;
+    private final CompilationEnvironment compilationEnv;
 
-    private final ScriptEnvironment env;
+    private final ScriptEnvironment scriptEnv;
 
     private String scriptName;
 
-    private boolean strict;
-
     private final CodeInstaller<ScriptEnvironment> installer;
 
     private final TemporarySymbols temporarySymbols = new TemporarySymbols();
@@ -109,6 +106,12 @@
      *  that affect classes */
     public static final DebugLogger LOG = new DebugLogger("compiler");
 
+    static {
+        if (ScriptEnvironment.globalOptimistic()) {
+            LOG.warning("Running with optimistic types. This is experimental. To switch off, use -Dnashorn.optimistic=false");
+        }
+    }
+
     /**
      * This array contains names that need to be reserved at the start
      * of a compile, to avoid conflict with variable names later introduced.
@@ -124,181 +127,47 @@
         ARGUMENTS.symbolName()
     };
 
-    /**
-     * This class makes it possible to do your own compilation sequence
-     * from the code generation package. There are predefined compilation
-     * sequences already
-     */
-    @SuppressWarnings("serial")
-    static class CompilationSequence extends LinkedList<CompilationPhase> {
+    private void initCompiler(final String className, final FunctionNode functionNode) {
+        this.source = functionNode.getSource();
+        this.sourceName = functionNode.getSourceName();
+        this.sourceURL = functionNode.getSourceURL();
 
-        CompilationSequence(final CompilationPhase... phases) {
-            super(Arrays.asList(phases));
+        if (functionNode.isStrict()) {
+            compilationEnv.setIsStrict(true);
         }
 
-        CompilationSequence(final CompilationSequence sequence) {
-            this(sequence.toArray(new CompilationPhase[sequence.size()]));
-        }
+        final StringBuilder sb = new StringBuilder();
+        sb.append(functionNode.uniqueName(className)).
+                append('$').
+                append(safeSourceName(functionNode.getSource()));
+        this.scriptName = sb.toString();
+    }
 
-        CompilationSequence insertAfter(final CompilationPhase phase, final CompilationPhase newPhase) {
-            final CompilationSequence newSeq = new CompilationSequence();
-            for (final CompilationPhase elem : this) {
-                newSeq.add(phase);
-                if (elem.equals(phase)) {
-                    newSeq.add(newPhase);
-                }
-            }
-            assert newSeq.contains(newPhase);
-            return newSeq;
-        }
-
-        CompilationSequence insertBefore(final CompilationPhase phase, final CompilationPhase newPhase) {
-            final CompilationSequence newSeq = new CompilationSequence();
-            for (final CompilationPhase elem : this) {
-                if (elem.equals(phase)) {
-                    newSeq.add(newPhase);
-                }
-                newSeq.add(phase);
-            }
-            assert newSeq.contains(newPhase);
-            return newSeq;
-        }
-
-        CompilationSequence insertFirst(final CompilationPhase phase) {
-            final CompilationSequence newSeq = new CompilationSequence(this);
-            newSeq.addFirst(phase);
-            return newSeq;
-        }
-
-        CompilationSequence insertLast(final CompilationPhase phase) {
-            final CompilationSequence newSeq = new CompilationSequence(this);
-            newSeq.addLast(phase);
-            return newSeq;
-        }
+    private Compiler(final CompilationEnvironment compilationEnv, final ScriptEnvironment scriptEnv, final CodeInstaller<ScriptEnvironment> installer) {
+        this.scriptEnv      = scriptEnv;
+        this.compilationEnv = compilationEnv;
+        this.installer      = installer;
+        this.constantData   = new ConstantData();
+        this.compileUnits   = new TreeSet<>();
+        this.bytecode       = new LinkedHashMap<>();
     }
 
     /**
-     * Environment information known to the compile, e.g. params
+     * Constructor - common entry point for generating code.
+     * @param env compilation environment
+     * @param installer code installer
      */
-    public static class Hints {
-        private final Type[] paramTypes;
-
-        /** singleton empty hints */
-        public static final Hints EMPTY = new Hints();
-
-        private Hints() {
-            this.paramTypes = null;
-        }
-
-        /**
-         * Constructor
-         * @param paramTypes known parameter types for this callsite
-         */
-        public Hints(final Type[] paramTypes) {
-            this.paramTypes = paramTypes;
-        }
-
-        /**
-         * Get the parameter type for this parameter position, or
-         * null if now known
-         * @param pos position
-         * @return parameter type for this callsite if known
-         */
-        public Type getParameterType(final int pos) {
-            if (paramTypes != null && pos < paramTypes.length) {
-                return paramTypes[pos];
-            }
-            return null;
-        }
+    public Compiler(final CompilationEnvironment env, final CodeInstaller<ScriptEnvironment> installer) {
+        this(env, installer.getOwner(), installer);
     }
 
     /**
-     * Standard (non-lazy) compilation, that basically will take an entire script
-     * and JIT it at once. This can lead to long startup time and fewer type
-     * specializations
+     * ScriptEnvironment constructor for compiler. Used only from Shell and --compile-only flag
+     * No code installer supplied
+     * @param scriptEnv script environment
      */
-    final static CompilationSequence SEQUENCE_EAGER = new CompilationSequence(
-        CompilationPhase.CONSTANT_FOLDING_PHASE,
-        CompilationPhase.LOWERING_PHASE,
-        CompilationPhase.ATTRIBUTION_PHASE,
-        CompilationPhase.RANGE_ANALYSIS_PHASE,
-        CompilationPhase.SPLITTING_PHASE,
-        CompilationPhase.TYPE_FINALIZATION_PHASE,
-        CompilationPhase.BYTECODE_GENERATION_PHASE);
-
-    final static CompilationSequence SEQUENCE_LAZY =
-        SEQUENCE_EAGER.insertFirst(CompilationPhase.LAZY_INITIALIZATION_PHASE);
-
-    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()) {
-            return '$' + LAZY.symbolName() + '$' + functionNode.getName();
-        }
-        return "";
-    }
-
-    /**
-     * Constructor
-     *
-     * @param env          script environment
-     * @param installer    code installer
-     * @param sequence     {@link Compiler.CompilationSequence} of {@link CompilationPhase}s to apply as this compilation
-     * @param strict       should this compilation use strict mode semantics
-     */
-    //TODO support an array of FunctionNodes for batch lazy compilation
-    Compiler(final ScriptEnvironment env, final CodeInstaller<ScriptEnvironment> installer, final CompilationSequence sequence, final boolean strict) {
-        this.env           = env;
-        this.sequence      = sequence;
-        this.installer     = installer;
-        this.constantData  = new ConstantData();
-        this.compileUnits  = new TreeSet<>();
-        this.bytecode      = new LinkedHashMap<>();
-    }
-
-    private void initCompiler(final FunctionNode functionNode) {
-        this.strict        = strict || functionNode.isStrict();
-        final StringBuilder sb = new StringBuilder();
-        sb.append(functionNode.uniqueName(DEFAULT_SCRIPT_NAME.symbolName() + lazyTag(functionNode))).
-                append('$').
-                append(safeSourceName(functionNode.getSource()));
-        this.source = functionNode.getSource();
-        this.sourceName = functionNode.getSourceName();
-        this.scriptName = sb.toString();
-    }
-
-    /**
-     * Constructor
-     *
-     * @param installer    code installer
-     * @param strict       should this compilation use strict mode semantics
-     */
-    public Compiler(final CodeInstaller<ScriptEnvironment> installer, final boolean strict) {
-        this(installer.getOwner(), installer, sequence(installer.getOwner()._lazy_compilation), strict);
-    }
-
-    /**
-     * Constructor - compilation will use the same strict semantics as in script environment
-     *
-     * @param installer    code installer
-     */
-    public Compiler(final CodeInstaller<ScriptEnvironment> installer) {
-        this(installer.getOwner(), installer, sequence(installer.getOwner()._lazy_compilation), installer.getOwner()._strict);
-    }
-
-    /**
-     * Constructor - compilation needs no installer, but uses a script environment
-     * Used in "compile only" scenarios
-     * @param env a script environment
-     */
-    public Compiler(final ScriptEnvironment env) {
-        this(env, null, sequence(env._lazy_compilation), env._strict);
+    public Compiler(final ScriptEnvironment scriptEnv) {
+        this(new CompilationEnvironment(CompilationPhases.EAGER, scriptEnv._strict), scriptEnv, null);
     }
 
     private static void printMemoryUsage(final String phaseName, final FunctionNode functionNode) {
@@ -337,30 +206,53 @@
         }
     }
 
+    CompilationEnvironment getCompilationEnvironment() {
+        return compilationEnv;
+    }
+
     /**
-     * Execute the compilation this Compiler was created with
+     * Execute the compilation this Compiler was created with with default class name
      * @param functionNode function node to compile from its current state
      * @throws CompilationException if something goes wrong
      * @return function node that results from code transforms
      */
     public FunctionNode compile(final FunctionNode functionNode) throws CompilationException {
+        return compile(CompilerConstants.DEFAULT_SCRIPT_NAME.symbolName(), functionNode);
+    }
+
+    /**
+     * Execute the compilation this Compiler was created with
+     * @param className    class name for the compile
+     * @param functionNode function node to compile from its current state
+     * @throws CompilationException if something goes wrong
+     * @return function node that results from code transforms
+     */
+    public FunctionNode compile(final String className, final FunctionNode functionNode) throws CompilationException {
+        try {
+            return compileInternal(className, functionNode);
+        } catch(AssertionError e) {
+            throw new AssertionError("Assertion failure compiling " + functionNode.getSource(), e);
+        }
+    }
+
+    private FunctionNode compileInternal(final String className, final FunctionNode functionNode) throws CompilationException {
         FunctionNode newFunctionNode = functionNode;
 
-        initCompiler(newFunctionNode); //TODO move this state into functionnode?
+        initCompiler(className, newFunctionNode); //TODO move this state into functionnode?
 
         for (final String reservedName : RESERVED_NAMES) {
             newFunctionNode.uniqueName(reservedName);
         }
 
-        final boolean fine = !LOG.levelAbove(Level.FINE);
-        final boolean info = !LOG.levelAbove(Level.INFO);
+        final boolean fine = LOG.levelFinerThanOrEqual(Level.FINE);
+        final boolean info = LOG.levelFinerThanOrEqual(Level.INFO);
 
         long time = 0L;
 
-        for (final CompilationPhase phase : sequence) {
+        for (final CompilationPhase phase : compilationEnv.getPhases()) {
             newFunctionNode = phase.apply(this, newFunctionNode);
 
-            if (env._print_mem_usage) {
+            if (scriptEnv._print_mem_usage) {
                 printMemoryUsage(phase.toString(), newFunctionNode);
             }
 
@@ -441,7 +333,7 @@
     public Class<?> install(final FunctionNode functionNode) {
         final long t0 = Timing.isEnabled() ? System.currentTimeMillis() : 0L;
 
-        assert functionNode.hasState(CompilationState.EMITTED) : functionNode.getName() + " has no bytecode and cannot be installed";
+        assert functionNode.hasState(CompilationState.EMITTED) : functionNode.getName() + " has unexpected compilation state";
 
         final Map<String, Class<?>> installedClasses = new HashMap<>();
 
@@ -464,8 +356,17 @@
             installedClasses.put(className, install(className, code));
         }
 
+        final Map<RecompilableScriptFunctionData, RecompilableScriptFunctionData> rfns = new IdentityHashMap<>();
+        for(final Object constant: getConstantData().constants) {
+            if(constant instanceof RecompilableScriptFunctionData) {
+                final RecompilableScriptFunctionData rfn = (RecompilableScriptFunctionData)constant;
+                rfns.put(rfn, rfn);
+            }
+        }
+
         for (final CompileUnit unit : compileUnits) {
             unit.setCode(installedClasses.get(unit.getUnitClassName()));
+            unit.initializeFunctionsCode();
         }
 
         final StringBuilder sb;
@@ -503,14 +404,6 @@
         return compileUnits;
     }
 
-    boolean getStrictMode() {
-        return strict;
-    }
-
-    void setStrictMode(final boolean strict) {
-        this.strict = strict;
-    }
-
     ConstantData getConstantData() {
         return constantData;
     }
@@ -528,7 +421,11 @@
     }
 
     ScriptEnvironment getEnv() {
-        return this.env;
+        return this.scriptEnv;
+    }
+
+    String getSourceURL() {
+        return sourceURL;
     }
 
     private String safeSourceName(final Source src) {
@@ -540,7 +437,7 @@
         }
 
         baseName = baseName.replace('.', '_').replace('-', '_');
-        if (! env._loader_per_compile) {
+        if (! scriptEnv._loader_per_compile) {
             baseName = baseName + installer.getUniqueScriptId();
         }
         final String mangled = NameCodec.encode(baseName);
@@ -576,7 +473,7 @@
     }
 
     private CompileUnit initCompileUnit(final String unitClassName, final long initialWeight) {
-        final ClassEmitter classEmitter = new ClassEmitter(env, sourceName, unitClassName, strict);
+        final ClassEmitter classEmitter = new ClassEmitter(scriptEnv, sourceName, unitClassName, compilationEnv.isStrict());
         final CompileUnit  compileUnit  = new CompileUnit(unitClassName, classEmitter, initialWeight);
 
         classEmitter.begin();
@@ -611,23 +508,4 @@
     public static String binaryName(final String name) {
         return name.replace('/', '.');
     }
-
-    /**
-     * Should we use integers for arithmetic operations as well?
-     * TODO: We currently generate no overflow checks so this is
-     * disabled
-     *
-     * @return true if arithmetic operations should not widen integer
-     *   operands by default.
-     */
-    static boolean shouldUseIntegerArithmetic() {
-        return USE_INT_ARITH;
-    }
-
-    private static final boolean USE_INT_ARITH;
-
-    static {
-        USE_INT_ARITH  =  Options.getBooleanProperty("nashorn.compiler.intarithmetic");
-        assert !USE_INT_ARITH : "Integer arithmetic is not enabled";
-    }
 }
--- a/src/jdk/nashorn/internal/codegen/CompilerConstants.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/src/jdk/nashorn/internal/codegen/CompilerConstants.java	Wed Feb 26 13:17:57 2014 +0100
@@ -51,9 +51,6 @@
     /** the __LINE__ variable */
     __LINE__,
 
-    /** lazy prefix for classes of jitted methods */
-    LAZY("Lazy"),
-
     /** constructor name */
     INIT("<init>"),
 
@@ -78,8 +75,11 @@
     /** function prefix for anonymous functions */
     ANON_FUNCTION_PREFIX("L:"),
 
-    /** method name for Java method that is script entry point */
-    RUN_SCRIPT("runScript"),
+    /** method name for Java method that is the program entry point */
+    PROGRAM(":program"),
+
+    /** method name for Java method that creates the script function for the program */
+    CREATE_PROGRAM_FUNCTION(":createProgramFunction"),
 
     /**
      * "this" name symbol for a parameter representing ECMAScript "this" in static methods that are compiled
@@ -161,7 +161,7 @@
     /** get map */
     GET_MAP(":getMap"),
 
-    /** get map */
+    /** set map */
     SET_MAP(":setMap"),
 
     /** get array prefix */
@@ -173,7 +173,7 @@
     /**
      * Prefix used for internal methods generated in script clases.
      */
-    public static final String INTERNAL_METHOD_PREFIX = ":";
+    private static final String INTERNAL_METHOD_PREFIX = ":";
 
     private final String symbolName;
     private final Class<?> type;
@@ -198,9 +198,23 @@
     }
 
     private CompilerConstants(final String symbolName, final Class<?> type, final int slot) {
-        this.symbolName  = symbolName;
-        this.type = type;
-        this.slot = slot;
+        this.symbolName = symbolName;
+        this.type       = type;
+        this.slot       = slot;
+    }
+
+    /**
+     * Check whether a name is that of a reserved compiler constnat
+     * @param name name
+     * @return true if compiler constant name
+     */
+    public static boolean isCompilerConstant(final String name) {
+        for (final CompilerConstants cc : CompilerConstants.values()) {
+            if (name.equals(cc.symbolName())) {
+                return true;
+            }
+        }
+        return false;
     }
 
     /**
@@ -539,6 +553,18 @@
     }
 
     /**
+     * Returns true if the passed string looks like a method name of an internally generated Nashorn method. Basically,
+     * if it starts with a colon character {@code :} but is not the name of the program method {@code :program}.
+     * Program function is not considered internal as we want it to show up in exception stack traces.
+     * @param methodName the name of a method
+     * @return true if it looks like an internal Nashorn method name.
+     * @throws NullPointerException if passed null
+     */
+    public static boolean isInternalMethodName(final String methodName) {
+        return methodName.startsWith(INTERNAL_METHOD_PREFIX) && !methodName.equals(PROGRAM.symbolName);
+     }
+
+    /**
      * Private class representing an access. This can generate code into a method code or
      * a field access.
      */
--- a/src/jdk/nashorn/internal/codegen/Condition.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/src/jdk/nashorn/internal/codegen/Condition.java	Wed Feb 26 13:17:57 2014 +0100
@@ -66,8 +66,7 @@
         case GT:
             return IFGT;
         default:
-            assert false;
-            return -1;
+            throw new UnsupportedOperationException("toUnary:" + c.toString());
         }
     }
 
@@ -86,8 +85,7 @@
         case GT:
             return IF_ICMPGT;
         default:
-            assert false;
-            return -1;
+            throw new UnsupportedOperationException("toBinary:" + c.toString());
         }
     }
 }
--- a/src/jdk/nashorn/internal/codegen/ConstantData.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/src/jdk/nashorn/internal/codegen/ConstantData.java	Wed Feb 26 13:17:57 2014 +0100
@@ -145,6 +145,7 @@
      * @return the index in the constant pool that the object was given
      */
     public int add(final Object object) {
+        assert object != null;
         final Object  entry = object.getClass().isArray() ? new ArrayWrapper(object) : object;
         final Integer value = objectMap.get(entry);
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/codegen/DumpBytecode.java	Wed Feb 26 13:17:57 2014 +0100
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.codegen;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import jdk.nashorn.internal.runtime.ECMAErrors;
+import jdk.nashorn.internal.runtime.ScriptEnvironment;
+
+/**
+ * Class that facilitates dumping bytecode to disk
+ */
+final class DumpBytecode {
+    static void dumpBytecode(final ScriptEnvironment env, final byte[] bytecode, final String className) {
+        File dir = null;
+        try {
+            // should could be printed to stderr for generate class?
+            if (env._print_code) {
+
+                final StringBuilder sb = new StringBuilder();
+                sb.append("class: " + className).
+                    append('\n').
+                    append(ClassEmitter.disassemble(bytecode)).
+                    append("=====");
+
+                if (env._print_code_dir != null) {
+
+                    String name = className;
+                    int dollar = name.lastIndexOf('$');
+                    if (dollar != -1) {
+                        name = name.substring(dollar + 1);
+                    }
+
+                    dir = new File(env._print_code_dir);
+                    if (!dir.exists() && !dir.mkdirs()) {
+                        throw new IOException(dir.toString());
+                    }
+
+                    File file;
+                    String fileName;
+                    int uniqueId = 0;
+                    do {
+                        fileName = name + (uniqueId == 0 ? "" : "_" + uniqueId) + ".bytecode";
+                        file = new File(env._print_code_dir, fileName);
+                        uniqueId++;
+                    } while (file.exists());
+
+                    try (final PrintWriter pw = new PrintWriter(new FileOutputStream(file))) {
+                        pw.print(sb.toString());
+                        pw.flush();
+                    }
+                } else {
+                    env.getErr().println(sb);
+                }
+            }
+
+
+            // should code be dumped to disk - only valid in compile_only mode?
+            if (env._dest_dir != null && env._compile_only) {
+                final String fileName = className.replace('.', File.separatorChar) + ".class";
+                final int    index    = fileName.lastIndexOf(File.separatorChar);
+
+                if (index != -1) {
+                    dir = new File(env._dest_dir, fileName.substring(0, index));
+                } else {
+                    dir = new File(env._dest_dir);
+                }
+
+                if (!dir.exists() && !dir.mkdirs()) {
+                    throw new IOException(dir.toString());
+                }
+                final File file = new File(env._dest_dir, fileName);
+                try (final FileOutputStream fos = new FileOutputStream(file)) {
+                    fos.write(bytecode);
+                }
+                Compiler.LOG.info("Wrote class to '" + file.getAbsolutePath() + '\'');
+            }
+        } catch (final IOException e) {
+            Compiler.LOG.warning("Skipping class dump for ",
+                    className,
+                    ": ",
+                    ECMAErrors.getMessage(
+                        "io.error.cant.write",
+                        dir.toString()));
+        }
+    }
+
+}
--- a/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java	Wed Feb 26 13:17:57 2014 +0100
@@ -28,8 +28,9 @@
 import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
 import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
 import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor;
+import static jdk.nashorn.internal.codegen.ObjectClassGenerator.getFieldName;
 import static jdk.nashorn.internal.codegen.ObjectClassGenerator.getPaddedFieldCount;
-import static jdk.nashorn.internal.codegen.types.Type.OBJECT;
+import static jdk.nashorn.internal.codegen.ObjectClassGenerator.PRIMITIVE_FIELD_TYPE;
 import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.getArrayIndex;
 import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex;
 
@@ -51,19 +52,16 @@
  * @param <T> the value type for the fields being written on object creation, e.g. Node
  * @see jdk.nashorn.internal.ir.Node
  */
-public abstract class FieldObjectCreator<T> extends ObjectCreator {
+public abstract class FieldObjectCreator<T> extends ObjectCreator<T> {
 
-    private         String        fieldObjectClassName;
-    private         Class<?>      fieldObjectClass;
-    private         int           fieldCount;
-    private         int           paddedFieldCount;
-    private         int           paramCount;
-
-    /** array of corresponding values to symbols (null for no values) */
-    private final List<T> values;
+    private String                        fieldObjectClassName;
+    private Class<? extends ScriptObject> fieldObjectClass;
+    private int                           fieldCount;
+    private int                           paddedFieldCount;
+    private int                           paramCount;
 
     /** call site flags to be used for invocations */
-    private final int     callSiteFlags;
+    private final int callSiteFlags;
 
     /**
      * Constructor
@@ -73,8 +71,8 @@
      * @param symbols  symbols for fields in object
      * @param values   list of values corresponding to keys
      */
-    FieldObjectCreator(final CodeGenerator codegen, final List<String> keys, final List<Symbol> symbols, final List<T> values) {
-        this(codegen, keys, symbols, values, false, false);
+    FieldObjectCreator(final CodeGenerator codegen, final List<MapTuple<T>> tuples) {
+        this(codegen, tuples, false, false);
     }
 
     /**
@@ -87,9 +85,8 @@
      * @param isScope      is this a scope object
      * @param hasArguments does the created object have an "arguments" property
      */
-    FieldObjectCreator(final CodeGenerator codegen, final List<String> keys, final List<Symbol> symbols, final List<T> values, final boolean isScope, final boolean hasArguments) {
-        super(codegen, keys, symbols, isScope, hasArguments);
-        this.values        = values;
+    FieldObjectCreator(final CodeGenerator codegen, final List<MapTuple<T>> tuples, final boolean isScope, final boolean hasArguments) {
+        super(codegen, tuples, isScope, hasArguments);
         this.callSiteFlags = codegen.getCallSiteFlags();
 
         countFields();
@@ -105,7 +102,19 @@
     protected void makeObject(final MethodEmitter method) {
         makeMap();
 
-        method._new(getClassName()).dup(); // create instance
+        final String className = getClassName();
+        try {
+            // NOTE: we must load the actual structure class here, because the API operates with Nashorn Type objects,
+            // and Type objects need a loaded class, for better or worse. We also have to be specific and use the type
+            // of the actual structure class, we can't generalize it to e.g. Type.typeFor(ScriptObject.class) as the
+            // exact type information is needed for generating continuations in rest-of methods. If we didn't do this,
+            // object initializers like { x: arr[i] } would fail during deoptimizing compilation on arr[i], as the
+            // values restored from the RewriteException would be cast to "ScriptObject" instead of to e.g. "JO4", and
+            // subsequently the "PUTFIELD J04.L0" instruction in the continuation code would fail bytecode verification.
+            method._new(Context.forStructureClass(className.replace('/', '.'))).dup();
+        } catch (final ClassNotFoundException e) {
+            throw new AssertionError(e);
+        }
         loadMap(method); //load the map
 
         if (isScope()) {
@@ -113,31 +122,27 @@
 
             if (hasArguments()) {
                 method.loadCompilerConstant(ARGUMENTS);
-                method.invoke(constructorNoLookup(getClassName(), PropertyMap.class, ScriptObject.class, ARGUMENTS.type()));
+                method.invoke(constructorNoLookup(className, PropertyMap.class, ScriptObject.class, ARGUMENTS.type()));
             } else {
-                method.invoke(constructorNoLookup(getClassName(), PropertyMap.class, ScriptObject.class));
+                method.invoke(constructorNoLookup(className, PropertyMap.class, ScriptObject.class));
             }
         } else {
-            method.invoke(constructorNoLookup(getClassName(), PropertyMap.class));
+            method.invoke(constructorNoLookup(className, PropertyMap.class));
         }
 
         // Set values.
-        final Iterator<Symbol> symbolIter = symbols.iterator();
-        final Iterator<String> keyIter    = keys.iterator();
-        final Iterator<T>      valueIter  = values.iterator();
+        final Iterator<MapTuple<T>> iter = tuples.iterator();
 
-        while (symbolIter.hasNext()) {
-            final Symbol symbol = symbolIter.next();
-            final String key    = keyIter.next();
-            final T      value  = valueIter.next();
-
-            if (symbol != null && value != null) {
-                final int index = getArrayIndex(key);
-
+        while (iter.hasNext()) {
+            final MapTuple<T> tuple = iter.next();
+            //we only load when we have both symbols and values (which can be == the symbol)
+            //if we didn't load, we need an array property
+            if (tuple.symbol != null && tuple.value != null) {
+                final int index = getArrayIndex(tuple.key);
                 if (!isValidArrayIndex(index)) {
-                    putField(method, key, symbol.getFieldIndex(), value);
+                    putField(method, tuple.key, tuple.symbol.getFieldIndex(), tuple);
                 } else {
-                    putSlot(method, ArrayIndex.toLongIndex(index), value);
+                    putSlot(method, ArrayIndex.toLongIndex(index), tuple);
                 }
             }
         }
@@ -151,13 +156,6 @@
     }
 
     /**
-     * Technique for loading an initial value. Defined by anonymous subclasses in code gen.
-     *
-     * @param value Value to load.
-     */
-    protected abstract void loadValue(T value);
-
-    /**
      * Store a value in a field of the generated class object.
      *
      * @param method      Script method.
@@ -165,12 +163,18 @@
      * @param fieldIndex  Field number.
      * @param value       Value to store.
      */
-    private void putField(final MethodEmitter method, final String key, final int fieldIndex, final T value) {
+    private void putField(final MethodEmitter method, final String key, final int fieldIndex, final MapTuple<T> tuple) {
         method.dup();
 
-        loadValue(value);
-        method.convert(OBJECT);
-        method.putField(getClassName(), ObjectClassGenerator.getFieldName(fieldIndex, Type.OBJECT), typeDescriptor(Object.class));
+        loadTuple(method, tuple);
+
+        final boolean isPrimitive = tuple.isPrimitive();
+        final Type    fieldType   = isPrimitive ? PRIMITIVE_FIELD_TYPE : Type.OBJECT;
+        final String  fieldClass  = getClassName();
+        final String  fieldName   = getFieldName(fieldIndex, fieldType);
+        final String  fieldDesc   = typeDescriptor(fieldType.getTypeClass());
+
+        method.putField(fieldClass, fieldName, fieldDesc);
     }
 
     /**
@@ -180,14 +184,14 @@
      * @param index  Slot index.
      * @param value  Value to store.
      */
-    private void putSlot(final MethodEmitter method, final long index, final T value) {
+    private void putSlot(final MethodEmitter method, final long index, final MapTuple<T> tuple) {
         method.dup();
         if (JSType.isRepresentableAsInt(index)) {
-            method.load((int) index);
+            method.load((int)index);
         } else {
             method.load(index);
         }
-        loadValue(value);
+        loadTuple(method, tuple, false); //we don't pack array like objects
         method.dynamicSetIndex(callSiteFlags);
     }
 
@@ -220,7 +224,8 @@
      * Tally the number of fields and parameters.
      */
     private void countFields() {
-        for (final Symbol symbol : this.symbols) {
+        for (final MapTuple<T> tuple : tuples) {
+            final Symbol symbol = tuple.symbol;
             if (symbol != null) {
                 if (hasArguments() && symbol.isParam()) {
                     symbol.setFieldIndex(paramCount++);
--- a/src/jdk/nashorn/internal/codegen/FinalizeTypes.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/src/jdk/nashorn/internal/codegen/FinalizeTypes.java	Wed Feb 26 13:17:57 2014 +0100
@@ -26,6 +26,7 @@
 package jdk.nashorn.internal.codegen;
 
 import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
+import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
 import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
 
 import jdk.nashorn.internal.ir.BinaryNode;
@@ -38,7 +39,6 @@
 import jdk.nashorn.internal.ir.LexicalContext;
 import jdk.nashorn.internal.ir.Node;
 import jdk.nashorn.internal.ir.Symbol;
-import jdk.nashorn.internal.ir.TemporarySymbols;
 import jdk.nashorn.internal.ir.UnaryNode;
 import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
 import jdk.nashorn.internal.parser.Token;
@@ -62,11 +62,8 @@
 
     private static final DebugLogger LOG = new DebugLogger("finalize");
 
-    private final TemporarySymbols temporarySymbols;
-
-    FinalizeTypes(final TemporarySymbols temporarySymbols) {
+    FinalizeTypes() {
         super(new LexicalContext());
-        this.temporarySymbols = temporarySymbols;
     }
 
     @Override
@@ -106,29 +103,41 @@
 
     @Override
     public Node leaveExpressionStatement(final ExpressionStatement expressionStatement) {
-        temporarySymbols.reuse();
         return expressionStatement.setExpression(discard(expressionStatement.getExpression()));
     }
 
     @Override
     public boolean enterFunctionNode(final FunctionNode functionNode) {
-        if (functionNode.isLazy()) {
-            return false;
-        }
+        // TODO: now that Splitter comes before Attr, these can probably all be moved to Attr.
 
-        // 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.
+        // 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.
         if (!functionNode.needsCallee()) {
             functionNode.compilerConstant(CALLEE).setNeedsSlot(false);
         }
-        // Similar reasoning applies to __scope__ symbol: if the function doesn't need either parent scope and none of
-        // its blocks create a scope, we ensure it doesn't get a slot, but we can't determine whether it needs a scope
+        // Similar reasoning applies to SCOPE symbol: if the function doesn't need either parent scope and none of its
+        // blocks create a scope, we ensure it doesn't get a slot, but we can't determine whether it needs a scope
         // earlier than this phase.
         if (!(functionNode.hasScopeBlock() || functionNode.needsParentScope())) {
             functionNode.compilerConstant(SCOPE).setNeedsSlot(false);
         }
-
+        // Also, we must wait until after Splitter to see if the function ended up needing the RETURN symbol.
+        if (!functionNode.usesReturnSymbol()) {
+            functionNode.compilerConstant(RETURN).setNeedsSlot(false);
+        }
+        // Named function expressions that end up not referencing themselves won't need a local slot for the self symbol.
+        if(!functionNode.isDeclared() && !functionNode.usesSelfSymbol() && !functionNode.isAnonymous()) {
+            final Symbol selfSymbol = functionNode.getBody().getExistingSymbol(functionNode.getIdent().getName());
+            if(selfSymbol != null) {
+                if(selfSymbol.isFunctionSelf()) {
+                    selfSymbol.setNeedsSlot(false);
+                    selfSymbol.clearFlag(Symbol.IS_VAR);
+                }
+            } else {
+                assert functionNode.isProgram();
+            }
+        }
         return true;
     }
 
@@ -183,16 +192,16 @@
         }
     }
 
-    private static Expression discard(final Expression node) {
-        if (node.getSymbol() != null) {
-            final UnaryNode discard = new UnaryNode(Token.recast(node.getToken(), TokenType.DISCARD), node);
+    private static Expression discard(final Expression expr) {
+        if (expr.getSymbol() != null) {
+            UnaryNode discard = new UnaryNode(Token.recast(expr.getToken(), TokenType.DISCARD), expr);
             //discard never has a symbol in the discard node - then it would be a nop
-            assert !node.isTerminal();
+            assert !expr.isTerminal();
             return discard;
         }
 
         // node has no result (symbol) so we can keep it the way it is
-        return node;
+        return expr;
     }
 
 
--- a/src/jdk/nashorn/internal/codegen/FoldConstants.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/src/jdk/nashorn/internal/codegen/FoldConstants.java	Wed Feb 26 13:17:57 2014 +0100
@@ -80,11 +80,6 @@
     }
 
     @Override
-    public boolean enterFunctionNode(final FunctionNode functionNode) {
-        return !functionNode.isLazy();
-    }
-
-    @Override
     public Node leaveFunctionNode(final FunctionNode functionNode) {
         return functionNode.setState(lc, CompilationState.CONSTANT_FOLDED);
     }
@@ -163,7 +158,7 @@
 
         @Override
         protected LiteralNode<?> eval() {
-            final Node rhsNode = parent.rhs();
+            final Node rhsNode = parent.getExpression();
 
             if (!(rhsNode instanceof LiteralNode)) {
                 return null;
@@ -311,8 +306,8 @@
                 return null;
             }
 
-            isInteger &= value != 0.0 && JSType.isRepresentableAsInt(value);
-            isLong    &= value != 0.0 && JSType.isRepresentableAsLong(value);
+            isInteger &= JSType.isRepresentableAsInt(value) && !JSType.isNegativeZero(value);
+            isLong    &= JSType.isRepresentableAsLong(value) && !JSType.isNegativeZero(value);
 
             if (isInteger) {
                 return LiteralNode.newInstance(token, finish, (int)value);
--- a/src/jdk/nashorn/internal/codegen/FunctionSignature.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/src/jdk/nashorn/internal/codegen/FunctionSignature.java	Wed Feb 26 13:17:57 2014 +0100
@@ -195,6 +195,14 @@
     }
 
     /**
+     * Get the param types for this function signature
+     * @return cloned vector of param types
+     */
+    public Type[] getParamTypes() {
+        return paramTypes.clone();
+    }
+
+    /**
      * Return the {@link MethodType} for this function signature
      * @return the method type
      */
--- a/src/jdk/nashorn/internal/codegen/Label.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/src/jdk/nashorn/internal/codegen/Label.java	Wed Feb 26 13:17:57 2014 +0100
@@ -25,7 +25,6 @@
 package jdk.nashorn.internal.codegen;
 
 import jdk.nashorn.internal.codegen.types.Type;
-import jdk.nashorn.internal.runtime.Debug;
 
 /**
  * Abstraction for labels, separating a label from the underlying
@@ -39,19 +38,22 @@
     //and correct opcode selection. one per label as a label may be a
     //join point
     static final class Stack {
+        static final int NON_LOAD = -1;
+
         Type[] data = new Type[8];
+        int[]  localLoads = new int[8];
         int sp = 0;
 
         Stack() {
         }
 
-        private Stack(final Type[] type, final int sp) {
+        private Stack(final Stack original) {
             this();
-            this.data = new Type[type.length];
-            this.sp   = sp;
-            for (int i = 0; i < sp; i++) {
-                data[i] = type[i];
-            }
+            this.sp   = original.sp;
+            this.data = new Type[original.data.length];
+            System.arraycopy(original.data, 0, data, 0, sp);
+            this.localLoads = new int[original.localLoads.length];
+            System.arraycopy(original.localLoads, 0, localLoads, 0, sp);
         }
 
         boolean isEmpty() {
@@ -62,7 +64,7 @@
             return sp;
         }
 
-        boolean isEquivalentTo(final Stack other) {
+        boolean isEquivalentInTypesTo(final Stack other) {
             if (sp != other.sp) {
                 return false;
             }
@@ -81,12 +83,15 @@
         void push(final Type type) {
             if (data.length == sp) {
                 final Type[] newData = new Type[sp * 2];
-                for (int i = 0; i < sp; i++) {
-                    newData[i] = data[i];
-                }
+                final int[]  newLocalLoad = new int[sp * 2];
+                System.arraycopy(data, 0, newData, 0, sp);
+                System.arraycopy(localLoads, 0, newLocalLoad, 0, sp);
                 data = newData;
+                localLoads = newLocalLoad;
             }
-            data[sp++] = type;
+            data[sp] = type;
+            localLoads[sp] = NON_LOAD;
+            sp++;
         }
 
         Type peek() {
@@ -98,12 +103,67 @@
             return pos < 0 ? null : data[pos];
         }
 
+        /**
+         * Retrieve the top <tt>count</tt> types on the stack without modifying it.
+         *
+         * @param count number of types to return
+         * @return array of Types
+         */
+        Type[] getTopTypes(final int count) {
+            final Type[] topTypes = new Type[count];
+            System.arraycopy(data, sp - count, topTypes, 0, count);
+            return topTypes;
+        }
+
+        int[] getLocalLoads(final int from, final int to) {
+            final int count = to - from;
+            final int[] topLocalLoads = new int[count];
+            System.arraycopy(localLoads, from, topLocalLoads, 0, count);
+            return topLocalLoads;
+        }
+
+        /**
+         * When joining branches, local loads that differ on different branches are invalidated.
+         * @param other the stack from the other branch.
+         */
+        void mergeLocalLoads(final Stack other) {