8035820: Optimistic recompilation
authorattila
Wed, 26 Feb 2014 13:17:57 +0100
changeset 24719 f726e9d67629
parent 23083 8c74590d5df1
child 24720 75f8388b79df
8035820: Optimistic recompilation Reviewed-by: hannesw, jlaskey, sundar Contributed-by: attila.szegedi@oracle.com, marcus.lagergren@oracle.com
nashorn/.hgignore
nashorn/bin/rundiff.sh
nashorn/bin/runnormal.sh
nashorn/bin/runnormaldual.sh
nashorn/bin/runopt.sh
nashorn/bin/runoptdual.sh
nashorn/bin/runoptdualcatch.sh
nashorn/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MethodGenerator.java
nashorn/make/build.xml
nashorn/make/nbproject/ide-targets.xml
nashorn/make/project.properties
nashorn/src/jdk/internal/dynalink/DynamicLinker.java
nashorn/src/jdk/internal/dynalink/DynamicLinkerFactory.java
nashorn/src/jdk/internal/dynalink/GuardedInvocationFilter.java
nashorn/src/jdk/internal/dynalink/beans/AbstractJavaLinker.java
nashorn/src/jdk/internal/dynalink/beans/BeanLinker.java
nashorn/src/jdk/internal/dynalink/beans/OverloadedDynamicMethod.java
nashorn/src/jdk/internal/dynalink/beans/OverloadedMethod.java
nashorn/src/jdk/internal/dynalink/beans/SingleDynamicMethod.java
nashorn/src/jdk/internal/dynalink/linker/GuardedInvocation.java
nashorn/src/jdk/internal/dynalink/linker/GuardingDynamicLinker.java
nashorn/src/jdk/internal/dynalink/linker/LinkRequest.java
nashorn/src/jdk/internal/dynalink/linker/LinkerServices.java
nashorn/src/jdk/internal/dynalink/support/CatchExceptionCombinator.java
nashorn/src/jdk/internal/dynalink/support/DefaultPrelinkFilter.java
nashorn/src/jdk/internal/dynalink/support/LinkRequestImpl.java
nashorn/src/jdk/internal/dynalink/support/LinkerServicesImpl.java
nashorn/src/jdk/internal/dynalink/support/RuntimeContextLinkRequestImpl.java
nashorn/src/jdk/internal/dynalink/support/TypeUtilities.java
nashorn/src/jdk/nashorn/api/scripting/JSObject.java
nashorn/src/jdk/nashorn/api/scripting/NashornException.java
nashorn/src/jdk/nashorn/api/scripting/NashornScriptEngine.java
nashorn/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java
nashorn/src/jdk/nashorn/internal/IntDeque.java
nashorn/src/jdk/nashorn/internal/codegen/Attr.java
nashorn/src/jdk/nashorn/internal/codegen/BranchOptimizer.java
nashorn/src/jdk/nashorn/internal/codegen/ClassEmitter.java
nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java
nashorn/src/jdk/nashorn/internal/codegen/CodeGeneratorLexicalContext.java
nashorn/src/jdk/nashorn/internal/codegen/CompilationEnvironment.java
nashorn/src/jdk/nashorn/internal/codegen/CompilationPhase.java
nashorn/src/jdk/nashorn/internal/codegen/CompileUnit.java
nashorn/src/jdk/nashorn/internal/codegen/Compiler.java
nashorn/src/jdk/nashorn/internal/codegen/CompilerConstants.java
nashorn/src/jdk/nashorn/internal/codegen/Condition.java
nashorn/src/jdk/nashorn/internal/codegen/ConstantData.java
nashorn/src/jdk/nashorn/internal/codegen/DumpBytecode.java
nashorn/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java
nashorn/src/jdk/nashorn/internal/codegen/FinalizeTypes.java
nashorn/src/jdk/nashorn/internal/codegen/FoldConstants.java
nashorn/src/jdk/nashorn/internal/codegen/FunctionSignature.java
nashorn/src/jdk/nashorn/internal/codegen/Label.java
nashorn/src/jdk/nashorn/internal/codegen/LocalStateRestorationInfo.java
nashorn/src/jdk/nashorn/internal/codegen/Lower.java
nashorn/src/jdk/nashorn/internal/codegen/MapCreator.java
nashorn/src/jdk/nashorn/internal/codegen/MapTuple.java
nashorn/src/jdk/nashorn/internal/codegen/MethodEmitter.java
nashorn/src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java
nashorn/src/jdk/nashorn/internal/codegen/ObjectCreator.java
nashorn/src/jdk/nashorn/internal/codegen/ParamTypeMap.java
nashorn/src/jdk/nashorn/internal/codegen/ProgramPoints.java
nashorn/src/jdk/nashorn/internal/codegen/RangeAnalyzer.java
nashorn/src/jdk/nashorn/internal/codegen/RuntimeCallSite.java
nashorn/src/jdk/nashorn/internal/codegen/SharedScopeCall.java
nashorn/src/jdk/nashorn/internal/codegen/SpillObjectCreator.java
nashorn/src/jdk/nashorn/internal/codegen/Splitter.java
nashorn/src/jdk/nashorn/internal/codegen/WeighNodes.java
nashorn/src/jdk/nashorn/internal/codegen/types/BooleanType.java
nashorn/src/jdk/nashorn/internal/codegen/types/BytecodeNumericOps.java
nashorn/src/jdk/nashorn/internal/codegen/types/BytecodeOps.java
nashorn/src/jdk/nashorn/internal/codegen/types/IntType.java
nashorn/src/jdk/nashorn/internal/codegen/types/LongType.java
nashorn/src/jdk/nashorn/internal/codegen/types/NumberType.java
nashorn/src/jdk/nashorn/internal/codegen/types/ObjectType.java
nashorn/src/jdk/nashorn/internal/codegen/types/Type.java
nashorn/src/jdk/nashorn/internal/ir/AccessNode.java
nashorn/src/jdk/nashorn/internal/ir/BaseNode.java
nashorn/src/jdk/nashorn/internal/ir/BinaryNode.java
nashorn/src/jdk/nashorn/internal/ir/Block.java
nashorn/src/jdk/nashorn/internal/ir/BlockStatement.java
nashorn/src/jdk/nashorn/internal/ir/CallNode.java
nashorn/src/jdk/nashorn/internal/ir/CatchNode.java
nashorn/src/jdk/nashorn/internal/ir/Expression.java
nashorn/src/jdk/nashorn/internal/ir/FunctionCall.java
nashorn/src/jdk/nashorn/internal/ir/FunctionNode.java
nashorn/src/jdk/nashorn/internal/ir/IdentNode.java
nashorn/src/jdk/nashorn/internal/ir/IndexNode.java
nashorn/src/jdk/nashorn/internal/ir/LexicalContext.java
nashorn/src/jdk/nashorn/internal/ir/LiteralNode.java
nashorn/src/jdk/nashorn/internal/ir/Node.java
nashorn/src/jdk/nashorn/internal/ir/Optimistic.java
nashorn/src/jdk/nashorn/internal/ir/OptimisticLexicalContext.java
nashorn/src/jdk/nashorn/internal/ir/RuntimeNode.java
nashorn/src/jdk/nashorn/internal/ir/Symbol.java
nashorn/src/jdk/nashorn/internal/ir/UnaryNode.java
nashorn/src/jdk/nashorn/internal/ir/VarNode.java
nashorn/src/jdk/nashorn/internal/ir/debug/ASTWriter.java
nashorn/src/jdk/nashorn/internal/ir/debug/JSONWriter.java
nashorn/src/jdk/nashorn/internal/ir/debug/NashornClassReader.java
nashorn/src/jdk/nashorn/internal/ir/debug/NashornTextifier.java
nashorn/src/jdk/nashorn/internal/ir/debug/PrintVisitor.java
nashorn/src/jdk/nashorn/internal/lookup/Lookup.java
nashorn/src/jdk/nashorn/internal/lookup/MethodHandleFactory.java
nashorn/src/jdk/nashorn/internal/lookup/MethodHandleFunctionality.java
nashorn/src/jdk/nashorn/internal/objects/AccessorPropertyDescriptor.java
nashorn/src/jdk/nashorn/internal/objects/ArrayBufferView.java
nashorn/src/jdk/nashorn/internal/objects/DataPropertyDescriptor.java
nashorn/src/jdk/nashorn/internal/objects/GenericPropertyDescriptor.java
nashorn/src/jdk/nashorn/internal/objects/Global.java
nashorn/src/jdk/nashorn/internal/objects/NativeArray.java
nashorn/src/jdk/nashorn/internal/objects/NativeDebug.java
nashorn/src/jdk/nashorn/internal/objects/NativeFunction.java
nashorn/src/jdk/nashorn/internal/objects/NativeJSAdapter.java
nashorn/src/jdk/nashorn/internal/objects/NativeJavaImporter.java
nashorn/src/jdk/nashorn/internal/objects/NativeNumber.java
nashorn/src/jdk/nashorn/internal/objects/NativeObject.java
nashorn/src/jdk/nashorn/internal/objects/NativeRegExp.java
nashorn/src/jdk/nashorn/internal/objects/NativeStrictArguments.java
nashorn/src/jdk/nashorn/internal/objects/NativeString.java
nashorn/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java
nashorn/src/jdk/nashorn/internal/objects/annotations/Optimistic.java
nashorn/src/jdk/nashorn/internal/objects/annotations/Property.java
nashorn/src/jdk/nashorn/internal/parser/AbstractParser.java
nashorn/src/jdk/nashorn/internal/parser/JSONParser.java
nashorn/src/jdk/nashorn/internal/parser/Lexer.java
nashorn/src/jdk/nashorn/internal/parser/Parser.java
nashorn/src/jdk/nashorn/internal/runtime/AccessorProperty.java
nashorn/src/jdk/nashorn/internal/runtime/CompiledFunction.java
nashorn/src/jdk/nashorn/internal/runtime/CompiledFunctions.java
nashorn/src/jdk/nashorn/internal/runtime/Context.java
nashorn/src/jdk/nashorn/internal/runtime/Debug.java
nashorn/src/jdk/nashorn/internal/runtime/DebugLogger.java
nashorn/src/jdk/nashorn/internal/runtime/DebuggerSupport.java
nashorn/src/jdk/nashorn/internal/runtime/DefaultPropertyAccess.java
nashorn/src/jdk/nashorn/internal/runtime/ECMAErrors.java
nashorn/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java
nashorn/src/jdk/nashorn/internal/runtime/FindProperty.java
nashorn/src/jdk/nashorn/internal/runtime/FunctionScope.java
nashorn/src/jdk/nashorn/internal/runtime/JSONFunctions.java
nashorn/src/jdk/nashorn/internal/runtime/JSType.java
nashorn/src/jdk/nashorn/internal/runtime/NativeJavaPackage.java
nashorn/src/jdk/nashorn/internal/runtime/OptimisticReturnFilters.java
nashorn/src/jdk/nashorn/internal/runtime/Property.java
nashorn/src/jdk/nashorn/internal/runtime/PropertyAccess.java
nashorn/src/jdk/nashorn/internal/runtime/PropertyHashMap.java
nashorn/src/jdk/nashorn/internal/runtime/PropertyMap.java
nashorn/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java
nashorn/src/jdk/nashorn/internal/runtime/RewriteException.java
nashorn/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java
nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java
nashorn/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java
nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java
nashorn/src/jdk/nashorn/internal/runtime/ScriptRuntime.java
nashorn/src/jdk/nashorn/internal/runtime/SetMethodCreator.java
nashorn/src/jdk/nashorn/internal/runtime/SpillProperty.java
nashorn/src/jdk/nashorn/internal/runtime/Undefined.java
nashorn/src/jdk/nashorn/internal/runtime/UnwarrantedOptimismException.java
nashorn/src/jdk/nashorn/internal/runtime/UserAccessorProperty.java
nashorn/src/jdk/nashorn/internal/runtime/WithObject.java
nashorn/src/jdk/nashorn/internal/runtime/arrays/ArrayData.java
nashorn/src/jdk/nashorn/internal/runtime/arrays/ArrayFilter.java
nashorn/src/jdk/nashorn/internal/runtime/arrays/ContinuousArray.java
nashorn/src/jdk/nashorn/internal/runtime/arrays/IntArrayData.java
nashorn/src/jdk/nashorn/internal/runtime/arrays/LongArrayData.java
nashorn/src/jdk/nashorn/internal/runtime/arrays/NoTypeArrayData.java
nashorn/src/jdk/nashorn/internal/runtime/arrays/NumberArrayData.java
nashorn/src/jdk/nashorn/internal/runtime/arrays/ObjectArrayData.java
nashorn/src/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java
nashorn/src/jdk/nashorn/internal/runtime/arrays/UndefinedArrayFilter.java
nashorn/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java
nashorn/src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java
nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java
nashorn/src/jdk/nashorn/internal/runtime/linker/JavaSuperAdapter.java
nashorn/src/jdk/nashorn/internal/runtime/linker/JavaSuperAdapterLinker.java
nashorn/src/jdk/nashorn/internal/runtime/linker/LinkerCallSite.java
nashorn/src/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java
nashorn/src/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java
nashorn/src/jdk/nashorn/internal/runtime/linker/NashornCallSiteDescriptor.java
nashorn/src/jdk/nashorn/internal/runtime/linker/NashornGuards.java
nashorn/src/jdk/nashorn/internal/runtime/linker/NashornLinker.java
nashorn/src/jdk/nashorn/internal/runtime/linker/NashornPrimitiveLinker.java
nashorn/src/jdk/nashorn/internal/runtime/resources/Messages.properties
nashorn/src/jdk/nashorn/internal/runtime/resources/Options.properties
nashorn/src/jdk/nashorn/internal/scripts/JO.java
nashorn/src/jdk/nashorn/tools/Shell.java
nashorn/test/script/assert.js
nashorn/test/script/basic/JDK-8025515.js
nashorn/test/script/basic/JDK-8029384.js
nashorn/test/script/basic/JDK-8029384.js.EXPECTED
nashorn/test/script/basic/JDK-8029467.js
nashorn/test/script/basic/JDK-8029667.js
nashorn/test/script/basic/exprclosure.js.EXPECTED
nashorn/test/script/basic/hideLocationProperties.js
nashorn/test/script/basic/hideLocationProperties.js.EXPECTED
nashorn/test/script/basic/relinkIndexGetter.js
nashorn/test/script/basic/relinkIndexGetter.js.EXPECTED
nashorn/test/script/basic/run-octane.js
nashorn/test/script/basic/runsunspider-lazy.js
nashorn/test/script/basic/runsunspider.js
nashorn/test/script/basic/typedarrays2.js
nashorn/test/script/basic/typedarrays2.js.EXPECTED
nashorn/test/script/currently-failing/property_delete.js
nashorn/test/script/maptests/constructor.js
nashorn/test/script/maptests/maputil.js
nashorn/test/script/maptests/object_literals.js
nashorn/test/script/maptests/point.js
nashorn/test/script/maptests/property_add.js
nashorn/test/script/maptests/property_delete.js
nashorn/test/script/trusted/JDK-8006529.js
nashorn/test/src/jdk/nashorn/api/javaaccess/NumberAccessTest.java
nashorn/test/src/jdk/nashorn/api/javaaccess/NumberBoxingTest.java
nashorn/test/src/jdk/nashorn/api/javaaccess/SharedObject.java
nashorn/test/src/jdk/nashorn/internal/codegen/CompilerTest.java
nashorn/test/src/jdk/nashorn/internal/parser/ParserTest.java
nashorn/test/src/jdk/nashorn/internal/test/framework/ScriptRunnable.java
--- a/nashorn/.hgignore	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/.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/nashorn/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/nashorn/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/nashorn/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/nashorn/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/nashorn/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/nashorn/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/nashorn/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MethodGenerator.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/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/nashorn/make/build.xml	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/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/nashorn/make/nbproject/ide-targets.xml	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/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/nashorn/make/project.properties	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/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/nashorn/src/jdk/internal/dynalink/DynamicLinker.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/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/nashorn/src/jdk/internal/dynalink/DynamicLinkerFactory.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/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/nashorn/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/nashorn/src/jdk/internal/dynalink/beans/AbstractJavaLinker.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/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/nashorn/src/jdk/internal/dynalink/beans/BeanLinker.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/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/nashorn/src/jdk/internal/dynalink/beans/OverloadedDynamicMethod.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/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/nashorn/src/jdk/internal/dynalink/beans/OverloadedMethod.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/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/nashorn/src/jdk/internal/dynalink/beans/SingleDynamicMethod.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/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/nashorn/src/jdk/internal/dynalink/linker/GuardedInvocation.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/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/nashorn/src/jdk/internal/dynalink/linker/GuardingDynamicLinker.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/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/nashorn/src/jdk/internal/dynalink/linker/LinkRequest.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/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/nashorn/src/jdk/internal/dynalink/linker/LinkerServices.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/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/nashorn/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/nashorn/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/nashorn/src/jdk/internal/dynalink/support/LinkRequestImpl.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/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/nashorn/src/jdk/internal/dynalink/support/LinkerServicesImpl.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/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/nashorn/src/jdk/internal/dynalink/support/RuntimeContextLinkRequestImpl.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/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/nashorn/src/jdk/internal/dynalink/support/TypeUtilities.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/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;
+        }
+        if(c1 == void.class) {
+            return c2;
+        } else if(c2 == void.class) {
+            return c1;
         }
-        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.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;
+            }
         }
-        Set<Class<?>> a1 = getAssignables(c1, c3);
-        Set<Class<?>> a2 = getAssignables(c3, c1);
+        // 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(targetType.isPrimitive()) {
+            final Class<?> unboxedCallSiteType = PRIMITIVE_TYPES.get(sourceType);
+            return unboxedCallSiteType != null
+                    && (unboxedCallSiteType == targetType || isProperPrimitiveSubtype(unboxedCallSiteType, targetType));
         }
-        if(methodType.isPrimitive()) {
-            final Class<?> unboxedCallSiteType = PRIMITIVE_TYPES.get(callSiteType);
+        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 == methodType || isProperPrimitiveSubtype(unboxedCallSiteType, methodType));
+                    && (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/nashorn/src/jdk/nashorn/api/scripting/JSObject.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/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/nashorn/src/jdk/nashorn/api/scripting/NashornException.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/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/nashorn/src/jdk/nashorn/api/scripting/NashornScriptEngine.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/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/nashorn/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/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/nashorn/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/nashorn/src/jdk/nashorn/internal/codegen/Attr.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/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/nashorn/src/jdk/nashorn/internal/codegen/BranchOptimizer.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/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/nashorn/src/jdk/nashorn/internal/codegen/ClassEmitter.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/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/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/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,33 +284,80 @@
             return method.load(symbol).convert(type);
         }
 
-        final String name   = symbol.getName();
-        final Source source = lc.getCurrentFunction().getSource();
-
-        if (CompilerConstants.__FILE__.name().equals(name)) {
-            return method.load(source.getName());
-        } else if (CompilerConstants.__DIR__.name().equals(name)) {
-            return method.load(source.getBase());
-        } else if (CompilerConstants.__LINE__.name().equals(name)) {
-            return method.load(source.getLine(identNode.position())).convert(Type.OBJECT);
+        // 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 {
-            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);
+            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);
+                    }
                 }
-                return loadFastScopeVar(type, symbol, flags, identNode.isFunction());
-            }
-            return method.dynamicGet(type, identNode.getName(), flags, identNode.isFunction());
+            }.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)) {
+            replaceCompileTimeProperty(identNode, type, getCurrentSource().getName());
+        } else if (CompilerConstants.__DIR__.name().equals(name)) {
+            replaceCompileTimeProperty(identNode, type, getCurrentSource().getBase());
+        } else if (CompilerConstants.__LINE__.name().equals(name)) {
+            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 {
+            throw new AssertionError();
+        }
+        globalReplaceLocationPropertyPlaceholder();
+        convertOptimisticReturnValue(identNode, type);
+    }
+
+    private boolean isOptimisticOrRestOf() {
+        return useOptimisticTypes() || compiler.getCompilationEnvironment().isCompileRestOf();
+    }
+
+    /**
      * Check if this symbol can be accessed directly with a putfield or getfield or dynamic load
      *
      * @param symbol symbol to check for fast scope
@@ -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 */
-        method.loadCompilerConstant(SCOPE);
-        method.invoke(ScriptObject.GET_PROTO);
-        method.storeCompilerConstant(SCOPE);
-        method._goto(skipLabel);
-        method.label(exitLabel);
+        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.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;
-            }
-
-            method.label(falseLabel);
-            load(rhs);
-            method.invokestatic(CompilerConstants.className(ScriptRuntime.class), request.toString(), signature);
-            method._goto(endLabel);
-
-            method.label(trueLabel);
-            // if NE (not strict) this can be "undefined != null" which is supposed to be false
-            if (request == Request.NE) {
+        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);
-                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(trueLabel);
+            }
+            method.label(falseLabel);
+            method.load(false);
+            method._goto(endLabel);
+            if (!Request.isStrict(request)) {
+                method.label(popLabel);
                 method.pop();
-                method.load(true);
             }
+            method.label(trueLabel);
+            method.load(true);
             method.label(endLabel);
-            method.convert(runtimeNode.getType());
-            method.store(runtimeNode.getSymbol());
-
-            return true;
+        } else if (Request.isNE(request)) {
+            method.ifnull(!Request.isStrict(request) ? popLabel : falseLabel);
+            if (!Request.isStrict(request)) {
+                method.loadUndefined(Type.OBJECT);
+                method.if_acmpeq(falseLabel);
+            }
+            method.label(trueLabel);
+            method.load(true);
+            method._goto(endLabel);
+            if (!Request.isStrict(request)) {
+                method.label(popLabel);
+                method.pop();
+            }
+            method.label(falseLabel);
+            method.load(false);
+            method.label(endLabel);
         }
 
-        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();
 
-        final Label tryLabel;
         if (hasScope) {
-            tryLabel = new Label("with_try");
-            method.label(tryLabel);
             method.loadCompilerConstant(SCOPE);
-        } else {
-            tryLabel = null;
         }
 
         load(expression, Type.OBJECT);
 
+        final Label tryLabel;
         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/nashorn/src/jdk/nashorn/internal/codegen/CodeGeneratorLexicalContext.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/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/nashorn/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/nashorn/src/jdk/nashorn/internal/codegen/CompilationPhase.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/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/nashorn/src/jdk/nashorn/internal/codegen/CompileUnit.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/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/nashorn/src/jdk/nashorn/internal/codegen/Compiler.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/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));
-        }
-
-        CompilationSequence(final CompilationSequence sequence) {
-            this(sequence.toArray(new CompilationPhase[sequence.size()]));
+        if (functionNode.isStrict()) {
+            compilationEnv.setIsStrict(true);
         }
 
-        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;
-        }
+        final StringBuilder sb = new StringBuilder();
+        sb.append(functionNode.uniqueName(className)).
+                append('$').
+                append(safeSourceName(functionNode.getSource()));
+        this.scriptName = sb.toString();
+    }
 
-        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
-     */
-    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
+     * ScriptEnvironment constructor for compiler. Used only from Shell and --compile-only flag
+     * No code installer supplied
+     * @param scriptEnv script environment
      */
-    //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/nashorn/src/jdk/nashorn/internal/codegen/CompilerConstants.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/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/nashorn/src/jdk/nashorn/internal/codegen/Condition.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/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/nashorn/src/jdk/nashorn/internal/codegen/ConstantData.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/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/nashorn/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/nashorn/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/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/nashorn/src/jdk/nashorn/internal/codegen/FinalizeTypes.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/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/nashorn/src/jdk/nashorn/internal/codegen/FoldConstants.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/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/nashorn/src/jdk/nashorn/internal/codegen/FunctionSignature.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/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/nashorn/src/jdk/nashorn/internal/codegen/Label.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/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) {
+            final int[] otherLoads = other.localLoads;
+            for(int i = 0; i < sp; ++i) {
+                if(localLoads[i] != otherLoads[i]) {
+                    localLoads[i] = NON_LOAD;
+                }
+            }
+        }
+
         Type pop() {
             return data[--sp];
         }
 
         Stack copy() {
-            return new Stack(data, sp);
+            return new Stack(this);
+        }
+
+        int getTopLocalLoad() {
+            return localLoads[sp - 1];
+        }
+
+        void markLocalLoad(final int slot) {
+            localLoads[sp - 1] = slot;
+        }
+
+        /**
+         * If we store a value in a local slot, it invalidates any on-stack loads from that same slot, as the values
+         * could have changed.
+         * @param slot the slot written to
+         * @param slotCount the size of the value, either 1 or 2 slots
+         */
+        void markLocalStore(final int slot, final int slotCount) {
+            for(int i = 0; i < sp; ++i) {
+                final int load = localLoads[i];
+                if(load == slot || load == slot + slotCount - 1) {
+                    localLoads[i] = NON_LOAD;
+                }
+            }
         }
 
         @Override
@@ -128,6 +188,12 @@
     /** ASM representation of this label */
     private jdk.internal.org.objectweb.asm.Label label;
 
+    /** Id for debugging purposes, remove if footprint becomes unmanageable */
+    private final int id;
+
+    /** Next id for debugging purposes, remove if footprint becomes unmanageable */
+    private static int nextId = 0;
+
     /**
      * Constructor
      *
@@ -136,6 +202,7 @@
     public Label(final String name) {
         super();
         this.name = name;
+        this.id   = nextId++;
     }
 
     /**
@@ -146,6 +213,7 @@
     public Label(final Label label) {
         super();
         this.name = label.name;
+        this.id   = label.id;
     }
 
 
@@ -166,6 +234,6 @@
 
     @Override
     public String toString() {
-        return name + '_' + Debug.id(this);
+        return name + '_' + id;
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/codegen/LocalStateRestorationInfo.java	Wed Feb 26 13:17:57 2014 +0100
@@ -0,0 +1,59 @@
+/*
+ * 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 jdk.nashorn.internal.codegen.types.Type;
+
+/**
+ * Encapsulates the information for restoring the local state when continuing execution after a rewrite triggered by
+ * an optimistic assumption failure. An instance of this class is specific to a program point.
+ *
+ */
+public class LocalStateRestorationInfo {
+    private final Type[] localVariableTypes;
+    private final int[] stackLoads;
+
+    LocalStateRestorationInfo(Type[] localVariableTypes, final int[] stackLoads) {
+        this.localVariableTypes = localVariableTypes;
+        this.stackLoads = stackLoads;
+    }
+
+    /**
+     * Returns the types of the local variables at the continuation of a program point.
+     * @return the types of the local variables at the continuation of a program point.
+     */
+    public Type[] getLocalVariableTypes() {
+        return localVariableTypes.clone();
+    }
+
+    /**
+     * Returns the indices of local variables that need to be loaded on stack before jumping to the continuation of the
+     * program point.
+     * @return the indices of local variables that need to be loaded on stack.
+     */
+    public int[] getStackLoads() {
+        return stackLoads.clone();
+    }
+}
--- a/nashorn/src/jdk/nashorn/internal/codegen/Lower.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Lower.java	Wed Feb 26 13:17:57 2014 +0100
@@ -31,6 +31,7 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 import java.util.ListIterator;
 import jdk.nashorn.internal.ir.BaseNode;
@@ -235,12 +236,18 @@
             newForNode = forNode.setTest(lc, null);
         }
 
-        return addStatement(checkEscape(newForNode));
-    }
-
-    @Override
-    public boolean enterFunctionNode(final FunctionNode functionNode) {
-        return !functionNode.isLazy();
+        newForNode = checkEscape(newForNode);
+        if(newForNode.isForIn()) {
+            // Wrap it in a block so its internally created iterator is restricted in scope
+            BlockStatement b = BlockStatement.createReplacement(newForNode, Collections.singletonList((Statement)newForNode));
+            if(newForNode.isTerminal()) {
+                b = b.setBlock(b.getBlock().setIsTerminal(null, true));
+            }
+            addStatement(b);
+        } else {
+            addStatement(newForNode);
+        }
+        return newForNode;
     }
 
     @Override
@@ -308,7 +315,7 @@
         final long token      = tryNode.getToken();
         final int  finish     = tryNode.getFinish();
 
-        final IdentNode exception = new IdentNode(token, finish, lc.getCurrentFunction().uniqueName("catch_all"));
+        final IdentNode exception = new IdentNode(token, finish, lc.getCurrentFunction().uniqueName(CompilerConstants.EXCEPTION_PREFIX.symbolName()));
 
         final Block catchBody = new Block(token, finish, new ThrowNode(lineNumber, token, finish, new IdentNode(exception), ThrowNode.IS_SYNTHETIC_RETHROW));
         assert catchBody.isTerminal(); //ends with throw, so terminal
@@ -340,6 +347,8 @@
     private Node spliceFinally(final TryNode tryNode, final List<ThrowNode> rethrows, final Block finallyBody) {
         assert tryNode.getFinallyBody() == null;
 
+        final LexicalContext lowerLc = lc;
+
         final TryNode newTryNode = (TryNode)tryNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
             final List<Node> insideTry = new ArrayList<>();
 
@@ -388,6 +397,7 @@
                     //still in the try block, store it in a result value and return it afterwards
                     resultNode = new IdentNode(Lower.this.compilerConstant(RETURN));
                     newStatements.add(new ExpressionStatement(returnNode.getLineNumber(), returnNode.getToken(), returnNode.getFinish(), new BinaryNode(Token.recast(returnNode.getToken(), TokenType.ASSIGN), resultNode, expr)));
+                    lowerLc.setFlag(lowerLc.getCurrentFunction(), FunctionNode.USES_RETURN_SYMBOL);
                 } else {
                     resultNode = null;
                 }
@@ -620,10 +630,11 @@
         return !escapes.isEmpty();
     }
 
-    private LoopNode checkEscape(final LoopNode loopNode) {
+    @SuppressWarnings("unchecked")
+    private <T extends LoopNode> T checkEscape(final T loopNode) {
         final boolean escapes = controlFlowEscapes(lc, loopNode.getBody());
         if (escapes) {
-            return loopNode.
+            return (T)loopNode.
                 setBody(lc, loopNode.getBody().setIsTerminal(lc, false)).
                 setControlFlowEscapes(lc, escapes);
         }
--- a/nashorn/src/jdk/nashorn/internal/codegen/MapCreator.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/MapCreator.java	Wed Feb 26 13:17:57 2014 +0100
@@ -34,19 +34,19 @@
 import jdk.nashorn.internal.runtime.AccessorProperty;
 import jdk.nashorn.internal.runtime.Property;
 import jdk.nashorn.internal.runtime.PropertyMap;
+import jdk.nashorn.internal.runtime.ScriptObject;
+import jdk.nashorn.internal.runtime.SpillProperty;
 
 /**
  * Class that creates PropertyMap sent to script object constructors.
+ * @param <T> value type for tuples, e.g. Symbol
  */
-public class MapCreator {
+public class MapCreator<T> {
     /** Object structure for objects associated with this map */
     private final Class<?> structure;
 
     /** key set for object map */
-    final List<String> keys;
-
-    /** corresponding symbol set for object map */
-    final List<Symbol> symbols;
+    private final List<MapTuple<T>> tuples;
 
     /**
      * Constructor
@@ -55,10 +55,9 @@
      * @param keys      list of keys for map
      * @param symbols   list of symbols for map
      */
-    MapCreator(final Class<?> structure, final List<String> keys, final List<Symbol> symbols) {
+    MapCreator(final Class<? extends ScriptObject> structure, final List<MapTuple<T>> tuples) {
         this.structure = structure;
-        this.keys      = keys;
-        this.symbols   = symbols;
+        this.tuples    = tuples;
     }
 
     /**
@@ -72,14 +71,14 @@
      */
     PropertyMap makeFieldMap(final boolean hasArguments, final int fieldCount, final int fieldMaximum) {
         final List<Property> properties = new ArrayList<>();
-        assert keys != null;
+        assert tuples != null;
 
-        for (int i = 0, length = keys.size(); i < length; i++) {
-            final String key    = keys.get(i);
-            final Symbol symbol = symbols.get(i);
-
+        for (final MapTuple<T> tuple : tuples) {
+            final String   key         = tuple.key;
+            final Symbol   symbol      = tuple.symbol;
+            final Class<?> initialType = tuple.getValueType();
             if (symbol != null && !isValidArrayIndex(getArrayIndex(key))) {
-                properties.add(new AccessorProperty(key, getPropertyFlags(symbol, hasArguments), structure, symbol.getFieldIndex()));
+                properties.add(new AccessorProperty(key, getPropertyFlags(symbol, hasArguments), structure, symbol.getFieldIndex(), initialType));
             }
         }
 
@@ -89,14 +88,14 @@
     PropertyMap makeSpillMap(final boolean hasArguments) {
         final List<Property> properties = new ArrayList<>();
         int spillIndex = 0;
-        assert keys != null;
+        assert tuples != null;
 
-        for (int i = 0, length = keys.size(); i < length; i++) {
-            final String key    = keys.get(i);
-            final Symbol symbol = symbols.get(i);
+        for (final MapTuple<T> tuple : tuples) {
+            final String key    = tuple.key;
+            final Symbol symbol = tuple.symbol;
 
             if (symbol != null && !isValidArrayIndex(getArrayIndex(key))) {
-                properties.add(new AccessorProperty(key, getPropertyFlags(symbol, hasArguments), spillIndex++));
+                properties.add(new SpillProperty(key, getPropertyFlags(symbol, hasArguments), spillIndex++));
             }
         }
 
@@ -119,17 +118,13 @@
         }
 
         if (hasArguments) {
-            flags |= Property.IS_ALWAYS_OBJECT | Property.HAS_ARGUMENTS;
+            flags |= Property.HAS_ARGUMENTS;
         }
 
         if (symbol.isScope()) {
             flags |= Property.NOT_CONFIGURABLE;
         }
 
-        if (symbol.canBePrimitive()) {
-            flags |= Property.CAN_BE_PRIMITIVE;
-        }
-
         if (symbol.canBeUndefined()) {
             flags |= Property.CAN_BE_UNDEFINED;
         }
@@ -140,5 +135,4 @@
 
         return flags;
     }
-
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/codegen/MapTuple.java	Wed Feb 26 13:17:57 2014 +0100
@@ -0,0 +1,62 @@
+/*
+ * 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.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY;
+import jdk.nashorn.internal.ir.Symbol;
+
+/**
+ * A tuple of values used for map creation
+ * @param <T> value type
+ */
+class MapTuple<T> {
+    final String key;
+    final Symbol symbol;
+    final T      value;
+
+    MapTuple(final String key, final Symbol symbol) {
+        this(key, symbol, null);
+    }
+
+    MapTuple(final String key, final Symbol symbol, final T value) {
+        this.key    = key;
+        this.symbol = symbol;
+        this.value  = value;
+    }
+
+    public Class<?> getValueType() {
+        return OBJECT_FIELDS_ONLY ? Object.class : null; //until proven otherwise we are undefined, see NASHORN-592 int.class;
+    }
+
+    boolean isPrimitive() {
+        return !OBJECT_FIELDS_ONLY && getValueType().isPrimitive() && getValueType() != boolean.class;
+    }
+
+    @Override
+    public String toString() {
+        return "[key=" + key + ", symbol=" + symbol + ", value=" + value + " (" + (value == null ? "null" : value.getClass().getSimpleName()) +")]";
+    }
+}
--- a/nashorn/src/jdk/nashorn/internal/codegen/MethodEmitter.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/MethodEmitter.java	Wed Feb 26 13:17:57 2014 +0100
@@ -64,9 +64,14 @@
 import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor;
 import static jdk.nashorn.internal.codegen.CompilerConstants.staticField;
 import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
+import static jdk.nashorn.internal.codegen.ObjectClassGenerator.PRIMITIVE_FIELD_TYPE;
+import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_OPTIMISTIC;
+import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_PROGRAM_POINT_SHIFT;
 
 import java.io.PrintStream;
 import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.EnumSet;
 import java.util.List;
 import jdk.internal.dynalink.support.NameCodec;
@@ -89,6 +94,7 @@
 import jdk.nashorn.internal.runtime.Debug;
 import jdk.nashorn.internal.runtime.DebugLogger;
 import jdk.nashorn.internal.runtime.JSType;
+import jdk.nashorn.internal.runtime.RewriteException;
 import jdk.nashorn.internal.runtime.ScriptEnvironment;
 import jdk.nashorn.internal.runtime.ScriptObject;
 import jdk.nashorn.internal.runtime.linker.Bootstrap;
@@ -126,6 +132,8 @@
     /** The script environment */
     private final ScriptEnvironment env;
 
+    private final List<Type> localVariableTypes = new ArrayList<>();
+
     /** Threshold in chars for when string constants should be split */
     static final int LARGE_STRING_THRESHOLD = 32 * 1024;
 
@@ -153,6 +161,9 @@
     /** Bootstrap for runtime node indy:s */
     private static final Handle RUNTIMEBOOTSTRAP = new Handle(H_INVOKESTATIC, RuntimeCallSite.BOOTSTRAP.className(), RuntimeCallSite.BOOTSTRAP.name(), RuntimeCallSite.BOOTSTRAP.descriptor());
 
+    /** Bootstrap for array populators */
+    private static final Handle POPULATE_ARRAY_BOOTSTRAP = new Handle(H_INVOKESTATIC, RewriteException.BOOTSTRAP.className(), RewriteException.BOOTSTRAP.name(), RewriteException.BOOTSTRAP.descriptor());
+
     /**
      * Constructor - internal use from ClassEmitter only
      * @see ClassEmitter#method
@@ -203,6 +214,11 @@
         classEmitter.endMethod(this);
     }
 
+    void createNewStack() {
+        assert stack == null;
+        newStack();
+    }
+
     private void newStack() {
         stack = new Label.Stack();
     }
@@ -216,7 +232,7 @@
      * Push a type to the existing stack
      * @param type the type
      */
-    private void pushType(final Type type) {
+    void pushType(final Type type) {
         if (type != null) {
             stack.push(type);
         }
@@ -230,7 +246,7 @@
      * @return the type that was retrieved
      */
     private Type popType(final Type expected) {
-        final Type type = stack.pop();
+        final Type type = popType();
         assert type.isObject() && expected.isObject() ||
             type.isEquivalentTo(expected) : type + " is not compatible with " + expected;
         return type;
@@ -252,7 +268,7 @@
      * @return the type
      */
     private NumericType popNumeric() {
-        final Type type = stack.pop();
+        final Type type = popType();
         assert type.isNumeric() : type + " is not numeric";
         return (NumericType)type;
     }
@@ -264,7 +280,7 @@
      * @return the type
      */
     private BitwiseType popInteger() {
-        final Type type = stack.pop();
+        final Type type = popType();
         assert type.isInteger() || type.isLong() : type + " is not an integer or long";
         return (BitwiseType)type;
     }
@@ -276,7 +292,7 @@
      * @return the type
      */
     private ArrayType popArray() {
-        final Type type = stack.pop();
+        final Type type = popType();
         assert type.isArray() : type;
         return (ArrayType)type;
     }
@@ -307,13 +323,14 @@
      * object type on the stack
      *
      * @param classDescriptor class descriptor for the object type
+     * @param type the type of the new object
      *
      * @return the method emitter
      */
-    MethodEmitter _new(final String classDescriptor) {
+    MethodEmitter _new(final String classDescriptor, final Type type) {
         debug("new", classDescriptor);
         method.visitTypeInsn(NEW, classDescriptor);
-        pushType(Type.OBJECT);
+        pushType(type);
         return this;
     }
 
@@ -326,7 +343,7 @@
      * @return the method emitter
      */
     MethodEmitter _new(final Class<?> clazz) {
-        return _new(className(clazz));
+        return _new(className(clazz), Type.typeFor(clazz));
     }
 
     /**
@@ -358,25 +375,40 @@
         debug("dup", depth);
 
         switch (depth) {
-        case 0:
+        case 0: {
+            final int l0 = stack.getTopLocalLoad();
             pushType(peekType());
+            stack.markLocalLoad(l0);
             break;
+        }
         case 1: {
+            final int l0 = stack.getTopLocalLoad();
             final Type p0 = popType();
+            final int l1 = stack.getTopLocalLoad();
             final Type p1 = popType();
             pushType(p0);
+            stack.markLocalLoad(l0);
             pushType(p1);
+            stack.markLocalLoad(l1);
             pushType(p0);
+            stack.markLocalLoad(l0);
             break;
         }
         case 2: {
+            final int l0 = stack.getTopLocalLoad();
             final Type p0 = popType();
+            final int l1 = stack.getTopLocalLoad();
             final Type p1 = popType();
+            final int l2 = stack.getTopLocalLoad();
             final Type p2 = popType();
             pushType(p0);
+            stack.markLocalLoad(l0);
             pushType(p2);
+            stack.markLocalLoad(l2);
             pushType(p1);
+            stack.markLocalLoad(l1);
             pushType(p0);
+            stack.markLocalLoad(l0);
             break;
         }
         default:
@@ -398,13 +430,22 @@
         debug("dup2");
 
         if (peekType().isCategory2()) {
+            final int l0 = stack.getTopLocalLoad();
             pushType(peekType());
+            stack.markLocalLoad(l0);
         } else {
-            final Type type = get2();
-            pushType(type);
-            pushType(type);
-            pushType(type);
-            pushType(type);
+            final int l0 = stack.getTopLocalLoad();
+            final Type p0 = popType();
+            final int l1 = stack.getTopLocalLoad();
+            final Type p1 = popType();
+            pushType(p0);
+            stack.markLocalLoad(l0);
+            pushType(p1);
+            stack.markLocalLoad(l1);
+            pushType(p0);
+            stack.markLocalLoad(l0);
+            pushType(p1);
+            stack.markLocalLoad(l1);
         }
         method.visitInsn(DUP2);
         return this;
@@ -454,16 +495,34 @@
     MethodEmitter swap() {
         debug("swap");
 
+        final int l0 = stack.getTopLocalLoad();
         final Type p0 = popType();
+        final int l1 = stack.getTopLocalLoad();
         final Type p1 = popType();
         p0.swap(method, p1);
 
         pushType(p0);
+        stack.markLocalLoad(l0);
         pushType(p1);
+        stack.markLocalLoad(l1);
         debug("after ", p0, p1);
         return this;
     }
 
+    void pack() {
+        final Type type = peekType();
+        if (type.isInteger()) {
+            convert(PRIMITIVE_FIELD_TYPE);
+        } else if (type.isLong()) {
+            //nop
+        } else if (type.isNumber()) {
+            invokestatic("java/lang/Double", "doubleToRawLongBits", "(D)J");
+        } else {
+            assert false : type + " cannot be packed!";
+        }
+        //all others are nops, objects aren't packed
+    }
+
     /**
      * Add a local variable. This is a nop if the symbol has no slot
      *
@@ -586,9 +645,9 @@
      *
      * @return the method emitter
      */
-    MethodEmitter neg() {
+    MethodEmitter neg(final int programPoint) {
         debug("neg");
-        pushType(popNumeric().neg(method));
+        pushType(popNumeric().neg(method, programPoint));
         return this;
     }
 
@@ -599,9 +658,23 @@
      * @param recovery label pointing to start of catch block
      */
     void _catch(final Label recovery) {
+        label(recovery);
         stack.clear();
-        stack.push(Type.OBJECT);
-        label(recovery);
+        pushType(Type.typeFor(Throwable.class));
+    }
+
+    /**
+     * Add any number of labels for the start of a catch block and push the exception to the
+     * stack
+     *
+     * @param recoveries labels pointing to start of catch block
+     */
+    void _catch(final Collection<Label> recoveries) {
+        for(final Label l: recoveries) {
+            label(l);
+        }
+        stack.clear();
+        pushType(Type.OBJECT);
     }
 
     /**
@@ -665,6 +738,12 @@
         return this;
     }
 
+    MethodEmitter loadForcedInitializer(final Type type) {
+        debug("load forced initializer ", type);
+        pushType(type.loadForcedInitializer(method));
+        return this;
+    }
+
     /**
      * Push the empty value for the given type, i.e. EMPTY.
      *
@@ -816,9 +895,10 @@
         assert symbol != null;
         if (symbol.hasSlot()) {
             final int slot = symbol.getSlot();
-            debug("load symbol", symbol.getName(), " slot=", slot);
-            final Type type = symbol.getSymbolType().load(method, slot);
-            pushType(type == Type.OBJECT && symbol.isThis() ? Type.THIS : type);
+            debug("load symbol", symbol.getName(), " slot=", slot, "type=", symbol.getSymbolType());
+            load(symbol.getSymbolType(), slot);
+           // _try(new Label("dummy"), new Label("dummy2"), recovery);
+           // method.visitTryCatchBlock(new Label(), arg1, arg2, arg3);
         } else if (symbol.isParam()) {
             assert !symbol.isScope();
             assert functionNode.isVarArg() : "Non-vararg functions have slotted parameters";
@@ -853,7 +933,9 @@
     MethodEmitter load(final Type type, final int slot) {
         debug("explicit load", type, slot);
         final Type loadType = type.load(method, slot);
+        assert loadType != null;
         pushType(loadType == Type.OBJECT && isThisSlot(slot) ? Type.THIS : loadType);
+        stack.markLocalLoad(slot);
         return this;
     }
 
@@ -949,7 +1031,7 @@
         if (symbol.hasSlot()) {
             final int slot = symbol.getSlot();
             debug("store symbol", symbol.getName(), " slot=", slot);
-            popType(symbol.getSymbolType()).store(method, slot);
+            store(symbol.getSymbolType(), slot);
         } else if (symbol.isParam()) {
             assert !symbol.isScope();
             assert functionNode.isVarArg() : "Non-vararg functions have slotted parameters";
@@ -977,8 +1059,37 @@
      * @param slot the slot
      */
     void store(final Type type, final int slot) {
+        debug("explicit store", type, slot);
         popType(type);
         type.store(method, slot);
+        // TODO: disable this when not running with optimistic types?
+        final int slotCount = type.getSlots();
+        ensureLocalVariableCount(slot + slotCount);
+        localVariableTypes.set(slot, type);
+        if(slotCount == 2) {
+            localVariableTypes.set(slot + 1, Type.SLOT_2);
+        }
+        stack.markLocalStore(slot, slotCount);
+    }
+
+    void ensureLocalVariableCount(final int slotCount) {
+        while(localVariableTypes.size() < slotCount) {
+            localVariableTypes.add(Type.UNKNOWN);
+        }
+    }
+
+    List<Type> getLocalVariableTypes() {
+        return localVariableTypes;
+    }
+
+    void setParameterTypes(final Type... paramTypes) {
+        assert localVariableTypes.isEmpty();
+        for(final Type type: paramTypes) {
+            localVariableTypes.add(type);
+            if(type.isCategory2()) {
+                localVariableTypes.add(Type.SLOT_2);
+            }
+        }
     }
 
     /**
@@ -999,7 +1110,7 @@
     public void athrow() {
         debug("athrow");
         final Type receiver = popType(Type.OBJECT);
-        assert receiver.isObject();
+        assert Throwable.class.isAssignableFrom(receiver.getTypeClass()) : receiver.getTypeClass();
         method.visitInsn(ATHROW);
         stack = null;
     }
@@ -1130,11 +1241,7 @@
             popType(Type.OBJECT);
         }
 
-        if (opcode == INVOKEINTERFACE) {
-            method.visitMethodInsn(opcode, className, methodName, methodDescriptor, true);
-        } else {
-            method.visitMethodInsn(opcode, className, methodName, methodDescriptor, false);
-        }
+        method.visitMethodInsn(opcode, className, methodName, methodDescriptor, opcode == INVOKEINTERFACE);
 
         if (returnType != null) {
             pushType(returnType);
@@ -1197,7 +1304,7 @@
      *
      * @return the method emitter
      */
-    MethodEmitter invokeStatic(final String className, final String methodName, final String methodDescriptor, final Type returnType) {
+    MethodEmitter invokestatic(final String className, final String methodName, final String methodDescriptor, final Type returnType) {
         invokestatic(className, methodName, methodDescriptor);
         popType();
         pushType(returnType);
@@ -1235,8 +1342,9 @@
      */
     void lookupswitch(final Label defaultLabel, final int[] values, final Label... table) {//Collection<Label> table) {
         debug("lookupswitch", peekType());
-        popType(Type.INT);
+        adjustStackForSwitch(defaultLabel, table);
         method.visitLookupSwitchInsn(defaultLabel.getLabel(), values, getLabels(table));
+        stack = null; //whoever reaches the point after us provides the stack, because we don't
     }
 
     /**
@@ -1248,8 +1356,17 @@
      */
     void tableswitch(final int lo, final int hi, final Label defaultLabel, final Label... table) {
         debug("tableswitch", peekType());
+        adjustStackForSwitch(defaultLabel, table);
+        method.visitTableSwitchInsn(lo, hi, defaultLabel.getLabel(), getLabels(table));
+        stack = null; //whoever reaches the point after us provides the stack, because we don't
+    }
+
+    private void adjustStackForSwitch(final Label defaultLabel, final Label... table) {
         popType(Type.INT);
-        method.visitTableSwitchInsn(lo, hi, defaultLabel.getLabel(), getLabels(table));
+        mergeStackTo(defaultLabel);
+        for(final Label label: table) {
+            mergeStackTo(label);
+        }
     }
 
     /**
@@ -1489,7 +1606,7 @@
      * @param label destination label
      */
     void _goto(final Label label) {
-        //debug("goto", label);
+        debug("goto", label);
         jump(GOTO, label, 0);
         stack = null; //whoever reaches the point after us provides the stack, because we don't
     }
@@ -1526,7 +1643,8 @@
             label.setStack(stack.copy());
             return;
         }
-        assert stack.isEquivalentTo(labelStack) : "stacks " + stack + " is not equivalent with " + labelStack + " at join point";
+        assert stack.isEquivalentInTypesTo(labelStack) : "stacks " + stack + " is not equivalent with " + labelStack + " at join point " + label;
+        stack.mergeLocalLoads(labelStack);
     }
 
     /**
@@ -1561,13 +1679,32 @@
      * @return the method emitter
      */
     MethodEmitter convert(final Type to) {
-        final Type type = peekType().convert(method, to);
+        final Type from = peekType();
+        final Type type = from.convert(method, to);
         if (type != null) {
-            if (!peekType().isEquivalentTo(to)) {
-                debug("convert", peekType(), "->", to);
+            if (!from.isEquivalentTo(to)) {
+                debug("convert", from, "->", to);
             }
-            popType();
-            pushType(type);
+            if (type != from) {
+                final int l0 = stack.getTopLocalLoad();
+                popType();
+                pushType(type);
+                // NOTE: conversions from a primitive type are considered to preserve the "load" property of the value
+                // on the stack. Otherwise we could introduce temporary locals in a deoptimized rest-of (e.g. doing an
+                // "i < x.length" where "i" is int and ".length" gets deoptimized to long would end up converting i to
+                // long with "ILOAD i; I2L; LSTORE tmp; LLOAD tmp;"). Such additional temporary would cause an error
+                // when restoring the state of the function for rest-of execution, as the not-yet deoptimized variant
+                // would have the (now invalidated) assumption that "x.length" is an int, so it wouldn't have the I2L,
+                // and therefore neither the subsequent LSTORE tmp; LLOAD tmp;. By making sure conversions from a
+                // primitive type don't erase the "load" information, we don't introduce temporaries in the deoptimized
+                // rest-of that didn't exist in the more optimistic version that triggered the deoptimization.
+                // NOTE: as a more general observation, we could theoretically track the operations required to
+                // reproduce any stack value as long as they are all local loads, constant loads, and stack operations.
+                // We won't go there in the current system
+                if(!from.isObject()) {
+                    stack.markLocalLoad(l0);
+                }
+            }
         }
         return this;
     }
@@ -1613,9 +1750,9 @@
      *
      * @return the method emitter
      */
-    MethodEmitter add() {
+    MethodEmitter add(final int programPoint) {
         debug("add");
-        pushType(get2().add(method));
+        pushType(get2().add(method, programPoint));
         return this;
     }
 
@@ -1624,9 +1761,9 @@
      *
      * @return the method emitter
      */
-    MethodEmitter sub() {
+    MethodEmitter sub(final int programPoint) {
         debug("sub");
-        pushType(get2n().sub(method));
+        pushType(get2n().sub(method, programPoint));
         return this;
     }
 
@@ -1635,9 +1772,9 @@
      *
      * @return the method emitter
      */
-    MethodEmitter mul() {
+    MethodEmitter mul(final int programPoint) {
         debug("mul ");
-        pushType(get2n().mul(method));
+        pushType(get2n().mul(method, programPoint));
         return this;
     }
 
@@ -1646,9 +1783,9 @@
      *
      * @return the method emitter
      */
-    MethodEmitter div() {
+    MethodEmitter div(final int programPoint) {
         debug("div");
-        pushType(get2n().div(method));
+        pushType(get2n().div(method, programPoint));
         return this;
     }
 
@@ -1670,13 +1807,15 @@
      * @return array of Types
      */
     protected Type[] getTypesFromStack(final int count) {
-        final Type[] types = new Type[count];
-        int pos = 0;
-        for (int i = count - 1; i >= 0; i--) {
-            types[i] = stack.peek(pos++);
-        }
+        return stack.getTopTypes(count);
+    }
 
-        return types;
+    int[] getLocalLoadsOnStack(final int from, final int to) {
+        return stack.getLocalLoads(from, to);
+    }
+
+    int getStackSize() {
+        return stack.size();
     }
 
     /**
@@ -1712,6 +1851,7 @@
      * @return the method emitter
      */
     MethodEmitter dynamicNew(final int argCount, final int flags) {
+        assert !isOptimistic(flags);
         debug("dynamic_new", "argcount=", argCount);
         final String signature = getDynamicSignature(Type.OBJECT, argCount);
         method.visitInvokeDynamicInsn("dyn:new", signature, LINKERBOOTSTRAP, flags);
@@ -1738,6 +1878,13 @@
         return this;
     }
 
+    MethodEmitter dynamicArrayPopulatorCall(final int argCount, final int startIndex) {
+        final String signature = getDynamicSignature(Type.OBJECT_ARRAY, argCount);
+        method.visitInvokeDynamicInsn("populateArray", signature, POPULATE_ARRAY_BOOTSTRAP, startIndex);
+        pushType(Type.OBJECT_ARRAY);
+        return this;
+    }
+
     /**
      * Generate a dynamic call for a runtime node
      *
@@ -1768,7 +1915,7 @@
      * @return the method emitter
      */
     MethodEmitter dynamicGet(final Type valueType, final String name, final int flags, final boolean isMethod) {
-        debug("dynamic_get", name, valueType);
+        debug("dynamic_get", name, valueType, getProgramPoint(flags));
 
         Type type = valueType;
         if (type.isObject() || type.isBoolean()) {
@@ -1780,7 +1927,6 @@
                 NameCodec.encode(name), Type.getMethodDescriptor(type, Type.OBJECT), LINKERBOOTSTRAP, flags);
 
         pushType(type);
-
         convert(valueType); //most probably a nop
 
         return this;
@@ -1794,7 +1940,8 @@
      * @param flags     call site flags
      */
      void dynamicSet(final String name, final int flags) {
-        debug("dynamic_set", name, peekType());
+         assert !isOptimistic(flags);
+         debug("dynamic_set", name, peekType());
 
         Type type = peekType();
         if (type.isObject() || type.isBoolean()) { //promote strings to objects etc
@@ -1818,7 +1965,8 @@
      * @return the method emitter
      */
     MethodEmitter dynamicGetIndex(final Type result, final int flags, final boolean isMethod) {
-        debug("dynamic_get_index", peekType(1), "[", peekType(), "]");
+        assert result.getTypeClass().isPrimitive() || result.getTypeClass() == Object.class;
+        debug("dynamic_get_index", peekType(1), "[", peekType(), "]", getProgramPoint(flags));
 
         Type resultType = result;
         if (result.isBoolean()) {
@@ -1836,8 +1984,7 @@
 
         final String signature = Type.getMethodDescriptor(resultType, Type.OBJECT /*e.g STRING->OBJECT*/, index);
 
-        method.visitInvokeDynamicInsn(isMethod ? "dyn:getMethod|getElem|getProp" : "dyn:getElem|getProp|getMethod",
-                signature, LINKERBOOTSTRAP, flags);
+        method.visitInvokeDynamicInsn(isMethod ? "dyn:getMethod|getElem|getProp" : "dyn:getElem|getProp|getMethod", signature, LINKERBOOTSTRAP, flags);
         pushType(resultType);
 
         if (result.isBoolean()) {
@@ -1847,6 +1994,14 @@
         return this;
     }
 
+
+    private static String getProgramPoint(int flags) {
+        if((flags & CALLSITE_OPTIMISTIC) == 0) {
+            return "";
+        }
+        return "pp=" + String.valueOf((flags & (-1 << CALLSITE_PROGRAM_POINT_SHIFT)) >> CALLSITE_PROGRAM_POINT_SHIFT);
+    }
+
     /**
      * Dynamic setter for indexed structures. Pop value, index and receiver from
      * stack, generate appropriate signature based on types
@@ -1854,6 +2009,7 @@
      * @param flags call site flags for setter
      */
     void dynamicSetIndex(final int flags) {
+        assert !isOptimistic(flags);
         debug("dynamic_set_index", peekType(2), "[", peekType(1), "] =", peekType());
 
         Type value = peekType();
@@ -2148,7 +2304,10 @@
                     } else {
                         sb.append(t.getDescriptor());
                     }
-
+                    final int loadIndex = stack.localLoads[stack.sp - 1 - pos];
+                    if(loadIndex != Label.Stack.NON_LOAD) {
+                        sb.append('(').append(loadIndex).append(')');
+                    }
                     if (pos + 1 < stack.size()) {
                         sb.append(' ');
                     }
@@ -2193,4 +2352,7 @@
         return null;
     }
 
+    private static boolean isOptimistic(final int flags) {
+        return (flags & CALLSITE_OPTIMISTIC) != 0;
+    }
 }
--- a/nashorn/src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java	Wed Feb 26 13:17:57 2014 +0100
@@ -35,27 +35,39 @@
 import static jdk.nashorn.internal.codegen.CompilerConstants.className;
 import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
 import static jdk.nashorn.internal.lookup.Lookup.MH;
+import static jdk.nashorn.internal.runtime.JSType.CONVERT_OBJECT;
+import static jdk.nashorn.internal.runtime.JSType.CONVERT_OBJECT_OPTIMISTIC;
+import static jdk.nashorn.internal.runtime.JSType.GET_UNDEFINED;
+import static jdk.nashorn.internal.runtime.JSType.getAccessorTypeIndex;
+import static jdk.nashorn.internal.runtime.JSType.TYPE_UNDEFINED_INDEX;
+import static jdk.nashorn.internal.runtime.JSType.TYPE_INT_INDEX;
+import static jdk.nashorn.internal.runtime.JSType.TYPE_LONG_INDEX;
+import static jdk.nashorn.internal.runtime.JSType.TYPE_DOUBLE_INDEX;
+import static jdk.nashorn.internal.runtime.JSType.TYPE_OBJECT_INDEX;
+import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
 
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
-import java.util.Arrays;
-import java.util.Collections;
 import java.util.EnumSet;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Set;
+import java.util.StringTokenizer;
+
 import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.runtime.AccessorProperty;
 import jdk.nashorn.internal.runtime.Context;
 import jdk.nashorn.internal.runtime.DebugLogger;
 import jdk.nashorn.internal.runtime.FunctionScope;
-import jdk.nashorn.internal.runtime.JSType;
 import jdk.nashorn.internal.runtime.PropertyMap;
 import jdk.nashorn.internal.runtime.ScriptEnvironment;
 import jdk.nashorn.internal.runtime.ScriptObject;
-import jdk.nashorn.internal.runtime.ScriptRuntime;
+import jdk.nashorn.internal.runtime.Undefined;
+import jdk.nashorn.internal.runtime.UnwarrantedOptimismException;
 import jdk.nashorn.internal.runtime.options.Options;
 
 /**
@@ -64,9 +76,9 @@
 public final class ObjectClassGenerator {
 
     /**
-     * Marker for scope parameters.
+     * Marker for scope parameters.g
      */
-    static final String SCOPE_MARKER = "P";
+    private static final String SCOPE_MARKER = "P";
 
     /**
      * Minimum number of extra fields in an object.
@@ -79,27 +91,55 @@
      */
     public static final DebugLogger LOG = new DebugLogger("fields", "nashorn.fields.debug");
 
+    private static final Set<String> FIELDS_TO_INSTRUMENT;
+    static {
+        final String fields = Options.getStringProperty("nashorn.fields", null);
+        final Set<String> fti = new HashSet<>();
+        if (fields != null) {
+            final StringTokenizer st = new StringTokenizer(fields, ",");
+            while (st.hasMoreTokens()) {
+                fti.add(st.nextToken());
+            }
+        }
+        FIELDS_TO_INSTRUMENT = fti.isEmpty() ? null : fti;
+    }
+
+    /**
+     * Should this particular field be instrumented with --log=fields
+     * Internal use only
+     * @param field field name
+     * @return true if it should be instrumented
+     */
+    public static boolean shouldInstrument(final String field) {
+        //if no explicit fields to imstrument are given, instrument all fields
+        return FIELDS_TO_INSTRUMENT == null || FIELDS_TO_INSTRUMENT.contains(field);
+    }
     /**
      * is field debugging enabled. Several modules in codegen and properties use this, hence
      * public access.
      */
     public static final boolean DEBUG_FIELDS = LOG.isEnabled();
 
+    private static final boolean EXPLICIT_OBJECT = Options.getBooleanProperty("nashorn.fields.objects");
+
     /**
      * Should the runtime only use java.lang.Object slots for fields? If this is false, the representation
      * will be a primitive 64-bit long value used for all primitives and a java.lang.Object for references.
      * This introduces a larger number of method handles in the system, as we need to have different getters
-     * and setters for the different fields. Currently this introduces significant overhead in Hotspot.
+     * and setters for the different fields.
      *
      * This is engineered to plug into the TaggedArray implementation, when it's done.
      */
-    public static final boolean OBJECT_FIELDS_ONLY = !Options.getBooleanProperty("nashorn.fields.dual");
+    public static final boolean OBJECT_FIELDS_ONLY = EXPLICIT_OBJECT || !ScriptEnvironment.globalOptimistic();
 
     /** The field types in the system */
     private static final List<Type> FIELD_TYPES = new LinkedList<>();
 
     /** What type is the primitive type in dual representation */
-    public static final Type PRIMITIVE_TYPE = Type.LONG;
+    public static final Type PRIMITIVE_FIELD_TYPE = Type.LONG;
+
+    private static final MethodHandle GET_DIFFERENT           = findOwnMH("getDifferent", Object.class, Object.class, Class.class, MethodHandle.class, MethodHandle.class, int.class);
+    private static final MethodHandle GET_DIFFERENT_UNDEFINED = findOwnMH("getDifferentUndefined", Object.class, int.class);
 
     /**
      * The list of field types that we support - one type creates one field. This is currently either
@@ -107,8 +147,10 @@
      */
     static {
         if (!OBJECT_FIELDS_ONLY) {
-            System.err.println("WARNING!!! Running with primitive fields - there is untested functionality!");
-            FIELD_TYPES.add(PRIMITIVE_TYPE);
+            LOG.warning("Running with primitive fields - there is untested functionality!");
+            FIELD_TYPES.add(PRIMITIVE_FIELD_TYPE);
+        } else {
+            System.err.println("Running with object fields only");
         }
         FIELD_TYPES.add(Type.OBJECT);
     }
@@ -117,23 +159,6 @@
     private final Context context;
 
     /**
-     * The list of available accessor types in width order. This order is used for type guesses narrow{@literal ->} wide
-     *  in the dual--fields world
-     */
-    public static final List<Type> ACCESSOR_TYPES = Collections.unmodifiableList(
-            Arrays.asList(
-                Type.INT,
-                Type.LONG,
-                Type.NUMBER,
-                Type.OBJECT));
-
-    //these are hard coded for speed and so that we can switch on them
-    private static final int TYPE_INT_INDEX    = 0; //getAccessorTypeIndex(int.class);
-    private static final int TYPE_LONG_INDEX   = 1; //getAccessorTypeIndex(long.class);
-    private static final int TYPE_DOUBLE_INDEX = 2; //getAccessorTypeIndex(double.class);
-    private static final int TYPE_OBJECT_INDEX = 3; //getAccessorTypeIndex(Object.class);
-
-    /**
      * Constructor
      *
      * @param context a context
@@ -144,61 +169,19 @@
     }
 
     /**
-     * Given a type of an accessor, return its index in [0..getNumberOfAccessorTypes())
-     *
-     * @param type the type
-     *
-     * @return the accessor index, or -1 if no accessor of this type exists
-     */
-    public static int getAccessorTypeIndex(final Type type) {
-        return getAccessorTypeIndex(type.getTypeClass());
-    }
-
-    /**
-     * Given a class of an accessor, return its index in [0..getNumberOfAccessorTypes())
-     *
-     * Note that this is hardcoded with respect to the dynamic contents of the accessor
-     * types array for speed. Hotspot got stuck with this as 5% of the runtime in
-     * a benchmark when it looped over values and increased an index counter. :-(
-     *
-     * @param type the type
-     *
-     * @return the accessor index, or -1 if no accessor of this type exists
+     * Pack a number into a primitive long field
+     * @param n number object
+     * @return primitive long value with all the bits in the number
      */
-    public static int getAccessorTypeIndex(final Class<?> type) {
-        if (type == int.class) {
-            return 0;
-        } else if (type == long.class) {
-            return 1;
-        } else if (type == double.class) {
-            return 2;
-        } else if (!type.isPrimitive()) {
-            return 3;
+    public static long pack(final Number n) {
+        if (n instanceof Integer) {
+            return n.intValue();
+        } else if (n instanceof Long) {
+            return n.longValue();
+        } else if (n instanceof Double) {
+            return Double.doubleToRawLongBits(n.doubleValue());
         }
-        return -1;
-    }
-
-    /**
-     * Return the number of accessor types available.
-     *
-     * @return number of accessor types in system
-     */
-    public static int getNumberOfAccessorTypes() {
-        return ACCESSOR_TYPES.size();
-    }
-
-    /**
-     * Return the accessor type based on its index in [0..getNumberOfAccessorTypes())
-     * Indexes are ordered narrower{@literal ->}wider / optimistic{@literal ->}pessimistic. Invalidations always
-     * go to a type of higher index
-     *
-     * @param index accessor type index
-     *
-     * @return a type corresponding to the index.
-     */
-
-    public static Type getAccessorType(final int index) {
-        return ACCESSOR_TYPES.get(index);
+        throw new AssertionError("cannot pack" + n);
     }
 
     /**
@@ -235,7 +218,7 @@
     public static int getFieldCount(Class<?> clazz) {
         final String name = clazz.getSimpleName();
         final String prefix = JS_OBJECT_PREFIX.symbolName();
-        if(prefix.equals(name)) {
+        if (prefix.equals(name)) {
             return 0;
         }
         final int scopeMarker = name.indexOf(SCOPE_MARKER);
@@ -324,6 +307,10 @@
         init.returnVoid();
         init.end();
 
+        final MethodEmitter initWithSpillArrays = newInitWithSpillArraysMethod(classEmitter, ScriptObject.class);
+        initWithSpillArrays.returnVoid();
+        initWithSpillArrays.end();
+
         newEmptyInit(classEmitter, className);
         newAllocate(classEmitter, className);
 
@@ -340,8 +327,8 @@
      * @return Byte codes for generated class.
      */
     public byte[] generate(final int fieldCount, final int paramCount) {
-        final String className          = getClassName(fieldCount, paramCount);
-        final String superName          = className(FunctionScope.class);
+        final String       className    = getClassName(fieldCount, paramCount);
+        final String       superName    = className(FunctionScope.class);
         final ClassEmitter classEmitter = newClassEmitter(className, superName);
         final List<String> initFields   = addFields(classEmitter, fieldCount);
 
@@ -350,6 +337,11 @@
         init.returnVoid();
         init.end();
 
+        final MethodEmitter initWithSpillArrays = newInitWithSpillArraysMethod(classEmitter, FunctionScope.class);
+        initializeToUndefined(initWithSpillArrays, className, initFields);
+        initWithSpillArrays.returnVoid();
+        initWithSpillArrays.end();
+
         final MethodEmitter initWithArguments = newInitScopeWithArgumentsMethod(classEmitter);
         initializeToUndefined(initWithArguments, className, initFields);
         initWithArguments.returnVoid();
@@ -414,6 +406,18 @@
         return init;
     }
 
+     private static MethodEmitter newInitWithSpillArraysMethod(final ClassEmitter classEmitter, final Class<?> superClass) {
+        final MethodEmitter init = classEmitter.init(PropertyMap.class, long[].class, Object[].class);
+        init.begin();
+        init.load(Type.OBJECT, JAVA_THIS.slot());
+        init.load(Type.OBJECT, INIT_MAP.slot());
+        init.load(Type.LONG_ARRAY, 2);
+        init.load(Type.OBJECT_ARRAY, 3);
+        init.invoke(constructorNoLookup(superClass, PropertyMap.class, long[].class, Object[].class));
+
+        return init;
+    }
+
     /**
      * Allocate and initialize a new <init> method for scopes.
      * @param classEmitter  Open class emitter.
@@ -472,7 +476,7 @@
     private static void newAllocate(final ClassEmitter classEmitter, final String className) {
         final MethodEmitter allocate = classEmitter.method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), ALLOCATE.symbolName(), ScriptObject.class, PropertyMap.class);
         allocate.begin();
-        allocate._new(className);
+        allocate._new(className, Type.typeFor(ScriptObject.class));
         allocate.dup();
         allocate.load(Type.typeFor(PropertyMap.class), 0);
         allocate.invoke(constructorNoLookup(className, PropertyMap.class));
@@ -492,7 +496,7 @@
         final byte[] code = classEmitter.toByteArray();
         final ScriptEnvironment env = context.getEnv();
 
-        if (env._print_code) {
+        if (env._print_code && env._print_code_dir == null) {
             env.getErr().println(ClassEmitter.disassemble(code));
         }
 
@@ -504,20 +508,172 @@
     }
 
     /** Double to long bits, used with --dual-fields for primitive double values */
-    private static final MethodHandle PACK_DOUBLE =
+    public static final MethodHandle PACK_DOUBLE =
         MH.explicitCastArguments(MH.findStatic(MethodHandles.publicLookup(), Double.class, "doubleToRawLongBits", MH.type(long.class, double.class)), MH.type(long.class, double.class));
 
     /** double bits to long, used with --dual-fields for primitive double values */
-    private static MethodHandle UNPACK_DOUBLE =
+    public static final MethodHandle UNPACK_DOUBLE =
         MH.findStatic(MethodHandles.publicLookup(), Double.class, "longBitsToDouble", MH.type(double.class, long.class));
 
-    /** object conversion quickies with JS semantics - used for return value and parameter filter */
-    private static MethodHandle[] CONVERT_OBJECT = {
-        JSType.TO_INT32.methodHandle(),
-        JSType.TO_UINT32.methodHandle(),
-        JSType.TO_NUMBER.methodHandle(),
-        null
-    };
+    //type != forType, so use the correct getter for forType, box it and throw
+    @SuppressWarnings("unused")
+    private static Object getDifferent(final Object receiver, final Class<?> forType, final MethodHandle primitiveGetter, final MethodHandle objectGetter, final int programPoint) {
+        //create the sametype getter, and upcast to value. no matter what the store format is,
+        //
+        final MethodHandle sameTypeGetter = getterForType(forType, primitiveGetter, objectGetter);
+        final MethodHandle mh = MH.asType(sameTypeGetter, sameTypeGetter.type().changeReturnType(Object.class));
+        try {
+            @SuppressWarnings("cast")
+            final Object value = (Object)mh.invokeExact(receiver);
+            throw new UnwarrantedOptimismException(value, programPoint);
+        } catch (final Error | RuntimeException e) {
+            throw e;
+        } catch (final Throwable e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @SuppressWarnings("unused")
+    private static Object getDifferentUndefined(final int programPoint) {
+        throw new UnwarrantedOptimismException(Undefined.getUndefined(), programPoint);
+    }
+
+    private static MethodHandle getterForType(final Class<?> forType, final MethodHandle primitiveGetter, final MethodHandle objectGetter) {
+        switch (getAccessorTypeIndex(forType)) {
+        case TYPE_INT_INDEX:
+            assert !OBJECT_FIELDS_ONLY : "this can only happen with dual fields";
+            return MH.explicitCastArguments(primitiveGetter, primitiveGetter.type().changeReturnType(int.class));
+        case TYPE_LONG_INDEX:
+            assert !OBJECT_FIELDS_ONLY : "this can only happen with dual fields";
+            return primitiveGetter;
+        case TYPE_DOUBLE_INDEX:
+            assert !OBJECT_FIELDS_ONLY : "this can only happen with dual fields";
+            return MH.filterReturnValue(primitiveGetter, UNPACK_DOUBLE);
+        case TYPE_OBJECT_INDEX:
+            return objectGetter;
+        default:
+            throw new AssertionError(forType);
+        }
+    }
+
+    //no optimism here. we do unconditional conversion to types
+    private static MethodHandle createGetterInner(final Class<?> forType, final Class<?> type, final MethodHandle primitiveGetter, final MethodHandle objectGetter, final MethodHandle[] converters, final int programPoint) {
+        final int fti = forType == null ? TYPE_UNDEFINED_INDEX : getAccessorTypeIndex(forType);
+        final int ti  = getAccessorTypeIndex(type);
+        //this means fail if forType != type
+        final boolean isOptimistic = converters == CONVERT_OBJECT_OPTIMISTIC;
+        final boolean isPrimitiveStorage = forType != null && forType.isPrimitive();
+
+        //which is the primordial getter
+        final MethodHandle getter = OBJECT_FIELDS_ONLY ? objectGetter : (isPrimitiveStorage ? primitiveGetter : objectGetter);
+
+        if (forType == null) {
+            if (isOptimistic) {
+                //return undefined if asking for object. otherwise throw UnwarrantedOptimismException
+                if (ti == TYPE_OBJECT_INDEX) {
+                    return MH.dropArguments(GET_UNDEFINED[TYPE_OBJECT_INDEX], 0, Object.class);
+                }
+                //throw exception
+                return MH.asType(
+                    MH.dropArguments(
+                            MH.insertArguments(
+                                    GET_DIFFERENT_UNDEFINED,
+                                    0,
+                                    programPoint),
+                            0,
+                            Object.class),
+                    getter.type().changeReturnType(type));
+            }
+            //return an undefined and coerce it to the appropriate type
+            return MH.dropArguments(GET_UNDEFINED[ti], 0, Object.class);
+        }
+
+        assert forType != null;
+        assert !OBJECT_FIELDS_ONLY || forType == Object.class : forType;
+
+        if (isOptimistic) {
+            if (fti < ti) {
+                //asking for a wider type than currently stored. then it's OK to coerce.
+                //e.g. stored as int,  ask for long or double
+                //e.g. stored as long, ask for double
+                assert fti != TYPE_UNDEFINED_INDEX;
+                final MethodHandle tgetter = getterForType(forType, primitiveGetter, objectGetter);
+                return MH.asType(tgetter, tgetter.type().changeReturnType(type));
+            } else if (fti == ti) {
+                //Fast path, never throw exception - exact getter, just unpack if needed
+                return getterForType(forType, primitiveGetter, objectGetter);
+            } else {
+                assert fti > ti;
+                //if asking for a narrower type than the storage - throw exception
+                //unless FTI is object, in that case we have to go through the converters
+                //there is no
+                if (fti == TYPE_OBJECT_INDEX) {
+                    return MH.filterReturnValue(
+                            objectGetter,
+                            MH.insertArguments(
+                                    converters[ti],
+                                    1,
+                                    programPoint));
+                }
+
+                //asking for narrower primitive than we have stored, that is an
+                //UnwarrantedOptimismException
+                return MH.asType(
+                        MH.filterArguments(
+                            objectGetter,
+                            0,
+                            MH.insertArguments(
+                                    GET_DIFFERENT,
+                                    1,
+                                    forType,
+                                    primitiveGetter,
+                                    objectGetter,
+                                    programPoint)),
+                        objectGetter.type().changeReturnType(type));
+            }
+        }
+
+        assert !isOptimistic;
+            //freely coerce the result to whatever you asked for, this is e.g. Object->int for a & b
+        final MethodHandle tgetter = getterForType(forType, primitiveGetter, objectGetter);
+        if (fti == TYPE_OBJECT_INDEX) {
+            if (fti != ti) {
+                return MH.filterReturnValue(tgetter, CONVERT_OBJECT[ti]);
+            }
+            return tgetter;
+        }
+
+        assert !OBJECT_FIELDS_ONLY;
+        //final MethodType pmt = primitiveGetter.type();
+        assert primitiveGetter != null;
+        final MethodType tgetterType = tgetter.type();
+        switch (fti) {
+        case TYPE_INT_INDEX:
+        case TYPE_LONG_INDEX:
+            switch (ti) {
+            case TYPE_INT_INDEX:
+                //get int while an int, truncating cast of long value
+                return MH.explicitCastArguments(tgetter, tgetterType.changeReturnType(int.class));
+            case TYPE_LONG_INDEX:
+                return primitiveGetter;
+            default:
+                return MH.asType(tgetter, tgetterType.changeReturnType(type));
+            }
+        case TYPE_DOUBLE_INDEX:
+            switch (ti) {
+            case TYPE_INT_INDEX:
+            case TYPE_LONG_INDEX:
+                return MH.explicitCastArguments(tgetter, tgetterType.changeReturnType(type));
+            case TYPE_DOUBLE_INDEX:
+                assert tgetterType.returnType() == double.class;
+                return tgetter;
+            default:
+                return MH.asType(tgetter, tgetterType.changeReturnType(Object.class));
+            }
+        default:
+            throw new UnsupportedOperationException(forType + "=>" + type);
+        }
+    }
 
     /**
      * Given a primitiveGetter (optional for non dual fields) and an objectSetter that retrieve
@@ -526,7 +682,7 @@
      * and we want an Object getter, in the dual fields world we'd pick the primitiveGetter,
      * which reads a long, use longBitsToDouble on the result to unpack it, and then change the
      * return type to Object, boxing it. In the objects only world there are only object fields,
-     * primtives are boxed when asked for them and we don't need to bother with primitive encoding
+     * primitives are boxed when asked for them and we don't need to bother with primitive encoding
      * (or even undefined, which if forType==null) representation, so we just return whatever is
      * in the object field. The object field is always initiated to Undefined, so here, where we have
      * the representation for Undefined in all our bits, this is not a problem.
@@ -543,110 +699,18 @@
      * @param type            type to retrieve it as
      * @param primitiveGetter getter to read the primitive version of this field (null if Objects Only)
      * @param objectGetter    getter to read the object version of this field
+     * @param programPoint    program point for getter, if program point is INVALID_PROGRAM_POINT, then this is not an optimistic getter
      *
      * @return getter for the given representation that returns the given type
      */
-    public static MethodHandle createGetter(final Class<?> forType, final Class<?> type, final MethodHandle primitiveGetter, final MethodHandle objectGetter) {
-        final int fti = forType == null ? -1 : getAccessorTypeIndex(forType);
-        final int ti  = getAccessorTypeIndex(type);
-
-        if (fti == TYPE_OBJECT_INDEX || OBJECT_FIELDS_ONLY) {
-            if (ti == TYPE_OBJECT_INDEX) {
-                return objectGetter;
-            }
-
-            return MH.filterReturnValue(objectGetter, CONVERT_OBJECT[ti]);
-        }
-
-        assert !OBJECT_FIELDS_ONLY;
-        if (forType == null) {
-            return GET_UNDEFINED[ti];
-        }
-
-        final MethodType pmt = primitiveGetter.type();
-
-        switch (fti) {
-        case TYPE_INT_INDEX:
-        case TYPE_LONG_INDEX:
-            switch (ti) {
-            case TYPE_INT_INDEX:
-                //get int while an int, truncating cast of long value
-                return MH.explicitCastArguments(primitiveGetter, pmt.changeReturnType(int.class));
-            case TYPE_LONG_INDEX:
-                return primitiveGetter;
-            default:
-                return MH.asType(primitiveGetter, pmt.changeReturnType(type));
-            }
-        case TYPE_DOUBLE_INDEX:
-            final MethodHandle getPrimitiveAsDouble = MH.filterReturnValue(primitiveGetter, UNPACK_DOUBLE);
-            switch (ti) {
-            case TYPE_INT_INDEX:
-            case TYPE_LONG_INDEX:
-                return MH.explicitCastArguments(getPrimitiveAsDouble, pmt.changeReturnType(type));
-            case TYPE_DOUBLE_INDEX:
-                return getPrimitiveAsDouble;
-            default:
-                return MH.asType(getPrimitiveAsDouble, pmt.changeReturnType(Object.class));
-            }
-        default:
-            assert false;
-            return null;
-        }
-    }
-
-    private static final MethodHandle IS_TYPE_GUARD = findOwnMH("isType", boolean.class, Class.class, Object.class);
-
-    @SuppressWarnings("unused")
-    private static boolean isType(final Class<?> boxedForType, final Object x) {
-        return x.getClass() == boxedForType;
-    }
-
-    private static Class<? extends Number> getBoxedType(final Class<?> forType) {
-        if (forType == int.class) {
-            return Integer.class;
-        }
-
-        if (forType == long.class) {
-            return Long.class;
-        }
-
-        if (forType == double.class) {
-            return Double.class;
-        }
-
-        assert false;
-        return null;
-    }
-
-    /**
-     * If we are setting boxed types (because the compiler couldn't determine which they were) to
-     * a primitive field, we can reuse the primitive field getter, as long as we are setting an element
-     * of the same boxed type as the primitive type representation
-     *
-     * @param forType           the current type
-     * @param primitiveSetter   primitive setter for the current type with an element of the current type
-     * @param objectSetter      the object setter
-     *
-     * @return method handle that checks if the element to be set is of the currenttype, even though it's boxed
-     *  and instead of using the generic object setter, that would blow up the type and invalidate the map,
-     *  unbox it and call the primitive setter instead
-     */
-    public static MethodHandle createGuardBoxedPrimitiveSetter(final Class<?> forType, final MethodHandle primitiveSetter, final MethodHandle objectSetter) {
-        final Class<? extends Number> boxedForType = getBoxedType(forType);
-        //object setter that checks for primitive if current type is primitive
-
-        return MH.guardWithTest(
-            MH.insertArguments(
-                MH.dropArguments(
-                    IS_TYPE_GUARD,
-                    1,
-                    Object.class),
-                0,
-                boxedForType),
-                MH.asType(
-                    primitiveSetter,
-                    objectSetter.type()),
-                objectSetter);
+    public static MethodHandle createGetter(final Class<?> forType, final Class<?> type, final MethodHandle primitiveGetter, final MethodHandle objectGetter, final int programPoint) {
+        return createGetterInner(
+                forType,
+                type,
+                primitiveGetter,
+                objectGetter,
+                isValid(programPoint) ? CONVERT_OBJECT_OPTIMISTIC : CONVERT_OBJECT,
+                programPoint);
     }
 
     /**
@@ -699,8 +763,30 @@
             }
             return MH.asType(MH.filterArguments(primitiveSetter, 1, PACK_DOUBLE), pmt.changeParameterType(1, type));
         default:
-            assert false;
-            return null;
+            throw new UnsupportedOperationException(forType + "=>" + type);
+        }
+    }
+
+    /**
+     * Get the unboxed (primitive) type for an object
+     * @param o object
+     * @return primive type or Object.class if not primitive
+     */
+    public static Class<?> unboxedFieldType(final Object o) {
+        if (OBJECT_FIELDS_ONLY) {
+            return Object.class;
+        }
+
+        if (o == null) {
+            return Object.class;
+        } else if (o.getClass() == Integer.class) {
+            return int.class;
+        } else if (o.getClass() == Long.class) {
+            return long.class;
+        } else if (o.getClass() == Double.class) {
+            return double.class;
+        } else {
+            return Object.class;
         }
     }
 
@@ -713,80 +799,9 @@
         return count / FIELD_PADDING * FIELD_PADDING + FIELD_PADDING;
     }
 
-    //
-    // Provide generic getters and setters for undefined types. If a type is undefined, all
-    // and marshals the set to the correct setter depending on the type of the value being set.
-    // Note that there are no actual undefined versions of int, long and double in JavaScript,
-    // but executing toInt32, toLong and toNumber always returns a working result, 0, 0L or NaN
-    //
-
-    /** The value of Undefined cast to an int32 */
-    public static final int    UNDEFINED_INT    = 0;
-    /** The value of Undefined cast to a long */
-    public static final long   UNDEFINED_LONG   = 0L;
-    /** The value of Undefined cast to a double */
-    public static final double UNDEFINED_DOUBLE = Double.NaN;
-
-    /**
-     * Compute type name for correct undefined getter
-     * @param type the type
-     * @return name of getter
-     */
-    private static String typeName(final Type type) {
-        String name = type.getTypeClass().getName();
-        final int dot = name.lastIndexOf('.');
-        if (dot != -1) {
-            name = name.substring(dot + 1);
-        }
-        return Character.toUpperCase(name.charAt(0)) + name.substring(1);
-    }
-
-    /**
-     * Handles for undefined getters of the different types
-     */
-    private static final MethodHandle[] GET_UNDEFINED = new MethodHandle[ObjectClassGenerator.getNumberOfAccessorTypes()];
-
-    /**
-     * Used to wrap getters for undefined values, where this matters. Currently only in dual fields.
-     * If an object starts out as undefined it needs special getters until it has been assigned
-     * something the first time
-     *
-     * @param returnType type to cast the undefined to
-     *
-     * @return undefined as returnType
-     */
-    public static MethodHandle getUndefined(final Class<?> returnType) {
-        return GET_UNDEFINED[ObjectClassGenerator.getAccessorTypeIndex(returnType)];
-    }
-
-    static {
-        int pos = 0;
-        for (final Type type : ACCESSOR_TYPES) {
-            GET_UNDEFINED[pos++] = findOwnMH("getUndefined" + typeName(type), type.getTypeClass(), Object.class);
-        }
-    }
-
-    @SuppressWarnings("unused")
-    private static int getUndefinedInt(final Object obj) {
-        return UNDEFINED_INT;
-    }
-
-    @SuppressWarnings("unused")
-    private static long getUndefinedLong(final Object obj) {
-        return UNDEFINED_LONG;
-    }
-
-    @SuppressWarnings("unused")
-    private static double getUndefinedDouble(final Object obj) {
-        return UNDEFINED_DOUBLE;
-    }
-
-    @SuppressWarnings("unused")
-    private static Object getUndefinedObject(final Object obj) {
-        return ScriptRuntime.UNDEFINED;
-    }
-
     private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
         return MH.findStatic(MethodHandles.lookup(), ObjectClassGenerator.class, name, MH.type(rtype, types));
     }
+
+
 }
--- a/nashorn/src/jdk/nashorn/internal/codegen/ObjectCreator.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/ObjectCreator.java	Wed Feb 26 13:17:57 2014 +0100
@@ -28,22 +28,21 @@
 import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
 
 import java.util.List;
-import jdk.nashorn.internal.ir.Symbol;
+import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.runtime.PropertyMap;
+import jdk.nashorn.internal.runtime.ScriptObject;
 
 /**
  * Base class for object creation code generation.
+ * @param <T> value type
  */
-public abstract class ObjectCreator {
+public abstract class ObjectCreator<T> {
 
-    /** List of keys to initiate in this ObjectCreator */
-    protected final List<String>  keys;
-
-    /** List of symbols to initiate in this ObjectCreator */
-    protected final List<Symbol>  symbols;
+    /** List of keys & symbols to initiate in this ObjectCreator */
+    final List<MapTuple<T>> tuples;
 
     /** Code generator */
-    protected final CodeGenerator codegen;
+    final CodeGenerator codegen;
 
     /** Property map */
     protected PropertyMap   propertyMap;
@@ -55,15 +54,13 @@
      * Constructor
      *
      * @param codegen      the code generator
-     * @param keys         the keys
-     * @param symbols      the symbols corresponding to keys, same index
+     * @param tuples       key,symbol,value (optional) tuples
      * @param isScope      is this object scope
      * @param hasArguments does the created object have an "arguments" property
      */
-    protected ObjectCreator(final CodeGenerator codegen, final List<String> keys, final List<Symbol> symbols, final boolean isScope, final boolean hasArguments) {
+    ObjectCreator(final CodeGenerator codegen, final List<MapTuple<T>> tuples, final boolean isScope, final boolean hasArguments) {
         this.codegen       = codegen;
-        this.keys          = keys;
-        this.symbols       = symbols;
+        this.tuples        = tuples;
         this.isScope       = isScope;
         this.hasArguments  = hasArguments;
     }
@@ -85,8 +82,8 @@
      * @param clazz type of MapCreator
      * @return map creator instantiated by type
      */
-    protected MapCreator newMapCreator(final Class<?> clazz) {
-        return new MapCreator(clazz, keys, symbols);
+    protected MapCreator<?> newMapCreator(final Class<? extends ScriptObject> clazz) {
+        return new MapCreator<>(clazz, tuples);
     }
 
     /**
@@ -107,6 +104,10 @@
         return method;
     }
 
+    PropertyMap getMap() {
+        return propertyMap;
+    }
+
     /**
      * Is this a scope object
      * @return true if scope
@@ -122,4 +123,26 @@
     protected boolean hasArguments() {
         return hasArguments;
     }
+
+    /**
+     * Technique for loading an initial value. Defined by anonymous subclasses in code gen.
+     *
+     * @param value Value to load.
+     */
+    protected abstract void loadValue(T value);
+
+    MethodEmitter loadTuple(final MethodEmitter method, final MapTuple<T> tuple, final boolean pack) {
+        loadValue(tuple.value);
+        if (pack && tuple.isPrimitive()) {
+            method.pack();
+        } else {
+            method.convert(Type.OBJECT);
+        }
+        return method;
+    }
+
+    MethodEmitter loadTuple(final MethodEmitter method, final MapTuple<T> tuple) {
+        return loadTuple(method, tuple, true);
+    }
+
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/codegen/ParamTypeMap.java	Wed Feb 26 13:17:57 2014 +0100
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2010-2014, 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.lang.invoke.MethodType;
+import java.util.HashMap;
+import java.util.Map;
+
+import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.ir.FunctionNode;
+
+/**
+ * A data structure that maps one or several function nodes (by their unique id:s, not by
+ * the FunctionNode object itself, due to copy on write changing it several times through
+ * code generation.
+ */
+public class ParamTypeMap {
+    final Map<Integer, Type[]> map = new HashMap<>();
+
+    /**
+     * Constructor
+     * @param functionNode functionNode
+     * @param type         method type found at runtime corresponding to parameter guess
+     */
+    public ParamTypeMap(final FunctionNode functionNode, final MethodType type) {
+        this(functionNode.getId(), type);
+    }
+
+    /**
+     * Constructor
+     * @param functionNodeId function node id
+     * @param type           method type found at runtime corresponding to parameter guess
+     */
+    public ParamTypeMap(final int functionNodeId, final MethodType type) {
+        final Type[] types = new Type[type.parameterCount()];
+        int pos = 0;
+        for (final Class<?> p : type.parameterArray()) {
+            types[pos++] = Type.typeFor(p);
+        }
+        map.put(functionNodeId, types);
+    }
+
+    ParamTypeMap(final Map<FunctionNode, Type[]> typeMap) {
+        for (Map.Entry<FunctionNode, Type[]> entry : typeMap.entrySet()) {
+            map.put(entry.getKey().getId(), entry.getValue());
+        }
+    }
+    /**
+     * Get the parameter type for this parameter position, or
+     * null if now known
+     * @param functionNode functionNode
+     * @param pos position
+     * @return parameter type for this callsite if known
+     */
+    Type get(final FunctionNode functionNode, final int pos) {
+        final Type[] types = map.get(functionNode.getId());
+        assert types == null || pos < types.length;
+        if (types != null && pos < types.length) {
+            return types[pos];
+        }
+        return null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/codegen/ProgramPoints.java	Wed Feb 26 13:17:57 2014 +0100
@@ -0,0 +1,110 @@
+/*
+ * 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.FIRST_PROGRAM_POINT;
+import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.MAX_PROGRAM_POINT_VALUE;
+
+import java.util.ArrayDeque;
+import java.util.Deque;
+import jdk.nashorn.internal.ir.AccessNode;
+import jdk.nashorn.internal.ir.BinaryNode;
+import jdk.nashorn.internal.ir.CallNode;
+import jdk.nashorn.internal.ir.Expression;
+import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.ir.IdentNode;
+import jdk.nashorn.internal.ir.IndexNode;
+import jdk.nashorn.internal.ir.LexicalContext;
+import jdk.nashorn.internal.ir.Node;
+import jdk.nashorn.internal.ir.Optimistic;
+import jdk.nashorn.internal.ir.UnaryNode;
+import jdk.nashorn.internal.ir.visitor.NodeVisitor;
+
+/**
+ * Find program points in the code that are needed for optimistic assumptions
+ */
+class ProgramPoints extends NodeVisitor<LexicalContext> {
+
+    private final Deque<int[]> nextProgramPoint = new ArrayDeque<>();
+
+    ProgramPoints() {
+        super(new LexicalContext());
+    }
+
+    private int next() {
+        final int next = nextProgramPoint.peek()[0]++;
+        if(next > MAX_PROGRAM_POINT_VALUE) {
+            throw new AssertionError("Function has more than " + MAX_PROGRAM_POINT_VALUE + " program points");
+        }
+        return next;
+    }
+
+    @Override
+    public boolean enterFunctionNode(final FunctionNode functionNode) {
+        nextProgramPoint.push(new int[] { FIRST_PROGRAM_POINT });
+        return true;
+    }
+
+    @Override
+    public Node leaveFunctionNode(final FunctionNode functionNode) {
+        nextProgramPoint.pop();
+        return functionNode;
+    }
+
+    private static Optimistic setProgramPoint(final Optimistic optimistic, final int programPoint) {
+        final Expression node = (Expression)optimistic.setProgramPoint(programPoint);
+        return (Optimistic)node;
+    }
+
+    @Override
+    public Node leaveIdentNode(final IdentNode identNode) {
+        return (Node)setProgramPoint(identNode, next());
+    }
+
+    @Override
+    public Node leaveCallNode(final CallNode callNode) {
+        return (Node)setProgramPoint(callNode, next());
+    }
+
+    @Override
+    public Node leaveAccessNode(final AccessNode accessNode) {
+        return (Node)setProgramPoint(accessNode, next());
+    }
+
+    @Override
+    public Node leaveIndexNode(final IndexNode indexNode) {
+        return (Node)setProgramPoint(indexNode, next());
+    }
+
+    @Override
+    public Node leaveBinaryNode(final BinaryNode binaryNode) {
+        return (Node)setProgramPoint(binaryNode, next());
+    }
+
+    @Override
+    public Node leaveUnaryNode(final UnaryNode unaryNode) {
+        return (Node)setProgramPoint(unaryNode, next());
+    }
+}
--- a/nashorn/src/jdk/nashorn/internal/codegen/RangeAnalyzer.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/RangeAnalyzer.java	Wed Feb 26 13:17:57 2014 +0100
@@ -28,6 +28,7 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
+
 import jdk.nashorn.internal.codegen.types.Range;
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.Assignment;
@@ -242,7 +243,7 @@
     }
 
     private Node leaveSelfModifyingAssign(final UnaryNode node, final Range range) {
-        setRange(node.rhs(), range);
+        setRange(node.getExpression(), range);
         setRange(node, range);
         return node;
     }
@@ -307,19 +308,18 @@
         switch (node.tokenType()) {
         case DECPREFIX:
         case DECPOSTFIX:
-            return leaveSelfModifyingAssign(node, RANGE.sub(node.rhs().getSymbol().getRange(), Range.createRange(1)));
+            return leaveSelfModifyingAssign(node, RANGE.sub(node.getExpression().getSymbol().getRange(), Range.createRange(1)));
         case INCPREFIX:
         case INCPOSTFIX:
-            return leaveSelfModifyingAssign(node, RANGE.add(node.rhs().getSymbol().getRange(), Range.createRange(1)));
+            return leaveSelfModifyingAssign(node, RANGE.add(node.getExpression().getSymbol().getRange(), Range.createRange(1)));
         default:
-            assert false;
-            return node;
+            throw new UnsupportedOperationException("" + node.tokenType());
         }
     }
 
     @Override
     public Node leaveADD(final UnaryNode node) {
-        Range range = node.rhs().getSymbol().getRange();
+        Range range = node.getExpression().getSymbol().getRange();
         if (!range.getType().isNumeric()) {
            range = Range.createTypeRange(Type.NUMBER);
         }
@@ -341,7 +341,7 @@
 
     @Override
     public Node leaveSUB(final UnaryNode node) {
-        setRange(node, RANGE.neg(node.rhs().getSymbol().getRange()));
+        setRange(node, RANGE.neg(node.getExpression().getSymbol().getRange()));
         return node;
     }
 
--- a/nashorn/src/jdk/nashorn/internal/codegen/RuntimeCallSite.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/RuntimeCallSite.java	Wed Feb 26 13:17:57 2014 +0100
@@ -42,7 +42,6 @@
 import jdk.nashorn.internal.ir.RuntimeNode;
 import jdk.nashorn.internal.ir.RuntimeNode.Request;
 import jdk.nashorn.internal.lookup.Lookup;
-import jdk.nashorn.internal.lookup.MethodHandleFactory;
 import jdk.nashorn.internal.runtime.ScriptRuntime;
 import jdk.nashorn.internal.runtime.linker.Bootstrap;
 
@@ -59,7 +58,7 @@
 public final class RuntimeCallSite extends MutableCallSite {
     static final Call BOOTSTRAP = staticCallNoLookup(Bootstrap.class, "runtimeBootstrap", CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class);
 
-    private static final MethodHandle NEXT = findOwnMH("next",  MethodHandle.class, String.class);
+    private static final MethodHandle NEXT = findOwnMH_V("next",  MethodHandle.class, String.class);
 
     private final RuntimeNode.Request request;
 
@@ -89,7 +88,7 @@
         }
 
         /**
-         * The first type to try to use for this genrated runtime node
+         * The first type to try to use for this generated runtime node
          *
          * @return a type
          */
@@ -351,19 +350,19 @@
     /** Unbox cache */
     private static final Map<Class<?>, MethodHandle> UNBOX;
 
-    private static final MethodHandle CHECKCAST  = findOwnMH("checkcast", boolean.class, Class.class, Object.class);
-    private static final MethodHandle CHECKCAST2 = findOwnMH("checkcast", boolean.class, Class.class, Object.class, Object.class);
-    private static final MethodHandle ADDCHECK   = findOwnMH("ADDcheck",  boolean.class, int.class, int.class);
+    private static final MethodHandle CHECKCAST  = findOwnMH_S("checkcast", boolean.class, Class.class, Object.class);
+    private static final MethodHandle CHECKCAST2 = findOwnMH_S("checkcast", boolean.class, Class.class, Object.class, Object.class);
+    private static final MethodHandle ADDCHECK   = findOwnMH_S("ADDcheck",  boolean.class, int.class, int.class);
 
     /**
      * Build maps of correct boxing operations
      */
     static {
         UNBOX = new HashMap<>();
-        UNBOX.put(Boolean.class, findOwnMH("unboxZ", int.class, Object.class));
-        UNBOX.put(Integer.class, findOwnMH("unboxI", int.class, Object.class));
-        UNBOX.put(Long.class,    findOwnMH("unboxJ", long.class, Object.class));
-        UNBOX.put(Number.class,  findOwnMH("unboxD", double.class, Object.class));
+        UNBOX.put(Boolean.class, findOwnMH_S("unboxZ", int.class, Object.class));
+        UNBOX.put(Integer.class, findOwnMH_S("unboxI", int.class, Object.class));
+        UNBOX.put(Long.class,    findOwnMH_S("unboxJ", long.class, Object.class));
+        UNBOX.put(Number.class,  findOwnMH_S("unboxD", double.class, Object.class));
 
         METHODS = new HashMap<>();
 
@@ -375,9 +374,9 @@
 
                 final boolean isCmp = Request.isComparison(req);
 
-                METHODS.put(req.name() + "int",    findOwnMH(req.name(), (isCmp ? boolean.class : int.class),  int.class, int.class));
-                METHODS.put(req.name() + "long",   findOwnMH(req.name(), (isCmp ? boolean.class : long.class), long.class, long.class));
-                METHODS.put(req.name() + "double", findOwnMH(req.name(), (isCmp ? boolean.class : double.class), double.class, double.class));
+                METHODS.put(req.name() + "int",    findOwnMH_S(req.name(), (isCmp ? boolean.class : int.class),  int.class, int.class));
+                METHODS.put(req.name() + "long",   findOwnMH_S(req.name(), (isCmp ? boolean.class : long.class), long.class, long.class));
+                METHODS.put(req.name() + "double", findOwnMH_S(req.name(), (isCmp ? boolean.class : double.class), double.class, double.class));
             }
         }
 
@@ -674,12 +673,11 @@
         return ((Number)obj).doubleValue();
     }
 
-    private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
-        try {
-            return MH.findStatic(MethodHandles.lookup(), RuntimeCallSite.class, name, MH.type(rtype, types));
-        } catch (final MethodHandleFactory.LookupException e) {
-            return MH.findVirtual(MethodHandles.lookup(), RuntimeCallSite.class, name, MH.type(rtype, types));
-        }
+    private static MethodHandle findOwnMH_S(final String name, final Class<?> rtype, final Class<?>... types) {
+        return MH.findStatic(MethodHandles.lookup(), RuntimeCallSite.class, name, MH.type(rtype, types));
     }
 
+    private static MethodHandle findOwnMH_V(final String name, final Class<?> rtype, final Class<?>... types) {
+        return MH.findVirtual(MethodHandles.lookup(), RuntimeCallSite.class, name, MH.type(rtype, types));
+    }
 }
--- a/nashorn/src/jdk/nashorn/internal/codegen/SharedScopeCall.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/SharedScopeCall.java	Wed Feb 26 13:17:57 2014 +0100
@@ -25,6 +25,8 @@
 
 package jdk.nashorn.internal.codegen;
 
+import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_OPTIMISTIC;
+
 import java.util.Arrays;
 import java.util.EnumSet;
 import jdk.nashorn.internal.codegen.types.Type;
@@ -81,6 +83,7 @@
         this.valueType = valueType;
         this.returnType = returnType;
         this.paramTypes = paramTypes;
+        assert (flags & CALLSITE_OPTIMISTIC) == 0;
         this.flags = flags;
         // If paramTypes is not null this is a call, otherwise it's just a get.
         this.isCall = paramTypes != null;
@@ -150,7 +153,10 @@
         method._goto(parentLoopStart);
         method.label(parentLoopDone);
 
-        method.dynamicGet(valueType, symbol.getName(), flags, isCall);
+        assert !isCall || valueType.isObject(); // Callables are always objects
+        // If flags are optimistic, but we're doing a call, remove optimistic flags from the getter, as they obviously
+        // only apply to the call.
+        method.dynamicGet(valueType, symbol.getName(), isCall ? CodeGenerator.nonOptimisticFlags(flags) : flags, isCall);
 
         // If this is a get we're done, otherwise call the value as function.
         if (isCall) {
@@ -164,6 +170,7 @@
                     slot++;
                 }
             }
+            // Shared scope calls disabled in optimistic world.
             method.dynamicCall(returnType, 2 + paramTypes.length, flags);
         }
 
@@ -179,17 +186,16 @@
                 final Type[] params = new Type[paramTypes.length + 2];
                 params[0] = Type.typeFor(ScriptObject.class);
                 params[1] = Type.INT;
-                int i = 2;
-                for (Type type : paramTypes)  {
-                    if (type.isObject()) {
-                        type = Type.OBJECT;
-                    }
-                    params[i++] = type;
-                }
+                System.arraycopy(paramTypes, 0, params, 2, paramTypes.length);
                 staticSignature = Type.getMethodDescriptor(returnType, params);
             }
         }
         return staticSignature;
     }
 
+    @Override
+    public String toString() {
+        return methodName + " " + staticSignature;
+    }
+
 }
--- a/nashorn/src/jdk/nashorn/internal/codegen/SpillObjectCreator.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/SpillObjectCreator.java	Wed Feb 26 13:17:57 2014 +0100
@@ -27,18 +27,18 @@
 
 import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
 import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
-import static jdk.nashorn.internal.codegen.types.Type.OBJECT;
+import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY;
+import static jdk.nashorn.internal.codegen.ObjectClassGenerator.unboxedFieldType;
 
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
-import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.Expression;
 import jdk.nashorn.internal.ir.LiteralNode;
-import jdk.nashorn.internal.ir.Symbol;
 import jdk.nashorn.internal.runtime.Property;
 import jdk.nashorn.internal.runtime.PropertyMap;
 import jdk.nashorn.internal.runtime.ScriptObject;
+import jdk.nashorn.internal.runtime.ScriptRuntime;
 import jdk.nashorn.internal.runtime.arrays.ArrayData;
 import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
 import jdk.nashorn.internal.scripts.JO;
@@ -46,21 +46,16 @@
 /**
  * An object creator that uses spill properties.
  */
-public class SpillObjectCreator extends ObjectCreator {
-
-    private final List<Expression> values;
+public final class SpillObjectCreator extends ObjectCreator<Expression> {
 
     /**
      * Constructor
      *
      * @param codegen  code generator
-     * @param keys     keys for fields in object
-     * @param symbols  symbols for fields in object
-     * @param values   list of values corresponding to keys
+     * @param tuples   tuples for key, symbol, value
      */
-    protected SpillObjectCreator(final CodeGenerator codegen, final List<String> keys, final List<Symbol> symbols, final List<Expression> values) {
-        super(codegen, keys, symbols, false, false);
-        this.values = values;
+    SpillObjectCreator(final CodeGenerator codegen, final List<MapTuple<Expression>> tuples) {
+        super(codegen, tuples, false, false);
         makeMap();
     }
 
@@ -68,82 +63,120 @@
     protected void makeObject(final MethodEmitter method) {
         assert !isScope() : "spill scope objects are not currently supported";
 
-        final int          length        = keys.size();
-        final Object[]     presetValues  = new Object[length];
+        final int          length        = tuples.size();
+        final long[]       jpresetValues = new long[ScriptObject.spillAllocationLength(length)];
+        final Object[]     opresetValues = new Object[ScriptObject.spillAllocationLength(length)];
         final Set<Integer> postsetValues = new LinkedHashSet<>();
         final int          callSiteFlags = codegen.getCallSiteFlags();
-        ArrayData          arrayData     = ArrayData.allocate(new Object[0]);
+        ArrayData          arrayData     = ArrayData.allocate(ScriptRuntime.EMPTY_ARRAY);
 
         // Compute constant property values
-        for (int i = 0; i < length; i++) {
-            final String key = keys.get(i);
-            final Expression value = values.get(i);
-
-            if (value == null) {
-                continue; // getter or setter
-            }
-
-            final Object constantValue = LiteralNode.objectAsConstant(value);
-            if (constantValue == LiteralNode.POSTSET_MARKER) {
-                postsetValues.add(i);
-                continue;
-            }
+        int pos = 0;
+        for (final MapTuple<Expression> tuple : tuples) {
+            final String     key   = tuple.key;
+            final Expression value = tuple.value;
 
-            final Property property = propertyMap.findProperty(key);
-            if (property != null) {
-                // normal property key
-                presetValues[property.getSlot()] = constantValue;
-            } else {
-                // array index key
-                final long oldLength = arrayData.length();
-                final int index = ArrayIndex.getArrayIndex(key);
-                assert ArrayIndex.isValidArrayIndex(index);
-                final long longIndex =  ArrayIndex.toLongIndex(index);
-                if (longIndex >= oldLength) {
-                    arrayData = arrayData.ensure(longIndex);
-                }
-                arrayData = arrayData.set(index, constantValue, false);
-                if (longIndex > oldLength) {
-                    arrayData = arrayData.delete(oldLength, longIndex - 1);
+            if (value != null) {
+                final Object constantValue = LiteralNode.objectAsConstant(value);
+                if (constantValue == LiteralNode.POSTSET_MARKER) {
+                    postsetValues.add(pos);
+                } else {
+                    final Property property = propertyMap.findProperty(key);
+                    if (property != null) {
+                        // normal property key
+                        property.setCurrentType(unboxedFieldType(constantValue));
+                        final int slot = property.getSlot();
+                        if (!OBJECT_FIELDS_ONLY && constantValue instanceof Number) {
+                            jpresetValues[slot] = ObjectClassGenerator.pack((Number)constantValue);
+                        } else {
+                            opresetValues[slot] = constantValue;
+                        }
+                    } else {
+                        // array index key
+                        final long oldLength = arrayData.length();
+                        final int  index     = ArrayIndex.getArrayIndex(key);
+                        final long longIndex = ArrayIndex.toLongIndex(index);
+
+                        assert ArrayIndex.isValidArrayIndex(index);
+
+                        if (longIndex >= oldLength) {
+                            arrayData = arrayData.ensure(longIndex);
+                        }
+
+                        //avoid blowing up the array if we can
+                        if (constantValue instanceof Integer) {
+                            arrayData = arrayData.set(index, ((Integer)constantValue).intValue(), false);
+                        } else if (constantValue instanceof Long) {
+                            arrayData = arrayData.set(index, ((Long)constantValue).longValue(), false);
+                        } else if (constantValue instanceof Double) {
+                            arrayData = arrayData.set(index, ((Double)constantValue).doubleValue(), false);
+                        } else {
+                            arrayData = arrayData.set(index, constantValue, false);
+                        }
+
+                        if (longIndex > oldLength) {
+                            arrayData = arrayData.delete(oldLength, longIndex - 1);
+                        }
+                    }
                 }
             }
+            pos++;
         }
 
+        //assert postsetValues.isEmpty() : "test me " + postsetValues;
+
         // create object and invoke constructor
         method._new(JO.class).dup();
         codegen.loadConstant(propertyMap);
-        method.invoke(constructorNoLookup(JO.class, PropertyMap.class));
+
+        //load primitive values to j spill array
+        codegen.loadConstant(jpresetValues);
+        for (final int i : postsetValues) {
+            final MapTuple<Expression> tuple    = tuples.get(i);
+            final Property                property = propertyMap.findProperty(tuple.key);
+            if (property != null && tuple.isPrimitive()) {
+                method.dup();
+                method.load(property.getSlot());
+                loadTuple(method, tuple);
+                method.arraystore();
+            }
+        }
 
-        // Set spill array with preset values
-        method.dup();
-        codegen.loadConstant(presetValues);
-        method.putField(Type.getInternalName(ScriptObject.class), "spill", Type.OBJECT_ARRAY.getDescriptor());
+        //load object values to o spill array
+        codegen.loadConstant(opresetValues);
+        for (final int i : postsetValues) {
+            final MapTuple<Expression> tuple    = tuples.get(i);
+            final Property             property = propertyMap.findProperty(tuple.key);
+            if (property != null && !tuple.isPrimitive()) {
+                method.dup();
+                method.load(property.getSlot());
+                loadTuple(method, tuple);
+                method.arraystore();
+            }
+        }
 
-        // Set array data if any
+        //instantiate the script object with spill objects
+        method.invoke(constructorNoLookup(JO.class, PropertyMap.class, long[].class, Object[].class));
+
+        // Set prefix array data if any
         if (arrayData.length() > 0) {
             method.dup();
             codegen.loadConstant(arrayData);
-            method.invoke(virtualCallNoLookup(ScriptObject.class, "setArray",void.class, ArrayData.class));
+            method.invoke(virtualCallNoLookup(ScriptObject.class, "setArray", void.class, ArrayData.class));
         }
 
-        // Create properties with non-constant values
-        for (int i : postsetValues) {
-            final String key = keys.get(i);
-            final Property property = propertyMap.findProperty(key);
-
+        // set postfix
+        for (final int i : postsetValues) {
+            final MapTuple<Expression> tuple    = tuples.get(i);
+            final Property             property = propertyMap.findProperty(tuple.key);
             if (property == null) {
-                final int index = ArrayIndex.getArrayIndex(key);
+                final int index = ArrayIndex.getArrayIndex(tuple.key);
                 assert ArrayIndex.isValidArrayIndex(index);
                 method.dup();
                 method.load(ArrayIndex.toLongIndex(index));
-                codegen.load(values.get(i));
+                //method.println("putting " + tuple + " into arraydata");
+                loadTuple(method, tuple);
                 method.dynamicSetIndex(callSiteFlags);
-            } else {
-                method.dup();
-                method.getField(Type.getInternalName(ScriptObject.class), "spill", Type.OBJECT_ARRAY.getDescriptor());
-                method.load(property.getSlot());
-                codegen.load(values.get(i), OBJECT);
-                method.arraystore();
             }
         }
     }
@@ -151,14 +184,12 @@
     @Override
     protected PropertyMap makeMap() {
         assert propertyMap == null : "property map already initialized";
-
-        propertyMap = new MapCreator(JO.class, keys, symbols) {
-            @Override
-            protected int getPropertyFlags(Symbol symbol, boolean hasArguments) {
-                return super.getPropertyFlags(symbol, hasArguments) | Property.IS_SPILL | Property.IS_ALWAYS_OBJECT;
-            }
-        }.makeSpillMap(false);
-
+        propertyMap = new MapCreator<>(JO.class, tuples).makeSpillMap(false);
         return propertyMap;
     }
+
+    @Override
+    protected void loadValue(final Expression node) {
+        codegen.load(node);
+    }
 }
--- a/nashorn/src/jdk/nashorn/internal/codegen/Splitter.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Splitter.java	Wed Feb 26 13:17:57 2014 +0100
@@ -81,20 +81,16 @@
     }
 
     /**
-     * Execute the split
+     * Execute the split.
+     * @param fn the function to split
+     * @param top whether this is the topmost compiled function (it's either a program, or we're doing a recompilation).
      */
-    FunctionNode split(final FunctionNode fn) {
+    FunctionNode split(final FunctionNode fn, boolean top) {
         FunctionNode functionNode = fn;
 
-        if (functionNode.isLazy()) {
-            LOG.finest("Postponing split of '", functionNode.getName(), "' as it's lazy");
-            return functionNode;
-        }
-
         LOG.finest("Initiating split of '", functionNode.getName(), "'");
 
         long weight = WeighNodes.weigh(functionNode);
-        final boolean top = fn.isProgram(); //compiler.getFunctionNode() == outermost;
 
         // We know that our LexicalContext is empty outside the call to functionNode.accept(this) below,
         // so we can pass null to all methods expecting a LexicalContext parameter.
@@ -138,7 +134,7 @@
 
             @Override
             public Node leaveFunctionNode(final FunctionNode nestedFunction) {
-                FunctionNode split = new Splitter(compiler, nestedFunction, outermostCompileUnit).split(nestedFunction);
+                FunctionNode split = new Splitter(compiler, nestedFunction, outermostCompileUnit).split(nestedFunction, false);
                 lc.replace(nestedFunction, split);
                 return split;
             }
@@ -319,10 +315,7 @@
     @Override
     public boolean enterFunctionNode(final FunctionNode node) {
         //only go into the function node for this splitter. any subfunctions are rejected
-        if (node == outermost && !node.isLazy()) {
-            return true;
-        }
-        return false;
+        return node == outermost;
     }
 }
 
--- a/nashorn/src/jdk/nashorn/internal/codegen/WeighNodes.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/WeighNodes.java	Wed Feb 26 13:17:57 2014 +0100
@@ -27,7 +27,6 @@
 
 import java.util.List;
 import java.util.Map;
-import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.AccessNode;
 import jdk.nashorn.internal.ir.BinaryNode;
 import jdk.nashorn.internal.ir.Block;
@@ -69,24 +68,25 @@
      * Weight constants.
      */
     static final long FUNCTION_WEIGHT  = 40;
-    static final long AASTORE_WEIGHT   = 2;
-    static final long ACCESS_WEIGHT    = 4;
+    static final long AASTORE_WEIGHT   =  2;
+    static final long ACCESS_WEIGHT    =  4;
     static final long ADD_WEIGHT       = 10;
-    static final long BREAK_WEIGHT     = 1;
+    static final long BREAK_WEIGHT     =  1;
     static final long CALL_WEIGHT      = 10;
     static final long CATCH_WEIGHT     = 10;
-    static final long CONTINUE_WEIGHT  = 1;
-    static final long IF_WEIGHT        = 2;
+    static final long COMPARE_WEIGHT   =  6;
+    static final long CONTINUE_WEIGHT  =  1;
+    static final long IF_WEIGHT        =  2;
     static final long LITERAL_WEIGHT   = 10;
-    static final long LOOP_WEIGHT      = 4;
-    static final long NEW_WEIGHT       = 6;
+    static final long LOOP_WEIGHT      =  4;
+    static final long NEW_WEIGHT       =  6;
     static final long FUNC_EXPR_WEIGHT = 20;
-    static final long RETURN_WEIGHT    = 2;
+    static final long RETURN_WEIGHT    =  2;
     static final long SPLIT_WEIGHT     = 40;
-    static final long SWITCH_WEIGHT    = 8;
-    static final long THROW_WEIGHT     = 2;
+    static final long SWITCH_WEIGHT    =  8;
+    static final long THROW_WEIGHT     =  2;
     static final long VAR_WEIGHT       = 40;
-    static final long WITH_WEIGHT      = 8;
+    static final long WITH_WEIGHT      =  8;
 
     /** Accumulated weight. */
     private long weight;
@@ -446,22 +446,22 @@
 
     @Override
     public Node leaveEQ(final BinaryNode binaryNode) {
-        return runtimeNodeWeight(binaryNode);
+        return compareWeight(binaryNode);
     }
 
     @Override
     public Node leaveEQ_STRICT(final BinaryNode binaryNode) {
-        return runtimeNodeWeight(binaryNode);
+        return compareWeight(binaryNode);
     }
 
     @Override
     public Node leaveGE(final BinaryNode binaryNode) {
-        return runtimeNodeWeight(binaryNode);
+        return compareWeight(binaryNode);
     }
 
     @Override
     public Node leaveGT(final BinaryNode binaryNode) {
-        return runtimeNodeWeight(binaryNode);
+        return compareWeight(binaryNode);
     }
 
     @Override
@@ -478,12 +478,12 @@
 
     @Override
     public Node leaveLE(final BinaryNode binaryNode) {
-        return runtimeNodeWeight(binaryNode);
+        return compareWeight(binaryNode);
     }
 
     @Override
     public Node leaveLT(final BinaryNode binaryNode) {
-        return runtimeNodeWeight(binaryNode);
+        return compareWeight(binaryNode);
     }
 
     @Override
@@ -498,12 +498,12 @@
 
     @Override
     public Node leaveNE(final BinaryNode binaryNode) {
-        return runtimeNodeWeight(binaryNode);
+        return compareWeight(binaryNode);
     }
 
     @Override
     public Node leaveNE_STRICT(final BinaryNode binaryNode) {
-        return runtimeNodeWeight(binaryNode);
+        return compareWeight(binaryNode);
     }
 
     @Override
@@ -546,8 +546,8 @@
         return unaryNode;
     }
 
-    private Node runtimeNodeWeight(final BinaryNode binaryNode) {
-        weight += Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType()).isObject() ? CALL_WEIGHT : 1;
+    private Node compareWeight(final BinaryNode binaryNode) {
+        weight += COMPARE_WEIGHT;
         return binaryNode;
     }
 }
--- a/nashorn/src/jdk/nashorn/internal/codegen/types/BooleanType.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/types/BooleanType.java	Wed Feb 26 13:17:57 2014 +0100
@@ -56,10 +56,9 @@
 import static jdk.internal.org.objectweb.asm.Opcodes.IRETURN;
 import static jdk.internal.org.objectweb.asm.Opcodes.ISTORE;
 import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
-
+import static jdk.nashorn.internal.runtime.JSType.UNDEFINED_INT;
 import jdk.internal.org.objectweb.asm.MethodVisitor;
 import jdk.nashorn.internal.codegen.CompilerConstants;
-import jdk.nashorn.internal.codegen.ObjectClassGenerator;
 import jdk.nashorn.internal.runtime.JSType;
 
 /**
@@ -87,8 +86,19 @@
     }
 
     @Override
+    public char getBytecodeStackType() {
+        return 'I';
+    }
+
+    @Override
     public Type loadUndefined(final MethodVisitor method) {
-        method.visitLdcInsn(ObjectClassGenerator.UNDEFINED_INT);
+        method.visitLdcInsn(UNDEFINED_INT);
+        return BOOLEAN;
+    }
+
+    @Override
+    public Type loadForcedInitializer(MethodVisitor method) {
+        method.visitInsn(ICONST_0);
         return BOOLEAN;
     }
 
@@ -125,30 +135,29 @@
 
         if (to.isNumber()) {
             convert(method, OBJECT);
-            invokeStatic(method, JSType.TO_NUMBER);
+            invokestatic(method, JSType.TO_NUMBER);
         } else if (to.isInteger()) {
             return to; // do nothing.
         } else if (to.isLong()) {
             convert(method, OBJECT);
-            invokeStatic(method, JSType.TO_UINT32);
+            invokestatic(method, JSType.TO_UINT32);
         } else if (to.isLong()) {
             convert(method, OBJECT);
-            invokeStatic(method, JSType.TO_LONG);
+            invokestatic(method, JSType.TO_LONG);
         } else if (to.isString()) {
-            invokeStatic(method, VALUE_OF);
-            invokeStatic(method, JSType.TO_PRIMITIVE_TO_STRING);
+            invokestatic(method, VALUE_OF);
+            invokestatic(method, JSType.TO_PRIMITIVE_TO_STRING);
         } else if (to.isObject()) {
-            invokeStatic(method, VALUE_OF);
+            invokestatic(method, VALUE_OF);
         } else {
-            assert false : "Illegal conversion " + this + " -> " + to;
+            throw new UnsupportedOperationException("Illegal conversion " + this + " -> " + to);
         }
 
         return to;
     }
 
     @Override
-    public Type add(final MethodVisitor method) {
-        assert false : "unsupported operation";
-        return null;
+    public Type add(final MethodVisitor method, final int programPoint) {
+        throw new UnsupportedOperationException("add");
     }
 }
--- a/nashorn/src/jdk/nashorn/internal/codegen/types/BytecodeNumericOps.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/types/BytecodeNumericOps.java	Wed Feb 26 13:17:57 2014 +0100
@@ -36,47 +36,52 @@
      * Pop and negate the value on top of the stack and push the result
      *
      * @param method method visitor
-     *
+     * @param programPoint program point id
      * @return result type
      */
-    Type neg(MethodVisitor method);
+    Type neg(MethodVisitor method, int programPoint);
 
     /**
      * Pop two values on top of the stack and subtract the first from the
      * second, pushing the result on the stack
      *
      * @param method method visitor
-     *
+     * @param programPoint program point id
      * @return result type
      */
-    Type sub(MethodVisitor method);
+    Type sub(MethodVisitor method, int programPoint);
 
     /**
      * Pop and multiply the two values on top of the stack and push the result
      * on the stack
      *
      * @param method method visitor
-     *
+     * @param programPoint program point id
      * @return result type
      */
-    Type mul(MethodVisitor method);
+    Type mul(MethodVisitor method, int programPoint);
 
     /**
      * Pop two values on top of the stack and divide the first with the second,
      * pushing the result on the stack
      *
      * @param method method visitor
-     *
+     * @param programPoint program point id
      * @return result type
      */
-    Type div(MethodVisitor method);
+    Type div(MethodVisitor method, int programPoint);
 
     /**
      * Pop two values on top of the stack and compute the modulo of the first
      * with the second, pushing the result on the stack
      *
+     * Note that the rem method never takes a program point, because it
+     * can never be more optimistic than its widest operand - an int/int
+     * rem operation or a long/long rem operation can never return a
+     * winder remainder than the int or the long
+     *
      * @param method method visitor
-     *
+     * @param programPoint program point id
      * @return result type
      */
     Type rem(MethodVisitor method);
--- a/nashorn/src/jdk/nashorn/internal/codegen/types/BytecodeOps.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/types/BytecodeOps.java	Wed Feb 26 13:17:57 2014 +0100
@@ -85,9 +85,10 @@
      * first to the second, pushing the result on the stack
      *
      * @param method  method visitor
+     * @param programPoint program point id
      * @return result type
      */
-    Type add(MethodVisitor method);
+    Type add(MethodVisitor method, int programPoint);
 
     /**
      * Load a variable from a local slot to the stack
@@ -129,6 +130,17 @@
     Type loadUndefined(MethodVisitor method);
 
     /**
+     * Load the "forced initializer" value to the stack, used to ensure that a local variable has a value when it is
+     * read by the unwarranted optimism catch block.
+     *
+     * @param  method  method visitor.
+     *
+     * @return the forced initialization type at the top of the stack
+     */
+    Type loadForcedInitializer(MethodVisitor method);
+
+
+    /**
      * Load the "empty" value to the stack.
      *
      * @param  method  method visitor.
--- a/nashorn/src/jdk/nashorn/internal/codegen/types/IntType.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/types/IntType.java	Wed Feb 26 13:17:57 2014 +0100
@@ -28,7 +28,6 @@
 import static jdk.internal.org.objectweb.asm.Opcodes.BIPUSH;
 import static jdk.internal.org.objectweb.asm.Opcodes.I2D;
 import static jdk.internal.org.objectweb.asm.Opcodes.I2L;
-import static jdk.internal.org.objectweb.asm.Opcodes.IADD;
 import static jdk.internal.org.objectweb.asm.Opcodes.IAND;
 import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_0;
 import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_1;
@@ -37,25 +36,20 @@
 import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_4;
 import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_5;
 import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_M1;
-import static jdk.internal.org.objectweb.asm.Opcodes.IDIV;
 import static jdk.internal.org.objectweb.asm.Opcodes.ILOAD;
-import static jdk.internal.org.objectweb.asm.Opcodes.IMUL;
-import static jdk.internal.org.objectweb.asm.Opcodes.INEG;
 import static jdk.internal.org.objectweb.asm.Opcodes.IOR;
 import static jdk.internal.org.objectweb.asm.Opcodes.IREM;
 import static jdk.internal.org.objectweb.asm.Opcodes.IRETURN;
 import static jdk.internal.org.objectweb.asm.Opcodes.ISHL;
 import static jdk.internal.org.objectweb.asm.Opcodes.ISHR;
 import static jdk.internal.org.objectweb.asm.Opcodes.ISTORE;
-import static jdk.internal.org.objectweb.asm.Opcodes.ISUB;
 import static jdk.internal.org.objectweb.asm.Opcodes.IUSHR;
 import static jdk.internal.org.objectweb.asm.Opcodes.IXOR;
 import static jdk.internal.org.objectweb.asm.Opcodes.SIPUSH;
 import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
-
+import static jdk.nashorn.internal.runtime.JSType.UNDEFINED_INT;
 import jdk.internal.org.objectweb.asm.MethodVisitor;
 import jdk.nashorn.internal.codegen.CompilerConstants;
-import jdk.nashorn.internal.codegen.ObjectClassGenerator;
 
 /**
  * Type class: INT
@@ -80,42 +74,47 @@
     }
 
     @Override
+    public char getBytecodeStackType() {
+        return 'I';
+    }
+
+    @Override
     public Type ldc(final MethodVisitor method, final Object c) {
         assert c instanceof Integer;
 
         final int value = ((Integer) c).intValue();
 
         switch (value) {
-            case -1:
-                method.visitInsn(ICONST_M1);
-                break;
-            case 0:
-                method.visitInsn(ICONST_0);
-                break;
-            case 1:
-                method.visitInsn(ICONST_1);
-                break;
-            case 2:
-                method.visitInsn(ICONST_2);
-                break;
-            case 3:
-                method.visitInsn(ICONST_3);
-                break;
-            case 4:
-                method.visitInsn(ICONST_4);
-                break;
-            case 5:
-                method.visitInsn(ICONST_5);
-                break;
-            default:
-                if (value == (byte) value) {
-                    method.visitIntInsn(BIPUSH, value);
-                } else if (value == (short) value) {
-                    method.visitIntInsn(SIPUSH, value);
-                } else {
-                    method.visitLdcInsn(c);
-                }
-                break;
+        case -1:
+            method.visitInsn(ICONST_M1);
+            break;
+        case 0:
+            method.visitInsn(ICONST_0);
+            break;
+        case 1:
+            method.visitInsn(ICONST_1);
+            break;
+        case 2:
+            method.visitInsn(ICONST_2);
+            break;
+        case 3:
+            method.visitInsn(ICONST_3);
+            break;
+        case 4:
+            method.visitInsn(ICONST_4);
+            break;
+        case 5:
+            method.visitInsn(ICONST_5);
+            break;
+        default:
+            if (value == (byte) value) {
+                method.visitIntInsn(BIPUSH, value);
+            } else if (value == (short) value) {
+                method.visitIntInsn(SIPUSH, value);
+            } else {
+                method.visitLdcInsn(c);
+            }
+            break;
         }
 
         return Type.INT;
@@ -134,19 +133,19 @@
         } else if (to.isBoolean()) {
             //nop
         } else if (to.isString()) {
-            invokeStatic(method, TO_STRING);
+            invokestatic(method, TO_STRING);
         } else if (to.isObject()) {
-            invokeStatic(method, VALUE_OF);
+            invokestatic(method, VALUE_OF);
         } else {
-            assert false : "Illegal conversion " + this + " -> " + to;
+            throw new UnsupportedOperationException("Illegal conversion " + this + " -> " + to);
         }
 
         return to;
     }
 
     @Override
-    public Type add(final MethodVisitor method) {
-        method.visitInsn(IADD);
+    public Type add(final MethodVisitor method, final int programPoint) {
+        method.visitInvokeDynamicInsn("iadd", "(II)I", MATHBOOTSTRAP, programPoint);
         return INT;
     }
 
@@ -200,20 +199,20 @@
     }
 
     @Override
-    public Type sub(final MethodVisitor method) {
-        method.visitInsn(ISUB);
+    public Type sub(final MethodVisitor method, final int programPoint) {
+        method.visitInvokeDynamicInsn("isub", "(II)I", MATHBOOTSTRAP, programPoint);
         return INT;
     }
 
     @Override
-    public Type mul(final MethodVisitor method) {
-        method.visitInsn(IMUL);
+    public Type mul(final MethodVisitor method, final int programPoint) {
+        method.visitInvokeDynamicInsn("imul", "(II)I", MATHBOOTSTRAP, programPoint);
         return INT;
     }
 
     @Override
-    public Type div(final MethodVisitor method) {
-        method.visitInsn(IDIV);
+    public Type div(final MethodVisitor method, final int programPoint) {
+        method.visitInvokeDynamicInsn("idiv", "(II)I", MATHBOOTSTRAP, programPoint);
         return INT;
     }
 
@@ -224,8 +223,8 @@
     }
 
     @Override
-    public Type neg(final MethodVisitor method) {
-        method.visitInsn(INEG);
+    public Type neg(final MethodVisitor method, final int programPoint) {
+        method.visitInvokeDynamicInsn("ineg", "(I)I", MATHBOOTSTRAP, programPoint);
         return INT;
     }
 
@@ -236,19 +235,24 @@
 
     @Override
     public Type loadUndefined(final MethodVisitor method) {
-        method.visitLdcInsn(ObjectClassGenerator.UNDEFINED_INT);
+        method.visitLdcInsn(UNDEFINED_INT);
+        return INT;
+    }
+
+    @Override
+    public Type loadForcedInitializer(MethodVisitor method) {
+        method.visitInsn(ICONST_0);
         return INT;
     }
 
     @Override
     public Type cmp(final MethodVisitor method, final boolean isCmpG) {
-        assert false : "unsupported operation";
-        return null;
+        throw new UnsupportedOperationException("cmp" + (isCmpG ? 'g' : 'l'));
     }
 
     @Override
     public Type cmp(final MethodVisitor method) {
-        assert false : "unsupported operation";
-        return null;
+        throw new UnsupportedOperationException("cmp");
     }
+
 }
--- a/nashorn/src/jdk/nashorn/internal/codegen/types/LongType.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/types/LongType.java	Wed Feb 26 13:17:57 2014 +0100
@@ -27,29 +27,25 @@
 
 import static jdk.internal.org.objectweb.asm.Opcodes.L2D;
 import static jdk.internal.org.objectweb.asm.Opcodes.L2I;
-import static jdk.internal.org.objectweb.asm.Opcodes.LADD;
 import static jdk.internal.org.objectweb.asm.Opcodes.LAND;
 import static jdk.internal.org.objectweb.asm.Opcodes.LCMP;
 import static jdk.internal.org.objectweb.asm.Opcodes.LCONST_0;
 import static jdk.internal.org.objectweb.asm.Opcodes.LCONST_1;
-import static jdk.internal.org.objectweb.asm.Opcodes.LDIV;
 import static jdk.internal.org.objectweb.asm.Opcodes.LLOAD;
-import static jdk.internal.org.objectweb.asm.Opcodes.LMUL;
-import static jdk.internal.org.objectweb.asm.Opcodes.LNEG;
 import static jdk.internal.org.objectweb.asm.Opcodes.LOR;
 import static jdk.internal.org.objectweb.asm.Opcodes.LREM;
 import static jdk.internal.org.objectweb.asm.Opcodes.LRETURN;
 import static jdk.internal.org.objectweb.asm.Opcodes.LSHL;
 import static jdk.internal.org.objectweb.asm.Opcodes.LSHR;
 import static jdk.internal.org.objectweb.asm.Opcodes.LSTORE;
-import static jdk.internal.org.objectweb.asm.Opcodes.LSUB;
 import static jdk.internal.org.objectweb.asm.Opcodes.LUSHR;
 import static jdk.internal.org.objectweb.asm.Opcodes.LXOR;
 import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
+import static jdk.nashorn.internal.runtime.JSType.UNDEFINED_LONG;
 
 import jdk.internal.org.objectweb.asm.MethodVisitor;
 import jdk.nashorn.internal.codegen.CompilerConstants;
-import jdk.nashorn.internal.codegen.ObjectClassGenerator;
+import jdk.nashorn.internal.runtime.JSType;
 
 /**
  * Type class: LONG
@@ -77,6 +73,11 @@
     }
 
     @Override
+    public char getBytecodeStackType() {
+        return 'J';
+    }
+
+    @Override
     public Type cmp(final MethodVisitor method) {
         method.visitInsn(LCMP);
         return INT;
@@ -121,11 +122,11 @@
         if (to.isNumber()) {
             method.visitInsn(L2D);
         } else if (to.isInteger()) {
-            method.visitInsn(L2I);
+            invokestatic(method, JSType.TO_INT32_L);
         } else if (to.isBoolean()) {
             method.visitInsn(L2I);
         } else if (to.isObject()) {
-            invokeStatic(method, VALUE_OF);
+            invokestatic(method, VALUE_OF);
         } else {
             assert false : "Illegal conversion " + this + " -> " + to;
         }
@@ -134,26 +135,26 @@
     }
 
     @Override
-    public Type add(final MethodVisitor method) {
-        method.visitInsn(LADD);
+    public Type add(final MethodVisitor method, final int programPoint) {
+        method.visitInvokeDynamicInsn("ladd", "(JJ)J", MATHBOOTSTRAP, programPoint);
         return LONG;
     }
 
     @Override
-    public Type sub(final MethodVisitor method) {
-        method.visitInsn(LSUB);
+    public Type sub(final MethodVisitor method, final int programPoint) {
+        method.visitInvokeDynamicInsn("lsub", "(JJ)J", MATHBOOTSTRAP, programPoint);
         return LONG;
     }
 
     @Override
-    public Type mul(final MethodVisitor method) {
-        method.visitInsn(LMUL);
+    public Type mul(final MethodVisitor method, final int programPoint) {
+        method.visitInvokeDynamicInsn("lmul", "(JJ)J", MATHBOOTSTRAP, programPoint);
         return LONG;
     }
 
     @Override
-    public Type div(final MethodVisitor method) {
-        method.visitInsn(LDIV);
+    public Type div(final MethodVisitor method, final int programPoint) {
+        method.visitInvokeDynamicInsn("ldiv", "(JJ)J", MATHBOOTSTRAP, programPoint);
         return LONG;
     }
 
@@ -200,8 +201,8 @@
     }
 
     @Override
-    public Type neg(final MethodVisitor method) {
-        method.visitInsn(LNEG);
+    public Type neg(final MethodVisitor method, final int programPoint) {
+        method.visitInvokeDynamicInsn("lneg", "(J)J", MATHBOOTSTRAP, programPoint);
         return LONG;
     }
 
@@ -212,7 +213,13 @@
 
     @Override
     public Type loadUndefined(final MethodVisitor method) {
-        method.visitLdcInsn(ObjectClassGenerator.UNDEFINED_LONG);
+        method.visitLdcInsn(UNDEFINED_LONG);
+        return LONG;
+    }
+
+    @Override
+    public Type loadForcedInitializer(MethodVisitor method) {
+        method.visitInsn(LCONST_0);
         return LONG;
     }
 
--- a/nashorn/src/jdk/nashorn/internal/codegen/types/NumberType.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/types/NumberType.java	Wed Feb 26 13:17:57 2014 +0100
@@ -39,10 +39,10 @@
 import static jdk.internal.org.objectweb.asm.Opcodes.DSTORE;
 import static jdk.internal.org.objectweb.asm.Opcodes.DSUB;
 import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
+import static jdk.nashorn.internal.runtime.JSType.UNDEFINED_DOUBLE;
 
 import jdk.internal.org.objectweb.asm.MethodVisitor;
 import jdk.nashorn.internal.codegen.CompilerConstants;
-import jdk.nashorn.internal.codegen.ObjectClassGenerator;
 import jdk.nashorn.internal.runtime.JSType;
 
 class NumberType extends NumericType {
@@ -64,6 +64,11 @@
     }
 
     @Override
+    public char getBytecodeStackType() {
+        return 'D';
+    }
+
+    @Override
     public Type cmp(final MethodVisitor method, final boolean isCmpG) {
         method.visitInsn(isCmpG ? DCMPG : DCMPL);
         return INT;
@@ -84,7 +89,13 @@
 
     @Override
     public Type loadUndefined(final MethodVisitor method) {
-        method.visitLdcInsn(ObjectClassGenerator.UNDEFINED_DOUBLE);
+        method.visitLdcInsn(UNDEFINED_DOUBLE);
+        return NUMBER;
+    }
+
+    @Override
+    public Type loadForcedInitializer(MethodVisitor method) {
+        method.visitInsn(DCONST_0);
         return NUMBER;
     }
 
@@ -112,42 +123,42 @@
         }
 
         if (to.isInteger()) {
-            invokeStatic(method, JSType.TO_INT32_D);
+            invokestatic(method, JSType.TO_INT32_D);
         } else if (to.isLong()) {
-            invokeStatic(method, JSType.TO_INT64_D);
+            invokestatic(method, JSType.TO_LONG_D);
         } else if (to.isBoolean()) {
-            invokeStatic(method, JSType.TO_BOOLEAN_D);
+            invokestatic(method, JSType.TO_BOOLEAN_D);
         } else if (to.isString()) {
-            invokeStatic(method, JSType.TO_STRING_D);
+            invokestatic(method, JSType.TO_STRING_D);
         } else if (to.isObject()) {
-            invokeStatic(method, VALUE_OF);
+            invokestatic(method, VALUE_OF);
         } else {
-            assert false : "Illegal conversion " + this + " -> " + to;
+            throw new UnsupportedOperationException("Illegal conversion " + this + " -> " + to);
         }
 
         return to;
     }
 
     @Override
-    public Type add(final MethodVisitor method) {
+    public Type add(final MethodVisitor method, final int programPoint) {
         method.visitInsn(DADD);
         return NUMBER;
     }
 
     @Override
-    public Type sub(final MethodVisitor method) {
+    public Type sub(final MethodVisitor method, final int programPoint) {
         method.visitInsn(DSUB);
         return NUMBER;
     }
 
     @Override
-    public Type mul(final MethodVisitor method) {
+    public Type mul(final MethodVisitor method, final int programPoint) {
         method.visitInsn(DMUL);
         return NUMBER;
     }
 
     @Override
-    public Type div(final MethodVisitor method) {
+    public Type div(final MethodVisitor method, final int programPoint) {
         method.visitInsn(DDIV);
         return NUMBER;
     }
@@ -159,7 +170,7 @@
     }
 
     @Override
-    public Type neg(final MethodVisitor method) {
+    public Type neg(final MethodVisitor method, final int programPoint) {
         method.visitInsn(DNEG);
         return NUMBER;
     }
--- a/nashorn/src/jdk/nashorn/internal/codegen/types/ObjectType.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/types/ObjectType.java	Wed Feb 26 13:17:57 2014 +0100
@@ -65,8 +65,13 @@
     }
 
     @Override
-    public Type add(final MethodVisitor method) {
-        invokeStatic(method, ScriptRuntime.ADD);
+    public String getShortDescriptor() {
+        return getTypeClass() == Object.class ? "Object" : getTypeClass().getSimpleName();
+    }
+
+    @Override
+    public Type add(final MethodVisitor method, final int programPoint) {
+        invokestatic(method, ScriptRuntime.ADD);
         return Type.OBJECT;
     }
 
@@ -74,7 +79,7 @@
     public Type load(final MethodVisitor method, final int slot) {
         assert slot != -1;
         method.visitVarInsn(ALOAD, slot);
-        return Type.OBJECT;
+        return this;
     }
 
     @Override
@@ -86,13 +91,21 @@
     @Override
     public Type loadUndefined(final MethodVisitor method) {
         method.visitFieldInsn(GETSTATIC, className(ScriptRuntime.class), "UNDEFINED", typeDescriptor(Undefined.class));
+        return UNDEFINED;
+    }
+
+    @Override
+    public Type loadForcedInitializer(MethodVisitor method) {
+        method.visitInsn(ACONST_NULL);
+        // TODO: do we need a special type for null, e.g. Type.NULL? It should be assignable to any other object type
+        // without a checkast in convert.
         return OBJECT;
     }
 
     @Override
     public Type loadEmpty(final MethodVisitor method) {
         method.visitFieldInsn(GETSTATIC, className(ScriptRuntime.class), "EMPTY", typeDescriptor(Undefined.class));
-        return OBJECT;
+        return UNDEFINED;
     }
 
     @Override
@@ -108,10 +121,10 @@
             method.visitLdcInsn(c);
             return Type.typeFor(MethodHandle.class);
         } else {
-            assert false : "implementation missing for class " + c.getClass() + " value=" + c;
+            throw new UnsupportedOperationException("implementation missing for class " + c.getClass() + " value=" + c);
         }
 
-        return OBJECT;
+        return Type.OBJECT;
     }
 
     @Override
@@ -138,6 +151,10 @@
                 }
                 return to;
             } else if (to.isObject()) {
+                final Class<?> toClass = to.getTypeClass();
+                if(!toClass.isAssignableFrom(getTypeClass())) {
+                    method.visitTypeInsn(CHECKCAST, CompilerConstants.className(toClass));
+                }
                 return to;
             }
         } else if (isString()) {
@@ -145,17 +162,17 @@
         }
 
         if (to.isInteger()) {
-            invokeStatic(method, JSType.TO_INT32);
+            invokestatic(method, JSType.TO_INT32);
         } else if (to.isNumber()) {
-            invokeStatic(method, JSType.TO_NUMBER);
+            invokestatic(method, JSType.TO_NUMBER);
         } else if (to.isLong()) {
-            invokeStatic(method, JSType.TO_INT64);
+            invokestatic(method, JSType.TO_LONG);
         } else if (to.isBoolean()) {
-            invokeStatic(method, JSType.TO_BOOLEAN);
+            invokestatic(method, JSType.TO_BOOLEAN);
         } else if (to.isString()) {
-            invokeStatic(method, JSType.TO_PRIMITIVE_TO_STRING);
+            invokestatic(method, JSType.TO_PRIMITIVE_TO_STRING);
         } else {
-            assert false : "Illegal conversion " + this + " -> " + to + " " + isString() + " " + toString;
+            throw new UnsupportedOperationException("Illegal conversion " + this + " -> " + to + " " + isString() + " " + toString);
         }
 
         return to;
@@ -165,4 +182,9 @@
     public void _return(final MethodVisitor method) {
         method.visitInsn(ARETURN);
     }
+
+    @Override
+    public char getBytecodeStackType() {
+        return 'A';
+    }
 }
--- a/nashorn/src/jdk/nashorn/internal/codegen/types/Type.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/types/Type.java	Wed Feb 26 13:17:57 2014 +0100
@@ -33,6 +33,7 @@
 import static jdk.internal.org.objectweb.asm.Opcodes.DUP2_X2;
 import static jdk.internal.org.objectweb.asm.Opcodes.DUP_X1;
 import static jdk.internal.org.objectweb.asm.Opcodes.DUP_X2;
+import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKESTATIC;
 import static jdk.internal.org.objectweb.asm.Opcodes.IALOAD;
 import static jdk.internal.org.objectweb.asm.Opcodes.IASTORE;
 import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESTATIC;
@@ -45,13 +46,20 @@
 import static jdk.internal.org.objectweb.asm.Opcodes.T_DOUBLE;
 import static jdk.internal.org.objectweb.asm.Opcodes.T_INT;
 import static jdk.internal.org.objectweb.asm.Opcodes.T_LONG;
+import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
 
+import java.lang.invoke.CallSite;
 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 jdk.internal.org.objectweb.asm.Handle;
 import jdk.internal.org.objectweb.asm.MethodVisitor;
 import jdk.nashorn.internal.codegen.CompilerConstants.Call;
-
+import jdk.nashorn.internal.runtime.Undefined;
+import jdk.nashorn.internal.runtime.linker.Bootstrap;
 
 /**
  * This is the representation of a JavaScript type, disassociated from java
@@ -97,6 +105,10 @@
     /** Set way below Integer.MAX_VALUE to prevent overflow when adding weights. Objects are still heaviest. */
     protected static final int MAX_WEIGHT = 20;
 
+    static final Call BOOTSTRAP = staticCallNoLookup(Bootstrap.class, "mathBootstrap", CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, int.class);
+
+    static final Handle MATHBOOTSTRAP = new Handle(H_INVOKESTATIC, BOOTSTRAP.className(), "mathBootstrap", BOOTSTRAP.descriptor());
+
     /**
      * Constructor
      *
@@ -149,6 +161,17 @@
     }
 
     /**
+     * Returns the character describing the bytecode type for this value on the stack or local variable, identical to
+     * what would be used as the prefix for a bytecode {@code LOAD} or {@code STORE} instruction, therefore it must be
+     * one of {@code A, F, D, I, L}. Also, the special value {@code U} is used for local variable slots that haven't
+     * been initialized yet (it can't appear for a value pushed to the operand stack, those always have known values).
+     * Note that while we allow all JVM internal types, Nashorn doesn't necessarily use them all - currently we don't
+     * have floats, only doubles, but that might change in the future.
+     * @return the character describing the bytecode type for this value on the stack.
+     */
+    public abstract char getBytecodeStackType();
+
+    /**
      * Generate a method descriptor given a return type and a param array
      *
      * @param returnType return type
@@ -199,7 +222,11 @@
         case jdk.internal.org.objectweb.asm.Type.DOUBLE:
             return NUMBER;
         case jdk.internal.org.objectweb.asm.Type.OBJECT:
-            return OBJECT;
+            try {
+                return Type.typeFor(Class.forName(itype.getClassName()));
+            } catch(ClassNotFoundException e) {
+                throw new AssertionError(e);
+            }
         case jdk.internal.org.objectweb.asm.Type.VOID:
             return null;
         case jdk.internal.org.objectweb.asm.Type.ARRAY:
@@ -260,7 +287,7 @@
         return jdk.internal.org.objectweb.asm.Type.getType(type);
     }
 
-    static void invokeStatic(final MethodVisitor method, final Call call) {
+    static void invokestatic(final MethodVisitor method, final Call call) {
         method.visitMethodInsn(INVOKESTATIC, call.className(), call.name(), call.descriptor(), false);
     }
 
@@ -373,6 +400,14 @@
     }
 
     /**
+     * Is this a primitive type (e.g int, long, double, boolean)
+     * @return true if primitive
+     */
+    public boolean isPrimitive() {
+        return !isObject();
+    }
+
+    /**
      * Determines whether a type is a STRING type
      *
      * @return true if object type, false otherwise
@@ -470,7 +505,25 @@
      * @return the widest type
      */
     public static Type narrowest(final Type type0, final Type type1) {
-        return type0.weight() < type1.weight() ? type0 : type1;
+        return type0.narrowerThan(type1) ? type0 : type1;
+    }
+
+    /**
+     * Check whether this type is strictly narrower than another one
+     * @param type type to check against
+     * @return true if this type is strictly narrower
+     */
+    public boolean narrowerThan(final Type type) {
+        return weight() < type.weight();
+    }
+
+    /**
+     * Check whether this type is strictly wider than another one
+     * @param type type to check against
+     * @return true if this type is strictly wider
+     */
+    public boolean widerThan(final Type type) {
+        return weight() > type.weight();
     }
 
     /**
@@ -549,6 +602,16 @@
         return descriptor;
     }
 
+    /**
+     * Return the descriptor of a type, short version
+     * Used mainly for debugging purposes
+     *
+     * @return the short descriptor
+     */
+    public String getShortDescriptor() {
+        return descriptor;
+    }
+
     @Override
     public String toString() {
         return name;
@@ -711,6 +774,11 @@
     public static final Type OBJECT = putInCache(new ObjectType());
 
     /**
+     * A undefined singleton
+     */
+    public static final Type UNDEFINED = putInCache(new ObjectType(Undefined.class));
+
+    /**
      * This is the singleton for integer arrays
      */
     public static final ArrayType INT_ARRAY = new ArrayType(int[].class) {
@@ -820,55 +888,83 @@
         // EMPTY - used as a class that is absolutely not compatible with a type to represent "unknown"
     }
 
+    private abstract static class ValueLessType extends Type {
+
+        ValueLessType(final String name) {
+            super(name, Unknown.class, MIN_WEIGHT, 1);
+        }
+
+        @Override
+        public Type load(final MethodVisitor method, final int slot) {
+            throw new UnsupportedOperationException("load " + slot);
+        }
+
+        @Override
+        public void store(final MethodVisitor method, final int slot) {
+            throw new UnsupportedOperationException("store " + slot);
+        }
+
+        @Override
+        public Type ldc(final MethodVisitor method, final Object c) {
+            throw new UnsupportedOperationException("ldc " + c);
+        }
+
+        @Override
+        public Type loadUndefined(final MethodVisitor method) {
+            throw new UnsupportedOperationException("load undefined");
+        }
+
+        @Override
+        public Type loadForcedInitializer(final MethodVisitor method) {
+            throw new UnsupportedOperationException("load forced initializer");
+        }
+
+        @Override
+        public Type convert(final MethodVisitor method, final Type to) {
+            throw new UnsupportedOperationException("convert => " + to);
+        }
+
+        @Override
+        public void _return(final MethodVisitor method) {
+            throw new UnsupportedOperationException("return");
+       }
+
+        @Override
+        public Type add(final MethodVisitor method, final int programPoint) {
+            throw new UnsupportedOperationException("add");
+        }
+    }
+
     /**
      * This is the unknown type which is used as initial type for type
      * inference. It has the minimum type width
      */
-    public static final Type UNKNOWN = new Type("<unknown>", Unknown.class, MIN_WEIGHT, 1) {
-
+    public static final Type UNKNOWN = new ValueLessType("<unknown>") {
         @Override
         public String getDescriptor() {
             return "<unknown>";
         }
 
         @Override
-        public Type load(final MethodVisitor method, final int slot) {
-            assert false : "unsupported operation";
-            return null;
+        public char getBytecodeStackType() {
+            return 'U';
         }
+    };
+
+    /**
+     * This is the unknown type which is used as initial type for type
+     * inference. It has the minimum type width
+     */
+    public static final Type SLOT_2 = new ValueLessType("<slot_2>") {
 
         @Override
-        public void store(final MethodVisitor method, final int slot) {
-            assert false : "unsupported operation";
-        }
-
-        @Override
-        public Type ldc(final MethodVisitor method, final Object c) {
-            assert false : "unsupported operation";
-            return null;
+        public String getDescriptor() {
+            return "<slot_2>";
         }
 
         @Override
-        public Type loadUndefined(final MethodVisitor method) {
-            assert false : "unsupported operation";
-            return null;
-        }
-
-        @Override
-        public Type convert(final MethodVisitor method, final Type to) {
-            assert false : "unsupported operation";
-            return null;
-        }
-
-        @Override
-        public void _return(final MethodVisitor method) {
-            assert false : "unsupported operation";
-        }
-
-        @Override
-        public Type add(final MethodVisitor method) {
-            assert false : "unsupported operation";
-            return null;
+        public char getBytecodeStackType() {
+            throw new UnsupportedOperationException("getBytecodeStackType");
         }
     };
 
--- a/nashorn/src/jdk/nashorn/internal/ir/AccessNode.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/ir/AccessNode.java	Wed Feb 26 13:17:57 2014 +0100
@@ -25,6 +25,10 @@
 
 package jdk.nashorn.internal.ir;
 
+import static jdk.nashorn.internal.codegen.ObjectClassGenerator.DEBUG_FIELDS;
+
+import jdk.nashorn.internal.codegen.ObjectClassGenerator;
+import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.annotations.Immutable;
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
 
@@ -49,8 +53,8 @@
         this.property = property.setIsPropertyName();
     }
 
-    private AccessNode(final AccessNode accessNode, final Expression base, final IdentNode property, final boolean isFunction) {
-        super(accessNode, base, isFunction);
+    private AccessNode(final AccessNode accessNode, final Expression base, final IdentNode property, final boolean isFunction, final Type optimisticType, final boolean isOptimistic, final int id) {
+        super(accessNode, base, isFunction, optimisticType, isOptimistic, id);
         this.property = property;
     }
 
@@ -72,6 +76,8 @@
     public void toString(final StringBuilder sb) {
         final boolean needsParen = tokenType().needsParens(getBase().tokenType(), true);
 
+        Node.optimisticType(this, sb);
+
         if (needsParen) {
             sb.append('(');
         }
@@ -99,22 +105,49 @@
         if (this.base == base) {
             return this;
         }
-        return new AccessNode(this, base, property, isFunction());
+        return new AccessNode(this, base, property, isFunction(), optimisticType, isOptimistic, programPoint);
     }
 
     private AccessNode setProperty(final IdentNode property) {
         if (this.property == property) {
             return this;
         }
-        return new AccessNode(this, base, property, isFunction());
+        return new AccessNode(this, base, property, isFunction(), optimisticType, isOptimistic, programPoint);
+    }
+
+    @Override
+    public AccessNode setType(final TemporarySymbols ts, final Type optimisticType) {
+        if (this.optimisticType == optimisticType) {
+            return this;
+        }
+        if (DEBUG_FIELDS && getSymbol() != null && !Type.areEquivalent(getSymbol().getSymbolType(), optimisticType)) {
+            ObjectClassGenerator.LOG.info(getClass().getName(), " ", this, " => ", optimisticType, " instead of ", getType());
+        }
+        return new AccessNode(this, base, property, isFunction(), optimisticType, isOptimistic, programPoint);
     }
 
     @Override
-    public BaseNode setIsFunction() {
+    public AccessNode setProgramPoint(int programPoint) {
+        if (this.programPoint == programPoint) {
+            return this;
+        }
+        return new AccessNode(this, base, property, isFunction(), optimisticType, isOptimistic, programPoint);
+    }
+
+    @Override
+    public AccessNode setIsFunction() {
         if (isFunction()) {
             return this;
         }
-        return new AccessNode(this, base, property, true);
+        return new AccessNode(this, base, property, true, optimisticType, isOptimistic, programPoint);
+    }
+
+    @Override
+    public AccessNode setIsOptimistic(final boolean isOptimistic) {
+        if (this.isOptimistic == isOptimistic) {
+            return this;
+        }
+        return new AccessNode(this, base, property, isFunction(), optimisticType, isOptimistic, programPoint);
     }
 
 }
--- a/nashorn/src/jdk/nashorn/internal/ir/BaseNode.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/ir/BaseNode.java	Wed Feb 26 13:17:57 2014 +0100
@@ -25,6 +25,9 @@
 
 package jdk.nashorn.internal.ir;
 
+import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
+
+import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.annotations.Immutable;
 
 /**
@@ -34,13 +37,22 @@
  * @see IndexNode
  */
 @Immutable
-public abstract class BaseNode extends Expression implements FunctionCall {
+public abstract class BaseNode extends Expression implements FunctionCall, Optimistic {
 
     /** Base Node. */
     protected final Expression base;
 
     private final boolean isFunction;
 
+    /** Callsite type for this node, if overriden optimistically or conservatively depending on coercion */
+    protected final Type optimisticType;
+
+    /** Does this node have a callsite type, and it is optimistic rather than inferred from coercion semantics */
+    protected final boolean isOptimistic;
+
+    /** Program point id */
+    protected final int programPoint;
+
     /**
      * Constructor
      *
@@ -51,8 +63,11 @@
      */
     public BaseNode(final long token, final int finish, final Expression base, final boolean isFunction) {
         super(token, base.getStart(), finish);
-        this.base            = base;
-        this.isFunction      = isFunction;
+        this.base           = base;
+        this.isFunction     = isFunction;
+        this.optimisticType = null;
+        this.programPoint   = INVALID_PROGRAM_POINT;
+        this.isOptimistic   = false;
     }
 
     /**
@@ -60,11 +75,17 @@
      * @param baseNode node to inherit from
      * @param base base
      * @param isFunction is this a function
+     * @param callSiteType  the callsite type for this base node, either optimistic or conservative
+     * @param isOptimistic  is the callsite type optimistic rather than based on statically known coercion semantics
+     * @param programPoint  program point id
      */
-    protected BaseNode(final BaseNode baseNode, final Expression base, final boolean isFunction) {
+    protected BaseNode(final BaseNode baseNode, final Expression base, final boolean isFunction, final Type callSiteType, final boolean isOptimistic, final int programPoint) {
         super(baseNode);
-        this.base            = base;
-        this.isFunction      = isFunction;
+        this.base           = base;
+        this.isFunction     = isFunction;
+        this.optimisticType = callSiteType;
+        this.programPoint   = programPoint;
+        this.isOptimistic   = isOptimistic;
     }
 
     /**
@@ -80,6 +101,37 @@
         return isFunction;
     }
 
+    @Override
+    public final Type getType() {
+        return optimisticType == null ? super.getType() : optimisticType;
+    }
+
+    @Override
+    public int getProgramPoint() {
+        return programPoint;
+    }
+
+    @Override
+    public Type getMostOptimisticType() {
+        return Type.INT;
+    }
+
+    @Override
+    public Type getMostPessimisticType() {
+        return Type.OBJECT;
+    }
+
+    @Override
+    public boolean canBeOptimistic() {
+        return true;
+    }
+
+    @Override
+    public boolean isOptimistic() {
+        return isOptimistic;
+    }
+
+
     /**
      * Mark this node as being the callee operand of a {@link CallNode}.
      * @return a base node identical to this one in all aspects except with its function flag set.
--- a/nashorn/src/jdk/nashorn/internal/ir/BinaryNode.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/ir/BinaryNode.java	Wed Feb 26 13:17:57 2014 +0100
@@ -25,7 +25,13 @@
 
 package jdk.nashorn.internal.ir;
 
+import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
 import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.ir.annotations.Ignore;
 import jdk.nashorn.internal.ir.annotations.Immutable;
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
 import jdk.nashorn.internal.parser.TokenType;
@@ -34,12 +40,34 @@
  * BinaryNode nodes represent two operand operations.
  */
 @Immutable
-public final class BinaryNode extends Expression implements Assignment<Expression> {
+public final class BinaryNode extends Expression implements Assignment<Expression>, Optimistic {
     /** Left hand side argument. */
     private final Expression lhs;
 
     private final Expression rhs;
 
+    private final int programPoint;
+
+    private final boolean isOptimistic;
+
+    private final Type type;
+
+    @Ignore
+    private static final List<TokenType> CAN_OVERFLOW =
+        Collections.unmodifiableList(
+            Arrays.asList(new TokenType[] {
+                TokenType.ADD,
+                TokenType.DIV,
+                TokenType.MOD,
+                TokenType.MUL,
+                TokenType.SUB,
+                TokenType.ASSIGN_ADD,
+                TokenType.ASSIGN_DIV,
+                TokenType.ASSIGN_MOD,
+                TokenType.ASSIGN_MUL,
+                TokenType.ASSIGN_SUB
+            }));
+
     /**
      * Constructor
      *
@@ -51,12 +79,18 @@
         super(token, lhs.getStart(), rhs.getFinish());
         this.lhs   = lhs;
         this.rhs   = rhs;
+        this.programPoint = INVALID_PROGRAM_POINT;
+        this.isOptimistic = false;
+        this.type = null;
     }
 
-    private BinaryNode(final BinaryNode binaryNode, final Expression lhs, final Expression rhs) {
+    private BinaryNode(final BinaryNode binaryNode, final Expression lhs, final Expression rhs, final Type type, final int programPoint, final boolean isOptimistic) {
         super(binaryNode);
         this.lhs = lhs;
         this.rhs = rhs;
+        this.programPoint = programPoint;
+        this.isOptimistic = isOptimistic;
+        this.type = type;
     }
 
     @Override
@@ -109,6 +143,9 @@
         case ASSIGN_SUB:
             return Type.NUMBER;
         default:
+            if (isComparison()) {
+                return Type.BOOLEAN;
+            }
             return Type.OBJECT;
         }
     }
@@ -210,10 +247,10 @@
 
     @Override
     public void toString(final StringBuilder sb) {
-        final TokenType type = tokenType();
+        final TokenType tokenType = tokenType();
 
-        final boolean lhsParen = type.needsParens(lhs().tokenType(), true);
-        final boolean rhsParen = type.needsParens(rhs().tokenType(), false);
+        final boolean lhsParen = tokenType.needsParens(lhs().tokenType(), true);
+        final boolean rhsParen = tokenType.needsParens(rhs().tokenType(), false);
 
         if (lhsParen) {
             sb.append('(');
@@ -227,7 +264,7 @@
 
         sb.append(' ');
 
-        switch (type) {
+        switch (tokenType) {
         case COMMALEFT:
             sb.append(",<");
             break;
@@ -239,10 +276,14 @@
             sb.append("++");
             break;
         default:
-            sb.append(type.getName());
+            sb.append(tokenType.getName());
             break;
         }
 
+        if (isOptimistic()) {
+            sb.append(Node.OPT_IDENTIFIER);
+        }
+
         sb.append(' ');
 
         if (rhsParen) {
@@ -279,7 +320,7 @@
         if (this.lhs == lhs) {
             return this;
         }
-        return new BinaryNode(this, lhs, rhs);
+        return new BinaryNode(this, lhs, rhs, type, programPoint, isOptimistic);
     }
 
     /**
@@ -291,7 +332,64 @@
         if (this.rhs == rhs) {
             return this;
         }
-        return new BinaryNode(this, lhs, rhs);
+        return new BinaryNode(this, lhs, rhs, type, programPoint, isOptimistic);
+    }
+
+    @Override
+    public int getProgramPoint() {
+        return programPoint;
+    }
+
+    @Override
+    public boolean canBeOptimistic() {
+        return getMostOptimisticType() != getMostPessimisticType();
+    }
+
+    @Override
+    public BinaryNode setProgramPoint(final int programPoint) {
+        if (this.programPoint == programPoint) {
+            return this;
+        }
+        return new BinaryNode(this, lhs, rhs, type, programPoint, isOptimistic);
+    }
+
+    @Override
+    public BinaryNode setIsOptimistic(final boolean isOptimistic) {
+        if (this.isOptimistic == isOptimistic) {
+            return this;
+        }
+        assert isOptimistic;
+        return new BinaryNode(this, lhs, rhs, type, programPoint, isOptimistic);
     }
 
+    @Override
+    public Type getMostOptimisticType() {
+        if (CAN_OVERFLOW.contains(tokenType())) {
+            return Type.widest(Type.INT, Type.widest(lhs.getType(), rhs.getType()));
+        }
+        return getMostPessimisticType();
+    }
+
+    @Override
+    public Type getMostPessimisticType() {
+        return getWidestOperationType();
+    }
+
+    @Override
+    public boolean isOptimistic() {
+        return isOptimistic;
+    }
+
+    @Override
+    public Type getType() {
+        return type == null ? super.getType() : type;
+    }
+
+    @Override
+    public BinaryNode setType(TemporarySymbols ts, Type type) {
+        if (this.type == type) {
+            return this;
+        }
+        return new BinaryNode(this, lhs, rhs, type, programPoint, isOptimistic);
+    }
 }
--- a/nashorn/src/jdk/nashorn/internal/ir/Block.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/ir/Block.java	Wed Feb 26 13:17:57 2014 +0100
@@ -25,6 +25,8 @@
 
 package jdk.nashorn.internal.ir;
 
+import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
+
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -33,14 +35,11 @@
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
-
 import jdk.nashorn.internal.codegen.Label;
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.annotations.Immutable;
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
 
-import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
-
 /**
  * IR representation for a list of statements.
  */
@@ -53,7 +52,7 @@
     protected final Map<String, Symbol> symbols;
 
     /** Entry label. */
-    protected final Label entryLabel;
+    private final Label entryLabel;
 
     /** Break label. */
     private final Label breakLabel;
@@ -65,13 +64,11 @@
     public static final int NEEDS_SCOPE = 1 << 0;
 
     /**
-     * Flag indicating whether this block needs
-     * self symbol assignment at the start. This is used only for
-     * blocks that are the bodies of function nodes who refer to themselves
-     * by name. It causes codegen to insert a var [fn_name] = __callee__
+     * Flag indicating whether this block uses the self symbol for the function. This is used only for blocks that are
+     * bodies of function nodes who refer to themselves by name. It causes Attr to insert a var [fn_name] = __callee__
      * at the start of the body
      */
-    public static final int NEEDS_SELF_SYMBOL = 1 << 1;
+    public static final int USES_SELF_SYMBOL = 1 << 1;
 
     /**
      * Is this block tagged as terminal based on its contents
--- a/nashorn/src/jdk/nashorn/internal/ir/BlockStatement.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/ir/BlockStatement.java	Wed Feb 26 13:17:57 2014 +0100
@@ -58,7 +58,7 @@
      * @return a block statement with the new statements. It will have the line number, token, and finish of the
      * original statement.
      */
-    public static Statement createReplacement(final Statement stmt, final List<Statement> newStmts) {
+    public static BlockStatement createReplacement(final Statement stmt, final List<Statement> newStmts) {
         return createReplacement(stmt, stmt.getFinish(), newStmts);
     }
 
@@ -70,7 +70,7 @@
      * @return a block statement with the new statements. It will have the line number, and token of the
      * original statement.
      */
-    public static Statement createReplacement(final Statement stmt, int finish, final List<Statement> newStmts) {
+    public static BlockStatement createReplacement(final Statement stmt, int finish, final List<Statement> newStmts) {
         return new BlockStatement(stmt.getLineNumber(), new Block(stmt.getToken(), finish, newStmts));
     }
 
--- a/nashorn/src/jdk/nashorn/internal/ir/CallNode.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/ir/CallNode.java	Wed Feb 26 13:17:57 2014 +0100
@@ -32,11 +32,13 @@
 import jdk.nashorn.internal.ir.annotations.Immutable;
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
 
+import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
+
 /**
  * IR representation for a function call.
  */
 @Immutable
-public final class CallNode extends LexicalContextExpression {
+public final class CallNode extends LexicalContextExpression implements Optimistic {
 
     /** Function identifier or function body. */
     private final Expression function;
@@ -45,12 +47,19 @@
     private final List<Expression> args;
 
     /** Is this a "new" operation */
-    public static final int IS_NEW        = 0x1;
+    public static final int IS_NEW =     1 << 0;
+
+    /** Is the callsite type for this call optimistic rather than based on statically known coercion semantics */
+    public static final int OPTIMISTIC = 1 << 1;
 
     private final int flags;
 
     private final int lineNumber;
 
+    private final int programPoint;
+
+    private final Type optimisticType;
+
     /**
      * Arguments to be passed to builtin {@code eval} function
      */
@@ -145,20 +154,24 @@
     public CallNode(final int lineNumber, final long token, final int finish, final Expression function, final List<Expression> args) {
         super(token, finish);
 
-        this.function   = function;
-        this.args       = args;
-        this.flags      = 0;
-        this.evalArgs   = null;
-        this.lineNumber = lineNumber;
+        this.function       = function;
+        this.args           = args;
+        this.flags          = 0;
+        this.evalArgs       = null;
+        this.lineNumber     = lineNumber;
+        this.programPoint   = INVALID_PROGRAM_POINT;
+        this.optimisticType = null;
     }
 
-    private CallNode(final CallNode callNode, final Expression function, final List<Expression> args, final int flags, final EvalArgs evalArgs) {
+    private CallNode(final CallNode callNode, final Expression function, final List<Expression> args, final int flags, final Type optimisticType, final EvalArgs evalArgs, final int programPoint) {
         super(callNode);
         this.lineNumber = callNode.lineNumber;
         this.function = function;
         this.args = args;
         this.flags = flags;
         this.evalArgs = evalArgs;
+        this.programPoint = programPoint;
+        this.optimisticType = optimisticType;
     }
 
     /**
@@ -171,7 +184,15 @@
 
     @Override
     public Type getType() {
-        return function instanceof FunctionNode ? ((FunctionNode)function).getReturnType() : Type.OBJECT;
+        return optimisticType == null ? super.getType() : optimisticType;
+    }
+
+    @Override
+    public Optimistic setType(TemporarySymbols ts, Type optimisticType) {
+        if (this.optimisticType == optimisticType) {
+            return this;
+        }
+        return new CallNode(this, function, args, flags, optimisticType, evalArgs, programPoint);
     }
 
     /**
@@ -187,7 +208,6 @@
             final CallNode newCallNode = (CallNode)visitor.leaveCallNode(
                     setFunction((Expression)function.accept(visitor)).
                     setArgs(Node.accept(visitor, Expression.class, args)).
-                    setFlags(flags).
                     setEvalArgs(evalArgs == null ?
                             null :
                             evalArgs.setCode((Expression)evalArgs.getCode().accept(visitor)).
@@ -204,6 +224,7 @@
 
     @Override
     public void toString(final StringBuilder sb) {
+        Node.optimisticType(this, sb);
         function.toString(sb);
 
         sb.append('(');
@@ -239,7 +260,7 @@
         if (this.args == args) {
             return this;
         }
-        return new CallNode(this, function, args, flags, evalArgs);
+        return new CallNode(this, function, args, flags, optimisticType, evalArgs, programPoint);
     }
 
     /**
@@ -261,7 +282,7 @@
         if (this.evalArgs == evalArgs) {
             return this;
         }
-        return new CallNode(this, function, args, flags, evalArgs);
+        return new CallNode(this, function, args, flags, optimisticType, evalArgs, programPoint);
     }
 
     /**
@@ -289,7 +310,7 @@
         if (this.function == function) {
             return this;
         }
-        return new CallNode(this, function, args, flags, evalArgs);
+        return new CallNode(this, function, args, flags, optimisticType, evalArgs, programPoint);
     }
 
     /**
@@ -312,6 +333,47 @@
         if (this.flags == flags) {
             return this;
         }
-        return new CallNode(this, function, args, flags, evalArgs);
+        return new CallNode(this, function, args, flags, optimisticType, evalArgs, programPoint);
+    }
+
+    @Override
+    public int getProgramPoint() {
+        return programPoint;
+    }
+
+    @Override
+    public CallNode setProgramPoint(final int programPoint) {
+        if (this.programPoint == programPoint) {
+            return this;
+        }
+        return new CallNode(this, function, args, flags, optimisticType, evalArgs, programPoint);
+    }
+
+    @Override
+    public Type getMostOptimisticType() {
+        return Type.INT;
+    }
+
+    @Override
+    public Type getMostPessimisticType() {
+        return Type.OBJECT;
+    }
+
+    @Override
+    public boolean canBeOptimistic() {
+        return true;
+    }
+
+    @Override
+    public boolean isOptimistic() {
+        return (flags & OPTIMISTIC) == OPTIMISTIC;
+    }
+
+    @Override
+    public Optimistic setIsOptimistic(final boolean isOptimistic) {
+        if (isOptimistic() == isOptimistic) {
+            return this;
+        }
+        return new CallNode(this, function, args, isOptimistic ? (flags | OPTIMISTIC) : (flags & ~OPTIMISTIC), optimisticType, evalArgs, programPoint);
     }
 }
--- a/nashorn/src/jdk/nashorn/internal/ir/CatchNode.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/ir/CatchNode.java	Wed Feb 26 13:17:57 2014 +0100
@@ -60,7 +60,7 @@
      */
     public CatchNode(final int lineNumber, final long token, final int finish, final IdentNode exception, final Expression exceptionCondition, final Block body, final int flags) {
         super(lineNumber, token, finish);
-        this.exception          = exception;
+        this.exception          = exception == null ? null : exception.setIsInitializedHere();
         this.exceptionCondition = exceptionCondition;
         this.body               = body;
         this.flags              = flags;
--- a/nashorn/src/jdk/nashorn/internal/ir/Expression.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/ir/Expression.java	Wed Feb 26 13:17:57 2014 +0100
@@ -35,15 +35,15 @@
 public abstract class Expression extends Node {
     private Symbol symbol;
 
-    Expression(long token, int start, int finish) {
+    Expression(final long token, final int start, final int finish) {
         super(token, start, finish);
     }
 
-    Expression(long token, int finish) {
+    Expression(final long token, final int finish) {
         super(token, finish);
     }
 
-    Expression(Expression expr) {
+    Expression(final Expression expr) {
         super(expr);
         this.symbol = expr.symbol;
     }
--- a/nashorn/src/jdk/nashorn/internal/ir/FunctionCall.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/ir/FunctionCall.java	Wed Feb 26 13:17:57 2014 +0100
@@ -26,14 +26,18 @@
 package jdk.nashorn.internal.ir;
 
 /**
- * Interface used by AccessNodes, IndexNodes and IdentNodes to signal
- * that they are function calls
+ * Interface used by AccessNodes, IndexNodes and IdentNodes to signal that when evaluated, their value will be treated
+ * as a function and immediately invoked, e.g. {@code foo()}, {@code foo.bar()} or {@code foo[bar]()}. Used to customize
+ * the priority of composite dynamic operations when emitting {@code INVOKEDYNAMIC} instructions that implement them,
+ * namely prioritize {@code getMethod} over {@code getElem} or {@code getProp}. An access or ident node with isFunction
+ * set to true will be emitted as {@code dyn:getMethod|getProp|getElem} while one with it set to false will be emitted
+ * as {@code dyn:getProp|getElem|getMethod}. Similarly, an index node with isFunction set to true will be emitted as
+ * {@code dyn:getMethod|getElem|getProp} while the one set to false will be emitted as {@code dyn:getElem|getProp|getMethod}.
  */
 public interface FunctionCall {
     /**
-     * Return true if this function call implementor is a function
-     *
-     * @return true if implements a function call
+     * Returns true if the value of this expression will be treated as a function and immediately invoked.
+     * @return true if the value of this expression will be treated as a function and immediately invoked.
      */
     public boolean isFunction();
 }
--- a/nashorn/src/jdk/nashorn/internal/ir/FunctionNode.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/ir/FunctionNode.java	Wed Feb 26 13:17:57 2014 +0100
@@ -28,6 +28,7 @@
 import java.util.Collections;
 import java.util.EnumSet;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
@@ -89,14 +90,13 @@
     /** Source of entity. */
     private final Source source;
 
+    /** Unique ID used for recompilation among other things */
+    private final int id;
+
     /** External function identifier. */
     @Ignore
     private final IdentNode ident;
 
-    /** Parsed version of functionNode */
-    @Ignore
-    private final FunctionNode snapshot;
-
     /** The body of the function node */
     private final Block body;
 
@@ -129,9 +129,6 @@
     @Ignore
     private final EnumSet<CompilationState> compilationState;
 
-    @Ignore
-    private final Compiler.Hints hints;
-
     /** Properties of this object assigned in this function */
     @Ignore
     private HashSet<String> thisProperties;
@@ -156,7 +153,7 @@
     /** Does the function use the "arguments" identifier ? */
     public static final int USES_ARGUMENTS              = 1 << 3;
 
-    /** Has this node been split because it was too large? */
+    /** Has this function been split because it was too large? */
     public static final int IS_SPLIT                    = 1 << 4;
 
     /** Does the function call eval? If it does, then all variables in this function might be get/set by it and it can
@@ -164,11 +161,11 @@
     public static final int HAS_EVAL                    = 1 << 5;
 
     /** Does a nested function contain eval? If it does, then all variables in this function might be get/set by it. */
-    public static final int HAS_NESTED_EVAL = 1 << 6;
+    public static final int HAS_NESTED_EVAL             = 1 << 6;
 
     /** Does this function have any blocks that create a scope? This is used to determine if the function needs to
      * have a local variable slot for the scope symbol. */
-    public static final int HAS_SCOPE_BLOCK = 1 << 7;
+    public static final int HAS_SCOPE_BLOCK             = 1 << 7;
 
     /**
      * Flag this function as one that defines the identifier "arguments" as a function parameter or nested function
@@ -182,33 +179,43 @@
     /** Does this function or any of its descendants use variables from an ancestor function's scope (incl. globals)? */
     public static final int USES_ANCESTOR_SCOPE         = 1 << 9;
 
-    /** Is this function lazily compiled? */
-    public static final int IS_LAZY                     = 1 << 10;
-
-    /** Does this function have lazy, yet uncompiled children */
-    public static final int HAS_LAZY_CHILDREN           = 1 << 11;
-
-    /** Does this function have lazy, yet uncompiled children */
-    public static final int IS_PROGRAM                  = 1 << 12;
-
     /** Does this function have nested declarations? */
-    public static final int HAS_FUNCTION_DECLARATIONS   = 1 << 13;
+    public static final int HAS_FUNCTION_DECLARATIONS   = 1 << 10;
 
     /** Can this function be specialized? */
-    public static final int CAN_SPECIALIZE              = 1 << 14;
+    public static final int CAN_SPECIALIZE              = 1 << 11;
+
+    /** Does this function have optimistic expressions? */
+    public static final int IS_OPTIMISTIC               = 1 << 12;
+
+    /** Does this function explicitly use the {@link CompilerConstants#RETURN} symbol? Some functions are known to
+     * always use the return symbol, namely a function that is a program (as it must track its last executed expression
+     * statement's value) as well as functions that are split (to communicate return values from inner to outer
+     * partitions). Other functions normally don't use the return symbol (so we optimize away its slot), except in some
+     * very special cases, e.g. when containing a return statement in a finally block. These special cases set this
+     * flag. */
+    public static final int USES_RETURN_SYMBOL = 1 << 13;
+
+    /**
+     * Is this function the top-level program?
+     */
+    public static final int IS_PROGRAM = 1 << 14;
 
     /** Does this function or any nested functions contain an eval? */
     private static final int HAS_DEEP_EVAL = HAS_EVAL | HAS_NESTED_EVAL;
 
     /** Does this function need to store all its variables in scope? */
-    private static final int HAS_ALL_VARS_IN_SCOPE = HAS_DEEP_EVAL | IS_SPLIT | HAS_LAZY_CHILDREN;
+    private static final int HAS_ALL_VARS_IN_SCOPE = HAS_DEEP_EVAL;
 
     /** Does this function potentially need "arguments"? Note that this is not a full test, as further negative check of REDEFINES_ARGS is needed. */
     private static final int MAYBE_NEEDS_ARGUMENTS = USES_ARGUMENTS | HAS_EVAL;
 
     /** Does this function need the parent scope? It needs it if either it or its descendants use variables from it, or have a deep eval.
      *  We also pessimistically need a parent scope if we have lazy children that have not yet been compiled */
-    private static final int NEEDS_PARENT_SCOPE = USES_ANCESTOR_SCOPE | HAS_DEEP_EVAL | HAS_LAZY_CHILDREN;
+    private static final int NEEDS_PARENT_SCOPE = USES_ANCESTOR_SCOPE | HAS_DEEP_EVAL;
+
+    /** Where to start assigning global and unique function node ids */
+    public static final int FIRST_FUNCTION_ID = 1;
 
     /** What is the return type of this function? */
     private Type returnType = Type.UNKNOWN;
@@ -217,6 +224,7 @@
      * Constructor
      *
      * @param source     the source
+     * @param id         unique id
      * @param lineNumber line number
      * @param token      token
      * @param finish     finish
@@ -231,6 +239,7 @@
      */
     public FunctionNode(
         final Source source,
+        final int id,
         final int lineNumber,
         final long token,
         final int finish,
@@ -245,6 +254,7 @@
         super(token, finish);
 
         this.source           = source;
+        this.id               = id;
         this.lineNumber       = lineNumber;
         this.ident            = ident;
         this.name             = name;
@@ -259,23 +269,19 @@
         this.sourceURL        = sourceURL;
         this.compileUnit      = null;
         this.body             = null;
-        this.snapshot         = null;
-        this.hints            = null;
     }
 
     private FunctionNode(
         final FunctionNode functionNode,
         final long lastToken,
         final int flags,
-        final String sourceURL,
+        String sourceURL,
         final String name,
         final Type returnType,
         final CompileUnit compileUnit,
         final EnumSet<CompilationState> compilationState,
         final Block body,
-        final List<IdentNode> parameters,
-        final FunctionNode snapshot,
-        final Compiler.Hints hints) {
+        final List<IdentNode> parameters) {
         super(functionNode);
         this.lineNumber       = functionNode.lineNumber;
         this.flags            = flags;
@@ -287,11 +293,10 @@
         this.compilationState = compilationState;
         this.body             = body;
         this.parameters       = parameters;
-        this.snapshot         = snapshot;
-        this.hints            = hints;
 
         // the fields below never change - they are final and assigned in constructor
         this.source          = functionNode.source;
+        this.id              = functionNode.id;
         this.ident           = functionNode.ident;
         this.namespace       = functionNode.namespace;
         this.declaredSymbols = functionNode.declaredSymbols;
@@ -317,6 +322,14 @@
     }
 
     /**
+     * Get the unique ID for this function
+     * @return the id
+     */
+    public int getId() {
+        return id;
+    }
+
+    /**
      * get source name - sourceURL or name derived from Source.
      *
      * @return name for the script source
@@ -345,7 +358,7 @@
             return this;
         }
 
-        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, newSourceURL, name, returnType, compileUnit, compilationState, body, parameters, null, hints));
+        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, newSourceURL, name, returnType, compileUnit, compilationState, body, parameters));
     }
 
     /**
@@ -357,50 +370,11 @@
     }
 
     /**
-     * Get the version of this function node's code as it looked upon construction
-     * i.e typically parsed and nothing else
-     * @return initial version of function node
-     */
-    public FunctionNode getSnapshot() {
-        return snapshot;
-    }
-
-    /**
-     * Throw away the snapshot, if any, to save memory. Used when heuristic
-     * determines that a method is not worth specializing
-     *
-     * @param lc lexical context
-     * @return new function node if a snapshot was present, now with snapsnot null
-     */
-    public FunctionNode clearSnapshot(final LexicalContext lc) {
-        if (this.snapshot == null) {
-            return this;
-        }
-        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, null, hints));
-    }
-
-    /**
-     * Take a snapshot of this function node at a given point in time
-     * and store it in the function node
-     * @param lc lexical context
-     * @return function node
-     */
-    public FunctionNode snapshot(final LexicalContext lc) {
-        if (this.snapshot == this) {
-            return this;
-        }
-        if (isProgram() || parameters.isEmpty()) {
-            return this; //never specialize anything that won't be recompiled
-        }
-        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, this, hints));
-    }
-
-    /**
      * Can this function node be regenerated with more specific type args?
      * @return true if specialization is possible
      */
     public boolean canSpecialize() {
-        return snapshot != null && getFlag(CAN_SPECIALIZE);
+        return (flags & (IS_OPTIMISTIC | CAN_SPECIALIZE)) != 0;
     }
 
     /**
@@ -450,28 +424,7 @@
         }
         final EnumSet<CompilationState> newState = EnumSet.copyOf(this.compilationState);
         newState.add(state);
-        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, newState, body, parameters, snapshot, hints));
-    }
-
-    /**
-     * Get any compiler hints that may associated with the function
-     * @return compiler hints
-     */
-    public Compiler.Hints getHints() {
-        return this.hints == null ? Compiler.Hints.EMPTY : hints;
-    }
-
-    /**
-     * Set compiler hints for this function
-     * @param lc    lexical context
-     * @param hints compiler hints
-     * @return new function if hints changed
-     */
-    public FunctionNode setHints(final LexicalContext lc, final Compiler.Hints hints) {
-        if (this.hints == hints) {
-            return this;
-        }
-        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
+        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, newState, body, parameters));
     }
 
     /**
@@ -486,10 +439,10 @@
 
     @Override
     public void toString(final StringBuilder sb) {
-        sb.append('[');
-        sb.append(returnType);
-        sb.append(']');
-        sb.append(' ');
+        sb.append('[').
+            append(returnType).
+            append(']').
+            append(' ');
 
         sb.append("function");
 
@@ -499,16 +452,16 @@
         }
 
         sb.append('(');
-        boolean first = true;
 
-        for (final IdentNode parameter : parameters) {
-            if (!first) {
+        for (final Iterator<IdentNode> iter = parameters.iterator(); iter.hasNext(); ) {
+            final IdentNode parameter = iter.next();
+            if (parameter.getSymbol() != null) {
+                sb.append('[').append(parameter.getType()).append(']').append(' ');
+            }
+            parameter.toString(sb);
+            if (iter.hasNext()) {
                 sb.append(", ");
-            } else {
-                first = false;
             }
-
-            parameter.toString(sb);
         }
 
         sb.append(')');
@@ -524,7 +477,7 @@
         if (this.flags == flags) {
             return this;
         }
-        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
+        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters));
     }
 
     @Override
@@ -546,11 +499,11 @@
     }
 
     /**
-     * Should this function node be lazily code generated, i.e. first at link time
-     * @return true if lazy
+     * Returns true if the function is optimistic
+     * @return True if this function is optimistic
      */
-    public boolean isLazy() {
-        return getFlag(IS_LAZY);
+    public boolean isOptimistic() {
+        return getFlag(IS_OPTIMISTIC);
     }
 
     /**
@@ -587,7 +540,15 @@
      * @return true if the function's generated Java method needs a {@code callee} parameter.
      */
     public boolean needsCallee() {
-        return needsParentScope() || needsSelfSymbol() || isSplit() || (needsArguments() && !isStrict());
+        return needsParentScope() || usesSelfSymbol() || isSplit() || (needsArguments() && !isStrict());
+    }
+
+    /**
+     * Check if this function uses the return symbol
+     * @return true if uses the return symbol
+     */
+    public boolean usesReturnSymbol() {
+        return isProgram() || isSplit() || getFlag(USES_RETURN_SYMBOL);
     }
 
     /**
@@ -634,7 +595,7 @@
         if(this.body == body) {
             return this;
         }
-        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags | (body.needsScope() ? FunctionNode.HAS_SCOPE_BLOCK : 0), sourceURL, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
+        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags | (body.needsScope() ? FunctionNode.HAS_SCOPE_BLOCK : 0), sourceURL, name, returnType, compileUnit, compilationState, body, parameters));
     }
 
     /**
@@ -729,7 +690,7 @@
         if (this.lastToken == lastToken) {
             return this;
         }
-        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
+        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters));
     }
 
     /**
@@ -751,7 +712,7 @@
         if (this.name.equals(name)) {
             return this;
         }
-        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
+        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters));
     }
 
     /**
@@ -774,15 +735,6 @@
     }
 
     /**
-     * Checks if this function has yet-to-be-generated child functions
-     *
-     * @return true if there are lazy child functions
-     */
-    public boolean hasLazyChildren() {
-        return getFlag(HAS_LAZY_CHILDREN);
-    }
-
-    /**
      * Get the parameters to this function
      * @return a list of IdentNodes which represent the function parameters, in order
      */
@@ -801,7 +753,7 @@
         if (this.parameters == parameters) {
             return this;
         }
-        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
+        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters));
     }
 
     /**
@@ -821,12 +773,13 @@
     }
 
     /**
-     * Does this function need a self symbol - this is needed only for self
-     * referring functions
-     * @return true if function needs a symbol for self
+     * Does this function use its self symbol - this is needed only for self-referencing named function expressions.
+     * Self-referencing declared functions won't have this flag set, as they can access their own symbol through the
+     * scope (since they're bound to the symbol with their name in their enclosing scope).
+     * @return true if this function node is a named function expression that uses the symbol for itself.
      */
-    public boolean needsSelfSymbol() {
-        return body.getFlag(Block.NEEDS_SELF_SYMBOL);
+    public boolean usesSelfSymbol() {
+        return body.getFlag(Block.USES_SELF_SYMBOL);
     }
 
     @Override
@@ -871,11 +824,8 @@
                 type,
                 compileUnit,
                 compilationState,
-                body.setReturnType(type),
-                parameters,
-                snapshot,
-                hints));
-    }
+                body.setReturnType(type), parameters));
+   }
 
     /**
      * Check if the function is generated in strict mode
@@ -905,7 +855,7 @@
         if (this.compileUnit == compileUnit) {
             return this;
         }
-        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters, snapshot, hints));
+        return Node.replaceInLexicalContext(lc, this, new FunctionNode(this, lastToken, flags, sourceURL, name, returnType, compileUnit, compilationState, body, parameters));
     }
 
     /**
--- a/nashorn/src/jdk/nashorn/internal/ir/IdentNode.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/ir/IdentNode.java	Wed Feb 26 13:17:57 2014 +0100
@@ -28,29 +28,36 @@
 import static jdk.nashorn.internal.codegen.CompilerConstants.__DIR__;
 import static jdk.nashorn.internal.codegen.CompilerConstants.__FILE__;
 import static jdk.nashorn.internal.codegen.CompilerConstants.__LINE__;
+import static jdk.nashorn.internal.codegen.ObjectClassGenerator.DEBUG_FIELDS;
 
+import jdk.nashorn.internal.codegen.ObjectClassGenerator;
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.annotations.Immutable;
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
 
+import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
+
 /**
  * IR representation for an identifier.
  */
 @Immutable
-public final class IdentNode extends Expression implements PropertyKey, FunctionCall {
+public final class IdentNode extends Expression implements PropertyKey, FunctionCall, Optimistic {
     private static final int PROPERTY_NAME     = 1 << 0;
     private static final int INITIALIZED_HERE  = 1 << 1;
     private static final int FUNCTION          = 1 << 2;
     private static final int FUTURESTRICT_NAME = 1 << 3;
+    private static final int OPTIMISTIC        = 1 << 4;
 
     /** Identifier. */
     private final String name;
 
-    /** Type for a callsite, e.g. X in a get()X or a set(X)V */
-    private final Type callSiteType;
+    /** Optimistic type */
+    private final Type optimisticType;
 
     private final int flags;
 
+    private final int programPoint;
+
     /**
      * Constructor
      *
@@ -60,16 +67,18 @@
      */
     public IdentNode(final long token, final int finish, final String name) {
         super(token, finish);
-        this.name = name.intern();
-        this.callSiteType = null;
-        this.flags = 0;
+        this.name           = name.intern();
+        this.optimisticType = null;
+        this.flags          = 0;
+        this.programPoint   = INVALID_PROGRAM_POINT;
     }
 
-    private IdentNode(final IdentNode identNode, final String name, final Type callSiteType, final int flags) {
+    private IdentNode(final IdentNode identNode, final String name, final Type callSiteType, final int flags, final int programPoint) {
         super(identNode);
-        this.name = name;
-        this.callSiteType = callSiteType;
-        this.flags = flags;
+        this.name           = name;
+        this.optimisticType = callSiteType;
+        this.flags          = flags;
+        this.programPoint   = programPoint;
     }
 
     /**
@@ -79,14 +88,15 @@
      */
     public IdentNode(final IdentNode identNode) {
         super(identNode);
-        this.name         = identNode.getName();
-        this.callSiteType = null;
-        this.flags        = identNode.flags;
+        this.name           = identNode.getName();
+        this.optimisticType = null;
+        this.flags          = identNode.flags;
+        this.programPoint   = INVALID_PROGRAM_POINT;
     }
 
     @Override
     public Type getType() {
-        return callSiteType == null ? super.getType() : callSiteType;
+        return optimisticType == null ? super.getType() : optimisticType;
     }
 
     @Override
@@ -94,11 +104,6 @@
         return true;
     }
 
-    private boolean hasCallSiteType() {
-        //this is an identity that's part of a getter or setter
-        return callSiteType != null;
-    }
-
     /**
      * Assist in IR navigation.
      *
@@ -115,13 +120,7 @@
 
     @Override
     public void toString(final StringBuilder sb) {
-        if (hasCallSiteType()) {
-            sb.append('{');
-            final String desc = getType().getDescriptor();
-            sb.append(desc.charAt(desc.length() - 1) == ';' ? 'O' : getType().getDescriptor());
-            sb.append('}');
-        }
-
+        Node.optimisticType(this, sb);
         sb.append(name);
     }
 
@@ -159,7 +158,7 @@
         if (isPropertyName()) {
             return this;
         }
-        return new IdentNode(this, name, callSiteType, flags | PROPERTY_NAME);
+        return new IdentNode(this, name, optimisticType, flags | PROPERTY_NAME, programPoint);
     }
 
     /**
@@ -178,7 +177,7 @@
         if (isFutureStrictName()) {
             return this;
         }
-        return new IdentNode(this, name, callSiteType, flags | FUTURESTRICT_NAME);
+        return new IdentNode(this, name, optimisticType, flags | FUTURESTRICT_NAME, programPoint);
     }
 
     /**
@@ -197,16 +196,16 @@
         if (isInitializedHere()) {
             return this;
         }
-        return new IdentNode(this, name, callSiteType, flags | INITIALIZED_HERE);
+        return new IdentNode(this, name, optimisticType, flags | INITIALIZED_HERE, programPoint);
     }
 
     /**
-     * Check if this IdentNode is a special identity, currently __DIR__, __FILE__
-     * or __LINE__
+     * Check if the name of this IdentNode is same as that of a compile-time property (currently __DIR__, __FILE__, and
+     * __LINE__).
      *
-     * @return true if this IdentNode is special
+     * @return true if this IdentNode's name is same as that of a compile-time property
      */
-    public boolean isSpecialIdentity() {
+    public boolean isCompileTimePropertyName() {
         return name.equals(__DIR__.symbolName()) || name.equals(__FILE__.symbolName()) || name.equals(__LINE__.symbolName());
     }
 
@@ -215,6 +214,17 @@
         return (flags & FUNCTION) != 0;
     }
 
+    @Override
+    public IdentNode setType(final TemporarySymbols ts, final Type callSiteType) {
+        if (this.optimisticType == callSiteType) {
+            return this;
+        }
+        if (DEBUG_FIELDS && getSymbol() != null && !Type.areEquivalent(getSymbol().getSymbolType(), callSiteType)) {
+            ObjectClassGenerator.LOG.info(getClass().getName(), " ", this, " => ", callSiteType, " instead of ", getType());
+        }
+        return new IdentNode(this, name, callSiteType, flags, programPoint);
+    }
+
     /**
      * Mark this node as being the callee operand of a {@link CallNode}.
      * @return an ident node identical to this one in all aspects except with its function flag set.
@@ -223,6 +233,47 @@
         if (isFunction()) {
             return this;
         }
-        return new IdentNode(this, name, callSiteType, flags | FUNCTION);
+        return new IdentNode(this, name, optimisticType, flags | FUNCTION, programPoint);
+    }
+
+    @Override
+    public int getProgramPoint() {
+        return programPoint;
+    }
+
+    @Override
+    public Optimistic setProgramPoint(final int programPoint) {
+        if (this.programPoint == programPoint) {
+            return this;
+        }
+        return new IdentNode(this, name, optimisticType, flags, programPoint);
+    }
+
+    @Override
+    public Type getMostOptimisticType() {
+        return Type.INT;
+    }
+
+    @Override
+    public Type getMostPessimisticType() {
+        return Type.OBJECT;
+    }
+
+    @Override
+    public boolean canBeOptimistic() {
+        return true;
+    }
+
+    @Override
+    public boolean isOptimistic() {
+        return (flags & OPTIMISTIC) == OPTIMISTIC;
+    }
+
+    @Override
+    public Optimistic setIsOptimistic(final boolean isOptimistic) {
+        if (isOptimistic() == isOptimistic) {
+            return this;
+        }
+        return new IdentNode(this, name, optimisticType, isOptimistic ? (flags | OPTIMISTIC) : (flags & ~OPTIMISTIC), programPoint);
     }
 }
--- a/nashorn/src/jdk/nashorn/internal/ir/IndexNode.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/ir/IndexNode.java	Wed Feb 26 13:17:57 2014 +0100
@@ -25,9 +25,12 @@
 
 package jdk.nashorn.internal.ir;
 
+import static jdk.nashorn.internal.codegen.ObjectClassGenerator.DEBUG_FIELDS;
+
+import jdk.nashorn.internal.codegen.ObjectClassGenerator;
+import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.annotations.Immutable;
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
-
 /**
  * IR representation of an indexed access (brackets operator.)
  */
@@ -49,8 +52,8 @@
         this.index = index;
     }
 
-    private IndexNode(final IndexNode indexNode, final Expression base, final Expression index, final boolean isFunction) {
-        super(indexNode, base, isFunction);
+    private IndexNode(final IndexNode indexNode, final Expression base, final Expression index, final boolean isFunction, final Type optimisticType, final boolean isOptimistic, final int programPoint) {
+        super(indexNode, base, isFunction, optimisticType, isOptimistic, programPoint);
         this.index = index;
     }
 
@@ -72,6 +75,8 @@
             sb.append('(');
         }
 
+        Node.optimisticType(this, sb);
+
         base.toString(sb);
 
         if (needsParen) {
@@ -95,7 +100,7 @@
         if (this.base == base) {
             return this;
         }
-        return new IndexNode(this, base, index, isFunction());
+        return new IndexNode(this, base, index, isFunction(), optimisticType, isOptimistic, programPoint);
     }
 
     /**
@@ -107,15 +112,41 @@
         if(this.index == index) {
             return this;
         }
-        return new IndexNode(this, base, index, isFunction());
+        return new IndexNode(this, base, index, isFunction(), optimisticType, isOptimistic, programPoint);
+    }
+
+    @Override
+    public IndexNode setType(final TemporarySymbols ts, final Type optimisticType) {
+        if (this.optimisticType == optimisticType) {
+            return this;
+        }
+        if (DEBUG_FIELDS && getSymbol() != null && !Type.areEquivalent(getSymbol().getSymbolType(), optimisticType)) {
+            ObjectClassGenerator.LOG.info(getClass().getName(), " ", this, " => ", optimisticType, " instead of ", getType());
+        }
+        return new IndexNode(this, base, index, isFunction(), optimisticType, isOptimistic, programPoint);
     }
 
     @Override
-    public BaseNode setIsFunction() {
+    public IndexNode setIsFunction() {
         if (isFunction()) {
             return this;
         }
-        return new IndexNode(this, base, index, true);
+        return new IndexNode(this, base, index, true, optimisticType, isOptimistic, programPoint);
     }
 
+    @Override
+    public IndexNode setProgramPoint(int programPoint) {
+        if (this.programPoint == programPoint) {
+            return this;
+        }
+        return new IndexNode(this, base, index, isFunction(), optimisticType, isOptimistic, programPoint);
+    }
+
+    @Override
+    public IndexNode setIsOptimistic(boolean isOptimistic) {
+        if (this.isOptimistic == isOptimistic) {
+            return this;
+        }
+        return new IndexNode(this, base, index, isFunction(), optimisticType, isOptimistic, programPoint);
+    }
 }
--- a/nashorn/src/jdk/nashorn/internal/ir/LexicalContext.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/ir/LexicalContext.java	Wed Feb 26 13:17:57 2014 +0100
@@ -148,6 +148,7 @@
      * @return the node that was pushed
      */
     public <T extends LexicalContextNode> T push(final T node) {
+        assert !contains(node);
         if (sp == stack.length) {
             final LexicalContextNode[] newStack = new LexicalContextNode[sp * 2];
             System.arraycopy(stack, 0, newStack, 0, sp);
@@ -201,7 +202,6 @@
         return (T)popped;
     }
 
-
     /**
      * Return the top element in the context
      * @return the node that was pushed last
@@ -233,7 +233,6 @@
      * @return the new node
      */
     public LexicalContextNode replace(final LexicalContextNode oldNode, final LexicalContextNode newNode) {
-       //System.err.println("REPLACE old=" + Debug.id(oldNode) + " new=" + Debug.id(newNode));
         for (int i = sp - 1; i >= 0; i--) {
             if (stack[i] == oldNode) {
                 assert i == (sp - 1) : "violation of contract - we always expect to find the replacement node on top of the lexical context stack: " + newNode + " has " + stack[i + 1].getClass() + " above it";
@@ -411,22 +410,6 @@
     }
 
     /**
-     * Returns true if the expression defining the function is a callee of a CallNode that should be the second
-     * element on the stack, e.g. <code>(function(){})()</code>. That is, if the stack ends with
-     * {@code [..., CallNode, FunctionNode]} then {@code callNode.getFunction()} should be equal to
-     * {@code functionNode}, and the top of the stack should itself be a variant of {@code functionNode}.
-     * @param functionNode the function node being tested
-     * @return true if the expression defining the current function is a callee of a call expression.
-     */
-    public boolean isFunctionDefinedInCurrentCall(FunctionNode functionNode) {
-        final LexicalContextNode parent = stack[sp - 2];
-        if (parent instanceof CallNode && ((CallNode)parent).getFunction() == functionNode) {
-            return true;
-        }
-        return false;
-    }
-
-    /**
      * Get the parent function for a function in the lexical context
      * @param functionNode function for which to get parent
      * @return parent function of functionNode or null if none (e.g. if functionNode is the program)
@@ -444,15 +427,23 @@
     }
 
     /**
-     * Count the number of with scopes until a given node
-     * @param until node to stop counting at, or null if all nodes should be counted
+     * Count the number of scopes until a given node.
+     * @param until node to stop counting at. Must be within the current  function
      * @return number of with scopes encountered in the context
      */
     public int getScopeNestingLevelTo(final LexicalContextNode until) {
+        assert until != null;
         //count the number of with nodes until "until" is hit
         int n = 0;
-        for (final Iterator<WithNode> iter = new NodeIterator<>(WithNode.class, until); iter.hasNext(); iter.next()) {
-            n++;
+        for (final Iterator<LexicalContextNode> iter = getAllNodes(); iter.hasNext();) {
+            final LexicalContextNode node = iter.next();
+            if(node == until) {
+                break;
+            }
+            assert !(node instanceof FunctionNode); // Can't go outside current function
+            if(node instanceof WithNode || (node instanceof Block && ((Block)node).needsScope())) {
+                n++;
+            }
         }
         return n;
     }
--- a/nashorn/src/jdk/nashorn/internal/ir/LiteralNode.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/ir/LiteralNode.java	Wed Feb 26 13:17:57 2014 +0100
@@ -28,7 +28,6 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
-
 import jdk.nashorn.internal.codegen.CompileUnit;
 import jdk.nashorn.internal.codegen.types.ArrayType;
 import jdk.nashorn.internal.codegen.types.Type;
@@ -657,23 +656,12 @@
          * Compute things like widest element type needed. Internal use from compiler only
          */
         public void analyze() {
-            elementType = Type.INT;
-            analyzeElements();
-
-            if (elementType.isInteger()) {
-                presetIntArray();
-            } else if (elementType.isLong()) {
-                presetLongArray();
-            } else if (elementType.isNumeric()) {
-                presetNumberArray();
-            } else {
-                presetObjectArray();
-            }
+            assert elementType.isUnknown();
+            elementType = getNarrowestElementType(value);
         }
 
-        private void presetIntArray() {
+        private int[] presetIntArray() {
             final int[] array = new int[value.length];
-            final int[] computed = new int[value.length];
             int nComputed = 0;
 
             for (int i = 0; i < value.length; i++) {
@@ -682,17 +670,16 @@
                 if (element instanceof Number) {
                     array[i] = ((Number)element).intValue();
                 } else {
-                    computed[nComputed++] = i;
+                    assert getPostsets()[nComputed++] == i;
                 }
             }
 
-            presets = array;
-            postsets = Arrays.copyOf(computed, nComputed);
+            assert getPostsets().length == nComputed;
+            return array;
         }
 
-        private void presetLongArray() {
+        private long[] presetLongArray() {
             final long[] array = new long[value.length];
-            final int[] computed = new int[value.length];
             int nComputed = 0;
 
             for (int i = 0; i < value.length; i++) {
@@ -701,17 +688,16 @@
                 if (element instanceof Number) {
                     array[i] = ((Number)element).longValue();
                 } else {
-                    computed[nComputed++] = i;
+                    assert getPostsets()[nComputed++] == i;
                 }
             }
 
-            presets = array;
-            postsets = Arrays.copyOf(computed, nComputed);
+            assert getPostsets().length == nComputed;
+            return array;
         }
 
-        private void presetNumberArray() {
+        private double[] presetNumberArray() {
             final double[] array = new double[value.length];
-            final int[] computed = new int[value.length];
             int nComputed = 0;
 
             for (int i = 0; i < value.length; i++) {
@@ -720,40 +706,40 @@
                 if (element instanceof Number) {
                     array[i] = ((Number)element).doubleValue();
                 } else {
-                    computed[nComputed++] = i;
+                    assert getPostsets()[nComputed++] == i;
                 }
             }
 
-            presets = array;
-            postsets = Arrays.copyOf(computed, nComputed);
+            assert getPostsets().length == nComputed;
+            return array;
         }
 
-        private void presetObjectArray() {
+        private Object[] presetObjectArray() {
             final Object[] array = new Object[value.length];
-            final int[] computed = new int[value.length];
             int nComputed = 0;
 
             for (int i = 0; i < value.length; i++) {
                 final Node node = value[i];
 
                 if (node == null) {
-                    computed[nComputed++] = i;
+                    assert getPostsets()[nComputed++] == i;
                 } else {
                     final Object element = objectAsConstant(node);
 
                     if (element != POSTSET_MARKER) {
                         array[i] = element;
                     } else {
-                        computed[nComputed++] = i;
+                        assert getPostsets()[nComputed++] == i;
                     }
                 }
             }
 
-            presets = array;
-            postsets = Arrays.copyOf(computed, nComputed);
+            assert getPostsets().length == nComputed;
+            return array;
         }
 
-        private void analyzeElements() {
+        private static Type getNarrowestElementType(final Expression[] value) {
+            Type elementType = Type.INT;
             for (final Expression node : value) {
                 if (node == null) {
                     elementType = elementType.widest(Type.OBJECT); //no way to represent undefined as number
@@ -777,6 +763,7 @@
                     break;
                 }
             }
+            return elementType;
         }
 
         @Override
@@ -789,6 +776,10 @@
          * @return array element type
          */
         public ArrayType getArrayType() {
+            return getArrayType(getElementType());
+        }
+
+        private static ArrayType getArrayType(final Type elementType) {
             if (elementType.isInteger()) {
                 return Type.INT_ARRAY;
             } else if (elementType.isLong()) {
@@ -810,6 +801,7 @@
          * @return element type
          */
         public Type getElementType() {
+            assert !elementType.isUnknown();
             return elementType;
         }
 
@@ -819,6 +811,18 @@
          * @return post set indices
          */
         public int[] getPostsets() {
+            if(postsets == null) {
+                final int[] computed = new int[value.length];
+                int nComputed = 0;
+
+                for (int i = 0; i < value.length; i++) {
+                    final Expression element = value[i];
+                    if(element == null || objectAsConstant(element) == POSTSET_MARKER) {
+                        computed[nComputed++] = i;
+                    }
+                }
+                postsets = Arrays.copyOf(computed, nComputed);
+            }
             return postsets;
         }
 
@@ -827,6 +831,18 @@
          * @return presets array, always returns an array type
          */
         public Object getPresets() {
+            if(presets == null) {
+                final Type type = getElementType();
+                if (type.isInteger()) {
+                    presets = presetIntArray();
+                } else if (type.isLong()) {
+                    presets = presetLongArray();
+                } else if (type.isNumeric()) {
+                    presets = presetNumberArray();
+                } else {
+                    presets = presetObjectArray();
+                }
+            }
             return presets;
         }
 
--- a/nashorn/src/jdk/nashorn/internal/ir/Node.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/ir/Node.java	Wed Feb 26 13:17:57 2014 +0100
@@ -282,6 +282,21 @@
         return false;
     }
 
+    /**
+     * Tag an expression as optimistic or not. This is a convenience wrapper
+     * that is a no op of the expression cannot be optimistic
+     * @param expr          expression
+     * @param isOptimistic  is optimistic flag
+     * @return the new expression, or same if unmodified state
+     */
+    //SAM method in Java 8
+    public static Expression setIsOptimistic(final Expression expr, final boolean isOptimistic) {
+        if (expr instanceof Optimistic) {
+            return (Expression)((Optimistic)expr).setIsOptimistic(isOptimistic);
+        }
+        return expr;
+    }
+
     //on change, we have to replace the entire list, that's we can't simple do ListIterator.set
     static <T extends Node> List<T> accept(final NodeVisitor<? extends LexicalContext> visitor, final Class<T> clazz, final List<T> list) {
         boolean changed = false;
@@ -304,4 +319,21 @@
         }
         return newNode;
     }
+
+    static final String OPT_IDENTIFIER = "%";
+
+    static void optimisticType(final Node node, final StringBuilder sb) {
+        if (node instanceof Optimistic && ((Optimistic)node).isOptimistic()) {
+            sb.append('{');
+            final String desc = (((Expression)node).getType()).getDescriptor();
+            sb.append(desc.charAt(desc.length() - 1) == ';' ? "O" : desc);
+            if (node instanceof Optimistic && ((Optimistic)node).isOptimistic()) {
+                sb.append(OPT_IDENTIFIER);
+                sb.append('_');
+                sb.append(((Optimistic)node).getProgramPoint());
+            }
+            sb.append('}');
+        }
+    }
+
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/ir/Optimistic.java	Wed Feb 26 13:17:57 2014 +0100
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.nashorn.internal.ir;
+
+import jdk.nashorn.internal.codegen.types.Type;
+
+/**
+ * Is this a node that can be optimistically typed? This means that it
+ * has a probable type but it's not available through static analysis
+ *
+ * The follow nodes are optimistic, with reasons therefore given within
+ * parenthesis
+ *
+ * @see IndexNode  (dynamicGetIndex)
+ * @see BinaryNode (local calculations to strongly typed bytecode)
+ * @see UnaryNode  (local calculations to strongly typed bytecode)
+ * @see CallNode   (dynamicCall)
+ *
+ * TODO : to be implemented are
+ *
+ * @see AccessNode (dynamicGet)
+ * @see IdentNode  (dynamicGet)
+ */
+public interface Optimistic {
+    /**
+     * Unique node ID that is associated with an invokedynamic call that mail
+     * fail and its callsite. This is so that nodes can be regenerated less
+     * pessimistically the next generation if an assumption failed
+     *
+     * @return unique node id
+     */
+    public int getProgramPoint();
+
+    /**
+     * Set the node number for this node, associating with a unique per-function
+     * program point
+     * @param programPoint the node number
+     * @return new node, or same if unchanged
+     */
+    public Optimistic setProgramPoint(final int programPoint);
+
+    /**
+     * Is it possible for this particular implementor to actually have any optimism?
+     * SHIFT operators for instance are binary nodes, but never optimistic. Multiply
+     * operators are. We might want to refurbish the type hierarchy to fix this.
+     * @return true if theoretically optimistic
+     */
+    public boolean canBeOptimistic();
+
+    /**
+     * Is this op optimistic, i.e. narrower than its narrowest statically provable
+     * type
+     * @return true if optimistic
+     */
+    public boolean isOptimistic();
+
+    /**
+     * Get the most optimistic type for this node. Typically we start out as
+     * an int, and then at runtime we bump this up to number and then Object
+     *
+     * @return optimistic type to be used in code generation
+     */
+    public Type getMostOptimisticType();
+
+    /**
+     * Most pessimistic type that is guaranteed to be safe.  Typically this is
+     * number for arithmetic operations that can overflow, or Object for an add
+     *
+     * @return pessimistic type guaranteed to never overflow
+     */
+    public Type getMostPessimisticType();
+
+
+    /**
+     * Tag this type override as optimistic rather than based on statically known
+     * type coercion semantics
+     * @param isOptimistic is this function optimistic
+     * @return new optimistic function
+     */
+    public Optimistic setIsOptimistic(final boolean isOptimistic);
+
+    /**
+     * Set the override type
+     *
+     * @param ts temporary symbols
+     * @param type the type
+     * @return a node equivalent to this one except for the requested change.
+     */
+    public Optimistic setType(final TemporarySymbols ts, final Type type);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/ir/OptimisticLexicalContext.java	Wed Feb 26 13:17:57 2014 +0100
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.nashorn.internal.ir;
+
+import jdk.nashorn.internal.IntDeque;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.List;
+import jdk.nashorn.internal.codegen.types.Type;
+
+/**
+ * Lexical context that keeps track of optimistic assumptions (if any)
+ * made during code generation. Used from Attr and FinalizeTypes
+ */
+public class OptimisticLexicalContext extends LexicalContext {
+
+    private final boolean isEnabled;
+
+    class Assumption {
+        Symbol symbol;
+        Type   type;
+
+        Assumption(final Symbol symbol, final Type type) {
+            this.symbol = symbol;
+            this.type   = type;
+        }
+        @Override
+        public String toString() {
+            return symbol.getName() + "=" + type;
+        }
+    }
+
+    /** Optimistic assumptions that could be made per function */
+    private final Deque<List<Assumption>> optimisticAssumptions = new ArrayDeque<>();
+    private final IntDeque splitNodes = new IntDeque();
+
+    /**
+     * Constructor
+     * @param isEnabled are optimistic types enabled?
+     */
+    public OptimisticLexicalContext(final boolean isEnabled) {
+        super();
+        this.isEnabled = isEnabled;
+    }
+
+    /**
+     * Are optimistic types enabled
+     * @return true if optimistic types
+     */
+    public boolean isEnabled() {
+        return isEnabled;
+    }
+
+    /**
+     * Log an optimistic assumption during codegen
+     * TODO : different parameters and more info about the assumption for future profiling
+     * needs
+     * @param symbol symbol
+     * @param type   type
+     */
+    public void logOptimisticAssumption(final Symbol symbol, final Type type) {
+        if (isEnabled) {
+            final List<Assumption> peek = optimisticAssumptions.peek();
+            peek.add(new Assumption(symbol, type));
+        }
+    }
+
+    /**
+     * Get the list of optimistic assumptions made
+     * @return optimistic assumptions
+     */
+    public List<Assumption> getOptimisticAssumptions() {
+        return Collections.unmodifiableList(optimisticAssumptions.peek());
+    }
+
+    /**
+     * Does this method have optimistic assumptions made during codegen?
+     * @return true if optimistic assumptions were made
+     */
+    public boolean hasOptimisticAssumptions() {
+        return !optimisticAssumptions.isEmpty() && !getOptimisticAssumptions().isEmpty();
+    }
+
+    @Override
+    public <T extends LexicalContextNode> T push(final T node) {
+        if (isEnabled) {
+            if(node instanceof FunctionNode) {
+                optimisticAssumptions.push(new ArrayList<Assumption>());
+                splitNodes.push(0);
+            } else if(node instanceof SplitNode) {
+                splitNodes.getAndIncrement();
+            }
+        }
+
+        return super.push(node);
+    }
+
+    @Override
+    public <T extends LexicalContextNode> T pop(final T node) {
+        T popped = super.pop(node);
+        if (isEnabled) {
+            if(node instanceof FunctionNode) {
+                optimisticAssumptions.pop();
+                assert splitNodes.peek() == 0;
+                splitNodes.pop();
+            } else if(node instanceof SplitNode) {
+                splitNodes.decrementAndGet();
+            }
+        }
+        return popped;
+    }
+
+    /**
+     * Check whether the lexical context is inside a split node
+     * @return true if we are traversing a SplitNode
+     */
+    public boolean isInSplitNode() {
+        return !splitNodes.isEmpty() && splitNodes.peek() > 0;
+    }
+}
--- a/nashorn/src/jdk/nashorn/internal/ir/RuntimeNode.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/ir/RuntimeNode.java	Wed Feb 26 13:17:57 2014 +0100
@@ -34,11 +34,13 @@
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
 import jdk.nashorn.internal.parser.TokenType;
 
+import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
+
 /**
  * IR representation for a runtime call.
  */
 @Immutable
-public class RuntimeNode extends Expression {
+public class RuntimeNode extends Expression implements Optimistic {
 
     /**
      * Request enum used for meta-information about the runtime request
@@ -77,7 +79,9 @@
         /** !== operator with at least one object */
         NE_STRICT(TokenType.NE_STRICT, Type.BOOLEAN, 2, true),
         /** != operator with at least one object */
-        NE(TokenType.NE, Type.BOOLEAN, 2, true);
+        NE(TokenType.NE, Type.BOOLEAN, 2, true),
+        /** Verify type */
+        TYPE_GUARD(TokenType.VOID, Type.INT, 2);
 
         /** token type */
         private final TokenType tokenType;
@@ -211,6 +215,17 @@
         }
 
         /**
+         * Is this strict?
+         *
+         * @param request a request
+         *
+         * @return true if script
+         */
+        public static boolean isStrict(final Request request) {
+            return request == EQ_STRICT || request == NE_STRICT;
+        }
+
+        /**
          * If this request can be reversed, return the reverse request
          * Eq EQ {@literal ->} NE.
          *
@@ -301,6 +316,8 @@
     /** is final - i.e. may not be removed again, lower in the code pipeline */
     private final boolean isFinal;
 
+    private final int programPoint;
+
     /**
      * Constructor
      *
@@ -315,14 +332,16 @@
         this.request      = request;
         this.args         = args;
         this.isFinal      = false;
+        this.programPoint = INVALID_PROGRAM_POINT;
     }
 
-    private RuntimeNode(final RuntimeNode runtimeNode, final Request request, final boolean isFinal, final List<Expression> args) {
+    private RuntimeNode(final RuntimeNode runtimeNode, final Request request, final boolean isFinal, final List<Expression> args, final int programPoint) {
         super(runtimeNode);
 
         this.request      = request;
         this.args         = args;
         this.isFinal      = isFinal;
+        this.programPoint = programPoint;
     }
 
     /**
@@ -361,6 +380,7 @@
         this.request      = request;
         this.args         = args;
         this.isFinal      = false;
+        this.programPoint = parent instanceof Optimistic ? ((Optimistic)parent).getProgramPoint() : INVALID_PROGRAM_POINT;
     }
 
     /**
@@ -370,7 +390,7 @@
      * @param request the request
      */
     public RuntimeNode(final UnaryNode parent, final Request request) {
-        this(parent, request, parent.rhs());
+        this(parent, request, parent.getExpression());
     }
 
     /**
@@ -400,7 +420,7 @@
         if (this.isFinal == isFinal) {
             return this;
         }
-        return new RuntimeNode(this, request, isFinal, args);
+        return new RuntimeNode(this, request, isFinal, args, programPoint);
     }
 
     /**
@@ -457,7 +477,7 @@
         if (this.args == args) {
             return this;
         }
-        return new RuntimeNode(this, request, isFinal, args);
+        return new RuntimeNode(this, request, isFinal, args, programPoint);
     }
 
     /**
@@ -483,4 +503,49 @@
         }
         return true;
     }
+
+//TODO these are blank for now:
+
+    @Override
+    public int getProgramPoint() {
+        return programPoint;
+    }
+
+    @Override
+    public RuntimeNode setProgramPoint(final int programPoint) {
+        if(this.programPoint == programPoint) {
+            return this;
+        }
+        return new RuntimeNode(this, request, isFinal, args, programPoint);
+    }
+
+    @Override
+    public boolean canBeOptimistic() {
+        return false;
+    }
+
+    @Override
+    public Type getMostOptimisticType() {
+        return getType();
+    }
+
+    @Override
+    public Type getMostPessimisticType() {
+        return getType();
+    }
+
+    @Override
+    public boolean isOptimistic() {
+        return false;
+    }
+
+    @Override
+    public RuntimeNode setIsOptimistic(final boolean isOptimistic) {
+        return this;
+    }
+
+    @Override
+    public RuntimeNode setType(final TemporarySymbols ts, final Type type) {
+        return this;
+    }
 }
--- a/nashorn/src/jdk/nashorn/internal/ir/Symbol.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/ir/Symbol.java	Wed Feb 26 13:17:57 2014 +0100
@@ -25,6 +25,8 @@
 
 package jdk.nashorn.internal.ir;
 
+import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
+
 import java.io.PrintWriter;
 import java.util.HashSet;
 import java.util.Set;
@@ -35,8 +37,6 @@
 import jdk.nashorn.internal.runtime.Debug;
 import jdk.nashorn.internal.runtime.options.Options;
 
-import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
-
 /**
  * Maps a name to specific data.
  */
@@ -56,27 +56,27 @@
     public static final int KINDMASK = (1 << 3) - 1; // Kinds are represented by lower three bits
 
     /** Is this scope */
-    public static final int IS_SCOPE             = 1 <<  4;
+    public static final int IS_SCOPE                = 1 <<  4;
     /** Is this a this symbol */
-    public static final int IS_THIS              = 1 <<  5;
+    public static final int IS_THIS                 = 1 <<  5;
     /** Can this symbol ever be undefined */
-    public static final int CAN_BE_UNDEFINED     = 1 <<  6;
+    public static final int CAN_BE_UNDEFINED        = 1 <<  6;
     /** Is this symbol always defined? */
-    public static final int IS_ALWAYS_DEFINED    = 1 <<  8;
-    /** Can this symbol ever have primitive types */
-    public static final int CAN_BE_PRIMITIVE     = 1 <<  9;
+    public static final int IS_ALWAYS_DEFINED       = 1 <<  7;
     /** Is this a let */
-    public static final int IS_LET               = 1 << 10;
+    public static final int IS_LET                  = 1 <<  8;
     /** Is this an internal symbol, never represented explicitly in source code */
-    public static final int IS_INTERNAL          = 1 << 11;
+    public static final int IS_INTERNAL             = 1 <<  9;
     /** Is this a function self-reference symbol */
-    public static final int IS_FUNCTION_SELF     = 1 << 12;
-    /** Is this a specialized param? */
-    public static final int IS_SPECIALIZED_PARAM = 1 << 13;
+    public static final int IS_FUNCTION_SELF        = 1 << 10;
+    /** Is this a specialized param, i.e. known type base on runtime callsite? */
+    public static final int IS_SPECIALIZED_PARAM    = 1 << 11;
     /** Is this symbol a shared temporary? */
-    public static final int IS_SHARED            = 1 << 14;
+    public static final int IS_SHARED               = 1 << 12;
     /** Is this a function declaration? */
-    public static final int IS_FUNCTION_DECLARATION = 1 << 15;
+    public static final int IS_FUNCTION_DECLARATION = 1 << 13;
+    /** Is this a program level symbol? */
+    public static final int IS_PROGRAM_LEVEL        = 1 << 14;
 
     /** Null or name identifying symbol. */
     private final String name;
@@ -142,7 +142,7 @@
         this.slot       = slot;
         this.fieldIndex = -1;
         this.range      = Range.createUnknownRange();
-        trace("CREATE SYMBOL");
+        trace("CREATE SYMBOL " + type);
     }
 
     /**
@@ -205,57 +205,61 @@
      */
 
     void print(final PrintWriter stream) {
-        final String printName = align(name, 20);
-        final String printType = align(type.toString(), 10);
-        final String printSlot = align(slot == -1 ? "none" : "" + slot, 10);
-        String printFlags = "";
+        final StringBuilder sb = new StringBuilder();
+
+        sb.append(align(name, 20)).
+            append(": ").
+            append(align(type.toString(), 10)).
+            append(", ").
+            append(align(slot == -1 ? "none" : "" + slot, 10));
 
         switch (flags & KINDMASK) {
         case IS_TEMP:
-            printFlags = "temp " + printFlags;
+            sb.append(" temp");
             break;
         case IS_GLOBAL:
-            printFlags = "global " + printFlags;
+            sb.append(" global");
             break;
         case IS_VAR:
-            printFlags = "var " + printFlags;
+            sb.append(" var");
             break;
         case IS_PARAM:
-            printFlags = "param " + printFlags;
+            sb.append(" param");
             break;
         case IS_CONSTANT:
-            printFlags = "CONSTANT " + printFlags;
+            sb.append(" const");
             break;
         default:
             break;
         }
 
         if (isScope()) {
-            printFlags += "scope ";
+            sb.append(" scope");
         }
 
         if (isInternal()) {
-            printFlags += "internal ";
+            sb.append(" internal");
         }
 
         if (isLet()) {
-            printFlags += "let ";
+            sb.append(" let");
         }
 
         if (isThis()) {
-            printFlags += "this ";
+            sb.append(" this");
         }
 
         if (!canBeUndefined()) {
-            printFlags += "always_def ";
+            sb.append(" def'd");
         }
 
-        if (canBePrimitive()) {
-            printFlags += "can_be_prim ";
+        if (isProgramLevel()) {
+            sb.append(" program");
         }
 
-        stream.print(printName + ": " + printType + ", " + printSlot + ", " + printFlags);
-        stream.println();
+        sb.append('\n');
+
+        stream.print(sb.toString());
     }
 
     /**
@@ -272,9 +276,11 @@
      * Allocate a slot for this symbol.
      *
      * @param needsSlot True if symbol needs a slot.
+     * @return the symbol
      */
-    public void setNeedsSlot(final boolean needsSlot) {
+    public Symbol setNeedsSlot(final boolean needsSlot) {
         setSlot(needsSlot ? 0 : -1);
+        return this;
     }
 
     /**
@@ -312,10 +318,6 @@
             }
         }
 
-        if (canBePrimitive()) {
-            sb.append(" P?");
-        }
-
         return sb.toString();
     }
 
@@ -381,27 +383,28 @@
 
     /**
      * Flag this symbol as scope as described in {@link Symbol#isScope()}
+     * @return the symbol
      */
-    /**
-     * Flag this symbol as scope as described in {@link Symbol#isScope()}
-     */
-     public void setIsScope() {
+     public Symbol setIsScope() {
         if (!isScope()) {
             trace("SET IS SCOPE");
             assert !isShared();
             flags |= IS_SCOPE;
         }
+        return this;
     }
 
      /**
       * Mark this symbol as one being shared by multiple expressions. The symbol must be a temporary.
+     * @return the symbol
       */
-     public void setIsShared() {
+     public Symbol setIsShared() {
          if (!isShared()) {
              assert isTemp();
              trace("SET IS SHARED");
              flags |= IS_SHARED;
          }
+         return this;
      }
 
 
@@ -448,6 +451,14 @@
     }
 
     /**
+     * Check if this is a program (script) level definition
+     * @return true if program level
+     */
+    public boolean isProgramLevel() {
+        return (flags & IS_PROGRAM_LEVEL) == IS_PROGRAM_LEVEL;
+    }
+
+    /**
      * Get the range for this symbol
      * @return range for symbol
      */
@@ -458,9 +469,11 @@
     /**
      * Set the range for this symbol
      * @param range range
+     * @return the symbol
      */
-    public void setRange(final Range range) {
+    public Symbol setRange(final Range range) {
         this.range = range;
+        return this;
     }
 
     /**
@@ -481,14 +494,6 @@
     }
 
     /**
-     * Check whether this symbol ever has primitive assignments. Conservative
-     * @return true if primitive assignments exist
-     */
-    public boolean canBePrimitive() {
-        return (flags & CAN_BE_PRIMITIVE) == CAN_BE_PRIMITIVE;
-    }
-
-    /**
      * Check if this symbol can ever be undefined
      * @return true if can be undefined
      */
@@ -498,26 +503,16 @@
 
     /**
      * Flag this symbol as potentially undefined in parts of the program
+     * @return the symbol
      */
-    public void setCanBeUndefined() {
-        assert type.isObject() : type;
+    public Symbol setCanBeUndefined() {
         if (isAlwaysDefined()) {
-            return;
+            return this;
         } else if (!canBeUndefined()) {
             assert !isShared();
             flags |= CAN_BE_UNDEFINED;
         }
-    }
-
-    /**
-     * Flag this symbol as potentially primitive
-     * @param type the primitive type it occurs with, currently unused but can be used for width guesses
-     */
-    public void setCanBePrimitive(final Type type) {
-        if(!canBePrimitive()) {
-            assert !isShared();
-            flags |= CAN_BE_PRIMITIVE;
-        }
+        return this;
     }
 
     /**
@@ -557,7 +552,7 @@
      * Flag this symbol as a let
      */
     public void setIsLet() {
-        if(!isLet()) {
+        if (!isLet()) {
             assert !isShared();
             flags |= IS_LET;
         }
@@ -587,12 +582,14 @@
      * and get allocated in a JO-prefixed ScriptObject subclass.
      *
      * @param fieldIndex field index - a positive integer
+     * @return the symbol
      */
-    public void setFieldIndex(final int fieldIndex) {
-        if(this.fieldIndex != fieldIndex) {
+    public Symbol setFieldIndex(final int fieldIndex) {
+        if (this.fieldIndex != fieldIndex) {
             assert !isShared();
             this.fieldIndex = fieldIndex;
         }
+        return this;
     }
 
     /**
@@ -606,12 +603,40 @@
     /**
      * Set the symbol flags
      * @param flags flags
+     * @return the symbol
      */
-    public void setFlags(final int flags) {
-        if(this.flags != flags) {
-            assert !isShared();
+    public Symbol setFlags(final int flags) {
+        if (this.flags != flags) {
+            assert !isShared() : this;
             this.flags = flags;
         }
+        return this;
+    }
+
+    /**
+     * Set a single symbol flag
+     * @param flag flag to set
+     * @return the symbol
+     */
+    public Symbol setFlag(final int flag) {
+        if ((this.flags & flag) == 0) {
+            assert !isShared() : this;
+            this.flags |= flag;
+        }
+        return this;
+    }
+
+    /**
+     * Clears a single symbol flag
+     * @param flag flag to set
+     * @return the symbol
+     */
+    public Symbol clearFlag(final int flag) {
+        if ((this.flags & flag) != 0) {
+            assert !isShared() : this;
+            this.flags &= ~flag;
+        }
+        return this;
     }
 
     /**
@@ -632,9 +657,11 @@
 
     /**
      * Increase the symbol's use count by one.
+     * @return the symbol
      */
-    public void increaseUseCount() {
+    public Symbol increaseUseCount() {
         useCount++;
+        return this;
     }
 
     /**
@@ -647,33 +674,39 @@
 
     /**
      * Set the bytecode slot for this symbol
-     * @param slot valid bytecode slot, or -1 if not available
+     * @param  slot valid bytecode slot, or -1 if not available
+     * @return the symbol
      */
-    public void setSlot(final int slot) {
+    public Symbol setSlot(final int slot) {
         if (slot != this.slot) {
             assert !isShared();
             trace("SET SLOT " + slot);
             this.slot = slot;
         }
+        return this;
     }
 
     /**
      * Assign a specific subclass of Object to the symbol
      *
      * @param type  the type
+     * @return the symbol
      */
-    public void setType(final Class<?> type) {
+    public Symbol setType(final Class<?> type) {
         assert !type.isPrimitive() && !Number.class.isAssignableFrom(type) : "Class<?> types can only be subclasses of object";
         setType(Type.typeFor(type));
+        return this;
     }
 
     /**
      * Assign a type to the symbol
      *
      * @param type the type
+     * @return the symbol
      */
-    public void setType(final Type type) {
+    public Symbol setType(final Type type) {
         setTypeOverride(Type.widest(this.type, type));
+        return this;
     }
 
     /**
@@ -690,15 +723,17 @@
      * constraint - otherwise a type can only be
      * widened
      *
-     * @param type  the type
+     * @param  type  the type
+     * @return the symbol
      */
-    public void setTypeOverride(final Type type) {
+    public Symbol setTypeOverride(final Type type) {
         final Type old = this.type;
         if (old != type) {
-            assert !isShared();
+            assert !isShared() : this + " is a shared symbol and cannot have its type overridden to " + type;
             trace("TYPE CHANGE: " + old + "=>" + type + " == " + type);
             this.type = type;
         }
+        return this;
     }
 
     /**
@@ -711,8 +746,8 @@
      * be changed.
      */
     public Symbol setTypeOverrideShared(final Type type, final TemporarySymbols ts) {
-        if(getSymbolType() != type) {
-            if(isShared()) {
+        if (getSymbolType() != type) {
+            if (isShared()) {
                 assert !hasSlot();
                 return ts.getTypedTemporarySymbol(type);
             }
@@ -728,14 +763,16 @@
      * when flags need to be tagged, but block is in the
      * middle of evaluation and cannot be modified.
      *
-     * @param lc     lexical context
-     * @param symbol symbol
+     * @param  lc     lexical context
+     * @param  symbol symbol
+     * @return the symbol
      */
-    public static void setSymbolIsScope(final LexicalContext lc, final Symbol symbol) {
+    public static Symbol setSymbolIsScope(final LexicalContext lc, final Symbol symbol) {
         symbol.setIsScope();
         if (!symbol.isGlobal()) {
             lc.setBlockNeedsScope(lc.getDefiningBlock(symbol));
         }
+        return symbol;
     }
 
     private void trace(final String desc) {
--- a/nashorn/src/jdk/nashorn/internal/ir/UnaryNode.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/ir/UnaryNode.java	Wed Feb 26 13:17:57 2014 +0100
@@ -28,8 +28,13 @@
 import static jdk.nashorn.internal.parser.TokenType.BIT_NOT;
 import static jdk.nashorn.internal.parser.TokenType.DECPOSTFIX;
 import static jdk.nashorn.internal.parser.TokenType.INCPOSTFIX;
+import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
 
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
 import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.ir.annotations.Ignore;
 import jdk.nashorn.internal.ir.annotations.Immutable;
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
 import jdk.nashorn.internal.parser.Token;
@@ -39,9 +44,27 @@
  * UnaryNode nodes represent single operand operations.
  */
 @Immutable
-public final class UnaryNode extends Expression implements Assignment<Expression> {
+public final class UnaryNode extends Expression implements Assignment<Expression>, Optimistic {
     /** Right hand side argument. */
-    private final Expression rhs;
+    private final Expression expression;
+
+    private final int programPoint;
+
+    private final boolean isOptimistic;
+
+    private final Type type;
+
+    @Ignore
+    private static final List<TokenType> CAN_OVERFLOW =
+            Collections.unmodifiableList(
+                Arrays.asList(new TokenType[] {
+                    TokenType.ADD,
+                    TokenType.SUB, //negate
+                    TokenType.DECPREFIX,
+                    TokenType.DECPOSTFIX,
+                    TokenType.INCPREFIX,
+                    TokenType.INCPOSTFIX,
+                }));
 
     /**
      * Constructor
@@ -56,20 +79,26 @@
     /**
      * Constructor
      *
-     * @param token  token
-     * @param start  start
-     * @param finish finish
-     * @param rhs    expression
+     * @param token      token
+     * @param start      start
+     * @param finish     finish
+     * @param expression expression
      */
-    public UnaryNode(final long token, final int start, final int finish, final Expression rhs) {
+    public UnaryNode(final long token, final int start, final int finish, final Expression expression) {
         super(token, start, finish);
-        this.rhs = rhs;
+        this.expression   = expression;
+        this.programPoint = INVALID_PROGRAM_POINT;
+        this.isOptimistic = false;
+        this.type = null;
     }
 
 
-    private UnaryNode(final UnaryNode unaryNode, final Expression rhs) {
+    private UnaryNode(final UnaryNode unaryNode, final Expression expression, final Type type, final int programPoint, final boolean isOptimistic) {
         super(unaryNode);
-        this.rhs = rhs;
+        this.expression   = expression;
+        this.programPoint = programPoint;
+        this.isOptimistic = isOptimistic;
+        this.type = type;
     }
 
     /**
@@ -97,17 +126,23 @@
 
     @Override
     public Type getWidestOperationType() {
-        return isAssignment() ? Type.NUMBER : Type.OBJECT;
+        switch (tokenType()) {
+        case ADD:
+        case SUB:
+            return Type.NUMBER;
+        default:
+            return isAssignment() ? Type.NUMBER : Type.OBJECT;
+        }
     }
 
     @Override
     public Expression getAssignmentDest() {
-        return isAssignment() ? rhs() : null;
+        return isAssignment() ? getExpression() : null;
     }
 
     @Override
     public UnaryNode setAssignmentDest(Expression n) {
-        return setRHS(n);
+        return setExpression(n);
     }
 
     @Override
@@ -122,7 +157,7 @@
     @Override
     public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
         if (visitor.enterUnaryNode(this)) {
-            return visitor.leaveUnaryNode(setRHS((Expression)rhs.accept(visitor)));
+            return visitor.leaveUnaryNode(setExpression((Expression)expression.accept(visitor)));
         }
 
         return this;
@@ -131,20 +166,20 @@
     @Override
     public boolean isLocal() {
         switch (tokenType()) {
-            case NEW:
-                return false;
-            case ADD:
-            case SUB:
-            case NOT:
-            case BIT_NOT:
-                return rhs.isLocal() && rhs.getType().isJSPrimitive();
-            case DECPOSTFIX:
-            case DECPREFIX:
-            case INCPOSTFIX:
-            case INCPREFIX:
-                return rhs instanceof IdentNode && rhs.isLocal() && rhs.getType().isJSPrimitive();
-            default:
-                return rhs.isLocal();
+        case NEW:
+            return false;
+        case ADD:
+        case SUB:
+        case NOT:
+        case BIT_NOT:
+            return expression.isLocal() && expression.getType().isJSPrimitive();
+        case DECPOSTFIX:
+        case DECPREFIX:
+        case INCPOSTFIX:
+        case INCPREFIX:
+            return expression instanceof IdentNode && expression.isLocal() && expression.getType().isJSPrimitive();
+        default:
+            return expression.isLocal();
         }
     }
 
@@ -153,7 +188,7 @@
         toString(sb, new Runnable() {
             @Override
             public void run() {
-                sb.append(rhs().toString());
+                sb.append(getExpression().toString());
             }
         });
     }
@@ -166,20 +201,23 @@
      * when invoked.
      */
     public void toString(final StringBuilder sb, final Runnable rhsStringBuilder) {
-        final TokenType type      = tokenType();
-        final String    name      = type.getName();
-        final boolean   isPostfix = type == DECPOSTFIX || type == INCPOSTFIX;
+        final TokenType tokenType = tokenType();
+        final String    name      = tokenType.getName();
+        final boolean   isPostfix = tokenType == DECPOSTFIX || tokenType == INCPOSTFIX;
 
-        boolean rhsParen   = type.needsParens(rhs().tokenType(), false);
+        if (isOptimistic()) {
+            sb.append(Node.OPT_IDENTIFIER);
+        }
+        boolean rhsParen   = tokenType.needsParens(getExpression().tokenType(), false);
 
         if (!isPostfix) {
             if (name == null) {
-                sb.append(type.name());
+                sb.append(tokenType.name());
                 rhsParen = true;
             } else {
                 sb.append(name);
 
-                if (type.ordinal() > BIT_NOT.ordinal()) {
+                if (tokenType.ordinal() > BIT_NOT.ordinal()) {
                     sb.append(' ');
                 }
             }
@@ -194,7 +232,7 @@
         }
 
         if (isPostfix) {
-            sb.append(type == DECPOSTFIX ? "--" : "++");
+            sb.append(tokenType == DECPOSTFIX ? "--" : "++");
         }
     }
 
@@ -206,8 +244,8 @@
      *
      * @return right hand side or expression node
      */
-    public Expression rhs() {
-        return rhs;
+    public Expression getExpression() {
+        return expression;
     }
 
     /**
@@ -216,13 +254,72 @@
      *
      * @see BinaryNode
      *
-     * @param rhs right hand side or expression node
+     * @param expression right hand side or expression node
      * @return a node equivalent to this one except for the requested change.
      */
-    public UnaryNode setRHS(final Expression rhs) {
-        if (this.rhs == rhs) {
+    public UnaryNode setExpression(final Expression expression) {
+        if (this.expression == expression) {
+            return this;
+        }
+        return new UnaryNode(this, expression, type, programPoint, isOptimistic);
+    }
+
+    @Override
+    public int getProgramPoint() {
+        return programPoint;
+    }
+
+    @Override
+    public UnaryNode setProgramPoint(final int programPoint) {
+        if (this.programPoint == programPoint) {
+            return this;
+        }
+        return new UnaryNode(this, expression, type, programPoint, isOptimistic);
+    }
+
+    @Override
+    public UnaryNode setIsOptimistic(final boolean isOptimistic) {
+        if (this.isOptimistic == isOptimistic) {
             return this;
         }
-        return new UnaryNode(this, rhs);
+        return new UnaryNode(this, expression, type, programPoint, isOptimistic);
+    }
+
+    @Override
+    public boolean canBeOptimistic() {
+        return getMostOptimisticType() != getMostPessimisticType();
+    }
+
+    @Override
+    public Type getMostOptimisticType() {
+        if (CAN_OVERFLOW.contains(tokenType())) {
+            return Type.INT;
+        }
+        return getMostPessimisticType();
+    }
+
+    @Override
+    public Type getMostPessimisticType() {
+        return getWidestOperationType();
     }
+
+    @Override
+    public boolean isOptimistic() {
+        //return hasType() && canBeOptimistic() && getType().narrowerThan(getMostPessimisticType());
+        return isOptimistic;
+    }
+
+    @Override
+    public Type getType() {
+        return type == null ? super.getType() : type;
+    }
+
+    @Override
+    public UnaryNode setType(TemporarySymbols ts, Type type) {
+        if (this.type == type) {
+            return this;
+        }
+        return new UnaryNode(this, expression, type, programPoint, isOptimistic);
+    }
+
 }
--- a/nashorn/src/jdk/nashorn/internal/ir/VarNode.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/ir/VarNode.java	Wed Feb 26 13:17:57 2014 +0100
@@ -99,7 +99,7 @@
     }
 
     @Override
-    public VarNode setAssignmentDest(IdentNode n) {
+    public VarNode setAssignmentDest(final IdentNode n) {
         return setName(n);
     }
 
--- a/nashorn/src/jdk/nashorn/internal/ir/debug/ASTWriter.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/ir/debug/ASTWriter.java	Wed Feb 26 13:17:57 2014 +0100
@@ -108,19 +108,27 @@
         String   type  = clazz.getName();
 
         type = type.substring(type.lastIndexOf('.') + 1, type.length());
+        int truncate = type.indexOf("Node");
+        if (truncate == -1) {
+            truncate = type.indexOf("Statement");
+        }
+        if (truncate != -1) {
+            type = type.substring(0, truncate);
+        }
+        type = type.toLowerCase();
+
         if (isReference) {
             type = "ref: " + type;
         }
-        type += "@" + Debug.id(node);
         final Symbol symbol;
-        if(node instanceof Expression) {
+        if (node instanceof Expression) {
             symbol = ((Expression)node).getSymbol();
         } else {
             symbol = null;
         }
 
         if (symbol != null) {
-            type += "#" + symbol;
+            type += ">" + symbol;
         }
 
         if (node instanceof Block && ((Block)node).needsScope()) {
@@ -160,6 +168,8 @@
             status += " (" + tname + ")";
         }
 
+        status += " @" + Debug.id(node);
+
         if (children.isEmpty()) {
             sb.append("[").
                 append(type).
@@ -200,7 +210,7 @@
                 } else if (value instanceof Collection) {
                     int pos = 0;
                     ASTWriter.indent(sb, indent + 1);
-                    sb.append("[Collection ").
+                    sb.append('[').
                         append(child.getName()).
                         append("[0..").
                         append(((Collection<Node>)value).size()).
--- a/nashorn/src/jdk/nashorn/internal/ir/debug/JSONWriter.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/ir/debug/JSONWriter.java	Wed Feb 26 13:17:57 2014 +0100
@@ -91,7 +91,7 @@
         final Parser       parser     = new Parser(env, new Source(name, code), new Context.ThrowErrorManager(), env._strict);
         final JSONWriter   jsonWriter = new JSONWriter(includeLoc);
         try {
-            final FunctionNode functionNode = parser.parse(CompilerConstants.RUN_SCRIPT.symbolName());
+            final FunctionNode functionNode = parser.parse(CompilerConstants.PROGRAM.symbolName());
             functionNode.accept(jsonWriter);
             return jsonWriter.getString();
         } catch (final ParserException e) {
@@ -802,7 +802,7 @@
             type("NewExpression");
             comma();
 
-            final CallNode callNode = (CallNode)unaryNode.rhs();
+            final CallNode callNode = (CallNode)unaryNode.getExpression();
             property("callee");
             callNode.getFunction().accept(this);
             comma();
@@ -844,7 +844,7 @@
             comma();
 
             property("argument");
-            unaryNode.rhs().accept(this);
+            unaryNode.getExpression().accept(this);
         }
 
         return leave();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/ir/debug/NashornClassReader.java	Wed Feb 26 13:17:57 2014 +0100
@@ -0,0 +1,552 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.ir.debug;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import jdk.internal.org.objectweb.asm.Attribute;
+import jdk.internal.org.objectweb.asm.ClassReader;
+import jdk.internal.org.objectweb.asm.ClassVisitor;
+import jdk.internal.org.objectweb.asm.Label;
+import jdk.nashorn.internal.ir.debug.NashornTextifier.NashornLabel;
+
+/**
+ * Subclass of the ASM classs reader that retains more info, such
+ * as bytecode offsets
+ */
+public class NashornClassReader extends ClassReader {
+
+    private final Map<String, List<Label>> labelMap = new HashMap<>();
+
+    /**
+     * Constructor
+     * @param bytecode bytecode for class
+     */
+    public NashornClassReader(final byte[] bytecode) {
+        super(bytecode);
+        parse(bytecode);
+    }
+
+    List<Label> getExtraLabels(final String className, final String methodName, final String methodDesc) {
+        final String key = fullyQualifiedName(className, methodName, methodDesc);
+        return labelMap.get(key);
+    }
+
+    private static int readByte(final byte[] bytecode, int index) {
+        return (byte)(bytecode[index] & 0xff);
+    }
+
+    private static int readShort(final byte[] bytecode, int index) {
+        return (short)((bytecode[index] & 0xff) << 8) | (bytecode[index + 1] & 0xff);
+    }
+
+    private static int readInt(final byte[] bytecode, int index) {
+        return ((bytecode[index] & 0xff) << 24) | ((bytecode[index + 1] & 0xff) << 16) | ((bytecode[index + 2] & 0xff) << 8) | (bytecode[index + 3] & 0xff);
+    }
+
+    private static long readLong(final byte[] bytecode, int index) {
+        int hi = readInt(bytecode, index);
+        int lo = readInt(bytecode, index + 4);
+        return ((long)hi << 32) | lo;
+    }
+
+    private static String readUTF(int index, final int utfLen, final byte[] bytecode) {
+        int endIndex = index + utfLen;
+        char buf[] = new char[utfLen * 2];
+        int strLen = 0;
+        int c;
+        int st = 0;
+        char cc = 0;
+        int i = index;
+
+        while (i < endIndex) {
+            c = bytecode[i++];
+            switch (st) {
+            case 0:
+                c = c & 0xFF;
+                if (c < 0x80) { // 0xxxxxxx
+                    buf[strLen++] = (char) c;
+                } else if (c < 0xE0 && c > 0xBF) { // 110x xxxx 10xx xxxx
+                    cc = (char) (c & 0x1F);
+                    st = 1;
+                } else { // 1110 xxxx 10xx xxxx 10xx xxxx
+                    cc = (char) (c & 0x0F);
+                    st = 2;
+                }
+                break;
+
+            case 1: // byte 2 of 2-byte char or byte 3 of 3-byte char
+                buf[strLen++] = (char) ((cc << 6) | (c & 0x3F));
+                st = 0;
+                break;
+
+            case 2: // byte 2 of 3-byte char
+                cc = (char) ((cc << 6) | (c & 0x3F));
+                st = 1;
+                break;
+
+            default:
+                break;
+            }
+        }
+        return new String(buf, 0, strLen);
+    }
+
+    private String parse(final byte[] bytecode) {
+        String thisClassName;
+
+        int u = 0;
+
+        int magic = readInt(bytecode, u);
+        u += 4; //magic
+        assert magic == 0xcafebabe : Integer.toHexString(magic);
+        readShort(bytecode, u); //minor
+        u += 2;
+        readShort(bytecode, u); //major
+        u += 2; //minor
+
+        int cpc = readShort(bytecode, u);
+        u += 2;
+        ArrayList<Constant> cp = new ArrayList<>(cpc);
+        cp.add(null);
+
+        for (int i = 1; i < cpc; i++) {
+            //constant pool entries
+            final int tag = readByte(bytecode, u);
+            u += 1;
+            switch (tag) {
+            case 7: //class
+                cp.add(new IndexInfo(cp, tag, readShort(bytecode, u)));
+                u += 2;
+                break;
+            case 9:  //fieldref
+            case 10: //methodref
+            case 11: //interfacemethodref
+                cp.add(new IndexInfo2(cp, tag, readShort(bytecode, u), readShort(bytecode, u + 2)));
+                u += 4;
+               break;
+            case 8: //string
+                cp.add(new IndexInfo(cp, tag, readShort(bytecode, u))); //string index
+                u += 2;
+                break;
+            case 3:  //int
+                cp.add(new DirectInfo<>(cp, tag, readInt(bytecode, u)));
+                u += 4;
+                break;
+            case 4:  //float
+                cp.add(new DirectInfo<>(cp, tag, Float.intBitsToFloat(readInt(bytecode, u))));
+                u += 4;
+                break;
+            case 5:  //long
+                cp.add(new DirectInfo<>(cp, tag, readLong(bytecode, u)));
+                cp.add(null);
+                i++;
+                u += 8;
+                break;
+            case 6:  //double
+                cp.add(new DirectInfo<>(cp, tag, Double.longBitsToDouble(readLong(bytecode, u))));
+                cp.add(null);
+                i++;
+                u += 8;
+                break;
+            case 12: //name and type
+                cp.add(new IndexInfo2(cp, tag, readShort(bytecode, u), readShort(bytecode, u + 2)));
+                u += 4;
+                break;
+            case 1:  //utf8
+                int len = readShort(bytecode, u);
+                u += 2;
+                cp.add(new DirectInfo<>(cp, tag, readUTF(u, len, bytecode)));
+                u += len;
+                break;
+            case 16: //methodtype
+                cp.add(new IndexInfo(cp, tag, readShort(bytecode, u)));
+                u += 2;
+                break;
+            case 18: //indy
+                cp.add(new IndexInfo2(cp, tag, readShort(bytecode, u), readShort(bytecode, u + 2)) {
+                    @Override
+                    public String toString() {
+                        return "#" + index + ' ' + cp.get(index2).toString();
+                    }
+
+                });
+                u += 4;
+                break;
+            case 15: //methodhandle
+                int kind = readByte(bytecode, u);
+                assert kind >= 1 && kind <= 9 : kind;
+                cp.add(new IndexInfo2(cp, tag, kind, readShort(bytecode, u + 1)) {
+                    @Override
+                    public String toString() {
+                        return "#" + index + ' ' + cp.get(index2).toString();
+                    }
+                });
+
+                u += 3;
+                break;
+            default:
+                assert false : tag;
+                break;
+            }
+        }
+
+        readShort(bytecode, u); //access flags
+        u += 2; //access
+        int cls = readShort(bytecode, u);
+        u += 2; //this_class
+        thisClassName = cp.get(cls).toString();
+        u += 2; //super
+
+        int ifc = readShort(bytecode, u);
+        u += 2;
+        u += ifc * 2;
+
+        int fc = readShort(bytecode, u);
+        u += 2; //fields
+
+        for (int i = 0 ; i < fc ; i++) {
+            u += 2; //access
+            readShort(bytecode, u); //fieldname
+            u += 2; //name
+            u += 2; //descriptor
+            int ac = readShort(bytecode, u);
+            u += 2;
+            //field attributes
+            for (int j = 0; j < ac; j++) {
+                u += 2; //attribute name
+                int len = readInt(bytecode, u);
+                u += 4;
+                u += len;
+            }
+        }
+
+        int mc = readShort(bytecode, u);
+        u += 2;
+        for (int i = 0 ; i < mc ; i++) {
+            readShort(bytecode, u);
+            u += 2; //access
+
+            int methodNameIndex = readShort(bytecode, u);
+            u += 2;
+            final String methodName = cp.get(methodNameIndex).toString();
+
+            int methodDescIndex = readShort(bytecode, u);
+            u += 2;
+            final String methodDesc = cp.get(methodDescIndex).toString();
+
+            int ac = readShort(bytecode, u);
+            u += 2;
+
+            //method attributes
+            for (int j = 0; j < ac; j++) {
+                int nameIndex = readShort(bytecode, u);
+                u += 2;
+                String attrName = cp.get(nameIndex).toString();
+
+                int attrLen = readInt(bytecode, u);
+                u += 4;
+
+                if ("Code".equals(attrName)) {
+                    readShort(bytecode, u);
+                    u += 2; //max stack
+                    readShort(bytecode, u);
+                    u += 2; //max locals
+                    int len = readInt(bytecode, u);
+                    u += 4;
+                    parseCode(bytecode, u, len, fullyQualifiedName(thisClassName, methodName, methodDesc));
+                    u += len;
+                    int elen = readShort(bytecode, u); //exception table length
+                    u += 2;
+                    u += elen * 8;
+
+                    //method attributes
+                    int ac2 = readShort(bytecode, u);
+                    u += 2;
+                    for (int k = 0; k < ac2; k++) {
+                        u += 2; //name;
+                        int aclen = readInt(bytecode, u);
+                        u += 4; //length
+                        u += aclen; //bytes;
+                    }
+                } else {
+                    u += attrLen;
+                }
+            }
+        }
+
+        int ac = readShort(bytecode, u);
+        u += 2;
+        //other attributes
+        for (int i = 0 ; i < ac ; i++) {
+            readShort(bytecode, u); //name index
+            u += 2;
+            int len = readInt(bytecode, u);
+            u += 4;
+            u += len;
+            //attribute
+        }
+
+        return thisClassName;
+    }
+
+    private static String fullyQualifiedName(final String className, final String methodName, final String methodDesc) {
+        return className + '.' + methodName + methodDesc;
+    }
+
+    private void parseCode(final byte[] bytecode, final int index, final int len, final String desc) {
+        final List<Label> labels = new ArrayList<>();
+        labelMap.put(desc, labels);
+
+        boolean wide = false;
+
+        for (int i = index; i < index + len;) {
+            int opcode = bytecode[i];
+            labels.add(new NashornLabel(opcode, i - index));
+
+            switch (opcode & 0xff) {
+            case 0xc4: //wide
+                wide = true;
+                i += 1;
+                break;
+            case 0xa9: //ret
+                i += wide ? 4 : 2;
+                break;
+            case 0xab: //lookupswitch
+                i += 1;
+                while (((i - index) & 3) != 0) {
+                    i++;
+                }
+                readInt(bytecode, i);
+                i += 4; //defaultbyte
+                int npairs = readInt(bytecode, i);
+                i += 4;
+                i += 8 * npairs;
+                break;
+            case 0xaa: //tableswitch
+                i += 1;
+                while (((i - index) & 3) != 0) {
+                    i++;
+                }
+                readInt(bytecode, i); //default
+                i += 4;
+                int lo = readInt(bytecode, i);
+                i += 4;
+                int hi = readInt(bytecode, i);
+                i += 4;
+                i += 4 * (hi - lo + 1);
+                break;
+            case 0xc5: //multianewarray
+                i += 4;
+                break;
+            case 0x19: //aload (wide)
+            case 0x18: //dload
+            case 0x17: //fload
+            case 0x15: //iload
+            case 0x16: //lload
+            case 0x3a: //astore wide
+            case 0x39: //dstore
+            case 0x38: //fstore
+            case 0x36: //istore
+            case 0x37: //lstore
+                i += wide ? 3 : 2;
+                break;
+            case 0x10: //bipush
+            case 0x12: //ldc
+            case 0xbc: //anewarrayu
+                i += 2;
+                break;
+            case 0xb4: //getfield
+            case 0xb2: //getstatic
+            case 0xbd: //anewarray
+            case 0xc0: //checkcast
+            case 0xa5: //ifacmp_eq
+            case 0xa6: //ifacmp_ne
+            case 0x9f: //all ifs and ifcmps
+            case 0xa0:
+            case 0xa1:
+            case 0xa2:
+            case 0xa3:
+            case 0xa4:
+            case 0x99:
+            case 0x9a:
+            case 0x9b:
+            case 0x9c:
+            case 0x9d:
+            case 0x9e:
+            case 0xc7:
+            case 0xc6:
+            case 0xc1: //instanceof
+            case 0xa7: //goto
+            case 0xb7: //special
+            case 0xb8: //static
+            case 0xb6: //virtual
+            case 0xa8: //jsr
+            case 0x13: //ldc_w
+            case 0x14: //ldc2_w
+            case 0xbb: //new
+            case 0xb5: //putfield
+            case 0xb3: //putstatic
+            case 0x11: //sipush
+                i += 3;
+                break;
+            case 0x84: //iinc (wide)
+                i += wide ? 5 : 3;
+                break;
+            case 0xba: //indy
+            case 0xb9: //interface
+            case 0xc8:
+            case 0xc9:  //jsr_w
+                i += 5; //goto_w
+                break;
+            default:
+                i++;
+                break;
+            }
+
+            if (wide) {
+                wide = false;
+            }
+        }
+    }
+
+    @Override
+    public void accept(final ClassVisitor classVisitor, Attribute[] attrs, final int flags) {
+        super.accept(classVisitor, attrs, flags);
+    }
+
+    @Override
+    protected Label readLabel(final int offset, final Label[] labels) {
+        Label label = super.readLabel(offset, labels);
+        label.info = (int)offset;
+        return label;
+    }
+
+    private abstract static class Constant {
+        protected ArrayList<Constant> cp;
+        protected int tag;
+        protected Constant(final ArrayList<Constant> cp, int tag) {
+            this.cp = cp;
+            this.tag = tag;
+        }
+
+        @SuppressWarnings("unused")
+        final String getType() {
+            String str = type[tag];
+            while (str.length() < 16) {
+                str += " ";
+            }
+            return str;
+        }
+    }
+
+    private static class IndexInfo extends Constant {
+        protected final int index;
+
+        IndexInfo(final ArrayList<Constant> cp, int tag, int index) {
+            super(cp, tag);
+            this.index = index;
+        }
+
+        @Override
+        public String toString() {
+            return cp.get(index).toString();
+        }
+    }
+
+    private static class IndexInfo2 extends IndexInfo {
+        protected final int index2;
+
+        IndexInfo2(final ArrayList<Constant> cp, int tag, int index, int index2) {
+            super(cp, tag, index);
+            this.index2 = index2;
+        }
+
+        @Override
+        public String toString() {
+            return super.toString() + ' ' + cp.get(index2).toString();
+        }
+    }
+
+    private static class DirectInfo<T> extends Constant {
+        protected final T info;
+
+        DirectInfo(final ArrayList<Constant> cp, int tag, T info) {
+            super(cp, tag);
+            this.info = info;
+        }
+
+        @Override
+        public String toString() {
+            return info.toString();// + " [class=" + info.getClass().getSimpleName() + ']';
+        }
+    }
+
+    private static String type[] = {
+        //0
+        "<error>",
+        //1
+        "UTF8",
+        //2
+        "<error>",
+        //3
+        "Integer",
+        //4
+        "Float",
+        //5
+        "Long",
+        //6
+        "Double",
+        //7
+        "Class",
+        //8
+        "String",
+        //9
+        "Fieldref",
+        //10
+        "Methodref",
+        //11
+        "InterfaceMethodRef",
+        //12
+        "NameAndType",
+        //13
+        "<error>",
+        //14
+        "<error>",
+        //15
+        "MethodHandle",
+        //16
+        "MethodType",
+        //17
+        "<error>",
+        //18
+        "Invokedynamic"
+    };
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/ir/debug/NashornTextifier.java	Wed Feb 26 13:17:57 2014 +0100
@@ -0,0 +1,1224 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.ir.debug;
+
+import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_PROGRAM_POINT_SHIFT;
+import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.FLAGS_MASK;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import jdk.internal.org.objectweb.asm.Attribute;
+import jdk.internal.org.objectweb.asm.Handle;
+import jdk.internal.org.objectweb.asm.Label;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.Type;
+import jdk.internal.org.objectweb.asm.signature.SignatureReader;
+import jdk.internal.org.objectweb.asm.util.Printer;
+import jdk.internal.org.objectweb.asm.util.TraceSignatureVisitor;
+import jdk.nashorn.internal.runtime.ScriptEnvironment;
+
+/**
+ * Pretty printer for --print-code.
+ * Also supports dot formats if --print-code has arguments
+ */
+public final class NashornTextifier extends Printer {
+
+    private String currentClassName;
+    private Iterator<Label> labelIter;
+    private Graph graph;
+    private String currentBlock;
+
+    private static final int INTERNAL_NAME = 0;
+    private static final int FIELD_DESCRIPTOR = 1;
+    private static final int FIELD_SIGNATURE = 2;
+    private static final int METHOD_DESCRIPTOR = 3;
+    private static final int METHOD_SIGNATURE = 4;
+    private static final int CLASS_SIGNATURE = 5;
+
+    private final String tab = "  ";
+    private final String tab2 = "    ";
+    private final String tab3 = "      ";
+
+    private Map<Label, String> labelNames;
+
+    private boolean localVarsStarted = false;
+
+    private NashornClassReader cr;
+    private ScriptEnvironment env;
+
+    /**
+     * Constructs a new {@link NashornTextifier}. <i>Subclasses must not use this
+     * constructor</i>. Instead, they must use the {@link #NashornTextifier(int)}
+     * version.
+     * @param env script environment
+     * @param cr a customized classreader for gathering, among other things, label
+     * information
+     */
+    public NashornTextifier(final ScriptEnvironment env, final NashornClassReader cr) {
+        this(Opcodes.ASM5);
+        this.env = env;
+        this.cr = cr;
+    }
+
+    private NashornTextifier(final ScriptEnvironment env, final NashornClassReader cr, final Iterator<Label> labelIter, final Graph graph) {
+        this(env, cr);
+        this.labelIter = labelIter;
+        this.graph = graph;
+    }
+
+    /**
+     * Constructs a new {@link NashornTextifier}.
+     *
+     * @param api
+     *            the ASM API version implemented by this visitor. Must be one
+     *            of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
+     */
+    protected NashornTextifier(final int api) {
+        super(api);
+    }
+
+    @Override
+    public void visit(final int version, final int access, final String name, final String signature, final String superName, final String[] interfaces) {
+        final int major = version & 0xFFFF;
+        final int minor = version >>> 16;
+
+        currentClassName = name;
+
+        final StringBuilder sb = new StringBuilder();
+        sb.append("// class version ").
+            append(major).
+            append('.').
+            append(minor).append(" (").
+            append(version).
+            append(")\n");
+
+        if ((access & Opcodes.ACC_DEPRECATED) != 0) {
+            sb.append("// DEPRECATED\n");
+        }
+
+        sb.append("// access flags 0x"). //TODO TRANSLATE TO WHAT THEY MEAN
+            append(Integer.toHexString(access).toUpperCase()).
+            append('\n');
+
+        appendDescriptor(sb, CLASS_SIGNATURE, signature);
+        if (signature != null) {
+            final TraceSignatureVisitor sv = new TraceSignatureVisitor(access);
+            final SignatureReader r = new SignatureReader(signature);
+            r.accept(sv);
+            sb.append("// declaration: ").
+                append(name).
+                append(sv.getDeclaration()).
+                append('\n');
+        }
+
+        appendAccess(sb, access & ~Opcodes.ACC_SUPER);
+        if ((access & Opcodes.ACC_ANNOTATION) != 0) {
+            sb.append("@interface ");
+        } else if ((access & Opcodes.ACC_INTERFACE) != 0) {
+            sb.append("interface ");
+        } else if ((access & Opcodes.ACC_ENUM) == 0) {
+            sb.append("class ");
+        }
+        appendDescriptor(sb, INTERNAL_NAME, name);
+
+        if (superName != null && !"java/lang/Object".equals(superName)) {
+            sb.append(" extends ");
+            appendDescriptor(sb, INTERNAL_NAME, superName);
+            sb.append(' ');
+        }
+        if (interfaces != null && interfaces.length > 0) {
+            sb.append(" implements ");
+            for (int i = 0; i < interfaces.length; ++i) {
+                appendDescriptor(sb, INTERNAL_NAME, interfaces[i]);
+                sb.append(' ');
+            }
+        }
+        sb.append(" {\n");
+
+        addText(sb);
+    }
+
+    @Override
+    public void visitSource(final String file, final String debug) {
+        final StringBuilder sb = new StringBuilder();
+        if (file != null) {
+            sb.append(tab).
+                append("// compiled from: ").
+                append(file).
+                append('\n');
+        }
+        if (debug != null) {
+            sb.append(tab).
+                append("// debug info: ").
+                append(debug).
+                append('\n');
+        }
+        if (sb.length() > 0) {
+            addText(sb);
+        }
+    }
+
+    @Override
+    public void visitOuterClass(final String owner, final String name, final String desc) {
+        final StringBuilder sb = new StringBuilder();
+        sb.append(tab).append("outer class ");
+        appendDescriptor(sb, INTERNAL_NAME, owner);
+        sb.append(' ');
+        if (name != null) {
+            sb.append(name).append(' ');
+        }
+        appendDescriptor(sb, METHOD_DESCRIPTOR, desc);
+        sb.append('\n');
+        addText(sb);
+    }
+
+    @Override
+    public NashornTextifier visitField(final int access, final String name, final String desc, final String signature, final Object value) {
+        final StringBuilder sb = new StringBuilder();
+//        sb.append('\n');
+        if ((access & Opcodes.ACC_DEPRECATED) != 0) {
+            sb.append(tab).append("// DEPRECATED\n");
+        }
+
+/*        sb.append(tab).
+            append("// access flags 0x").
+            append(Integer.toHexString(access).toUpperCase()).
+            append('\n');
+*/
+
+        if (signature != null) {
+            sb.append(tab);
+            appendDescriptor(sb, FIELD_SIGNATURE, signature);
+
+            TraceSignatureVisitor sv = new TraceSignatureVisitor(0);
+            SignatureReader r = new SignatureReader(signature);
+            r.acceptType(sv);
+            sb.append(tab).
+                append("// declaration: ").
+                append(sv.getDeclaration()).
+                append('\n');
+        }
+
+        sb.append(tab);
+        appendAccess(sb, access);
+
+        final String prunedDesc = desc.endsWith(";") ? desc.substring(0, desc.length() - 1) : desc;
+        appendDescriptor(sb, FIELD_DESCRIPTOR, prunedDesc);
+        sb.append(' ').append(name);
+        if (value != null) {
+            sb.append(" = ");
+            if (value instanceof String) {
+                sb.append('\"').append(value).append('\"');
+            } else {
+                sb.append(value);
+            }
+        }
+
+        sb.append(";\n");
+        addText(sb);
+
+        NashornTextifier t = createNashornTextifier();
+        addText(t.getText());
+
+        return t;
+    }
+
+    @Override
+    public NashornTextifier visitMethod(final int access, final String name, final String desc, final String signature, final String[] exceptions) {
+
+        graph = new Graph(name);
+
+        final List<Label> extraLabels = cr.getExtraLabels(currentClassName, name, desc);
+        this.labelIter = extraLabels == null ? null : extraLabels.iterator();
+
+        final StringBuilder sb = new StringBuilder();
+
+        sb.append('\n');
+        if ((access & Opcodes.ACC_DEPRECATED) != 0) {
+            sb.append(tab).
+                append("// DEPRECATED\n");
+        }
+
+        sb.append(tab).
+            append("// access flags 0x").
+            append(Integer.toHexString(access).toUpperCase()).
+            append('\n');
+
+        if (signature != null) {
+            sb.append(tab);
+            appendDescriptor(sb, METHOD_SIGNATURE, signature);
+
+            TraceSignatureVisitor v = new TraceSignatureVisitor(0);
+            SignatureReader r = new SignatureReader(signature);
+            r.accept(v);
+            String genericDecl = v.getDeclaration();
+            String genericReturn = v.getReturnType();
+            String genericExceptions = v.getExceptions();
+
+            sb.append(tab).
+                append("// declaration: ").
+                append(genericReturn).
+                append(' ').
+                append(name).
+                append(genericDecl);
+
+            if (genericExceptions != null) {
+                sb.append(" throws ").append(genericExceptions);
+            }
+            sb.append('\n');
+        }
+
+        sb.append(tab);
+        appendAccess(sb, access);
+        if ((access & Opcodes.ACC_NATIVE) != 0) {
+            sb.append("native ");
+        }
+        if ((access & Opcodes.ACC_VARARGS) != 0) {
+            sb.append("varargs ");
+        }
+        if ((access & Opcodes.ACC_BRIDGE) != 0) {
+            sb.append("bridge ");
+        }
+
+        sb.append(name);
+        appendDescriptor(sb, METHOD_DESCRIPTOR, desc);
+        if (exceptions != null && exceptions.length > 0) {
+            sb.append(" throws ");
+            for (int i = 0; i < exceptions.length; ++i) {
+                appendDescriptor(sb, INTERNAL_NAME, exceptions[i]);
+                sb.append(' ');
+            }
+        }
+
+        sb.append('\n');
+        addText(sb);
+
+        NashornTextifier t = createNashornTextifier();
+        addText(t.getText());
+        return t;
+    }
+
+    @Override
+    public void visitClassEnd() {
+        addText("}\n");
+    }
+
+    @Override
+    public void visitFieldEnd() {
+        //empty
+    }
+
+    @Override
+    public void visitParameter(final String name, final int access) {
+        final StringBuilder sb = new StringBuilder();
+        sb.append(tab2).append("// parameter ");
+        appendAccess(sb, access);
+        sb.append(' ').append((name == null) ? "<no name>" : name)
+                .append('\n');
+        addText(sb);
+    }
+
+    @Override
+    public void visitCode() {
+        //empty
+    }
+
+    @Override
+    public void visitFrame(final int type, final int nLocal, final Object[] local, final int nStack, final Object[] stack) {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("frame ");
+        switch (type) {
+        case Opcodes.F_NEW:
+        case Opcodes.F_FULL:
+            sb.append("full [");
+            appendFrameTypes(sb, nLocal, local);
+            sb.append("] [");
+            appendFrameTypes(sb, nStack, stack);
+            sb.append(']');
+            break;
+        case Opcodes.F_APPEND:
+            sb.append("append [");
+            appendFrameTypes(sb, nLocal, local);
+            sb.append(']');
+            break;
+        case Opcodes.F_CHOP:
+            sb.append("chop ").append(nLocal);
+            break;
+        case Opcodes.F_SAME:
+            sb.append("same");
+            break;
+        case Opcodes.F_SAME1:
+            sb.append("same1 ");
+            appendFrameTypes(sb, 1, stack);
+            break;
+        default:
+            assert false;
+            break;
+        }
+        sb.append('\n');
+        sb.append('\n');
+        addText(sb);
+    }
+
+    private StringBuilder appendOpcode(final StringBuilder sb, final int opcode) {
+        Label next = labelIter == null ? null : labelIter.next();
+        if (next instanceof NashornLabel) {
+            final int bci = next.getOffset();
+            if (bci != -1) {
+                String bcis = "" + bci;
+                for (int i = 0; i < 5 - bcis.length(); i++) {
+                    sb.append(' ');
+                }
+                sb.append(bcis);
+                sb.append(' ');
+            } else {
+                sb.append("       ");
+            }
+        }
+
+        return sb.append(tab2).append(OPCODES[opcode].toLowerCase());
+    }
+
+    @Override
+    public void visitInsn(final int opcode) {
+        final StringBuilder sb = new StringBuilder();
+        appendOpcode(sb, opcode).append('\n');
+        addText(sb);
+        checkNoFallThru(opcode, null);
+    }
+
+    @Override
+    public void visitIntInsn(final int opcode, final int operand) {
+        final StringBuilder sb = new StringBuilder();
+        appendOpcode(sb, opcode)
+                .append(' ')
+                .append(opcode == Opcodes.NEWARRAY ? TYPES[operand] : Integer
+                        .toString(operand)).append('\n');
+        addText(sb);
+    }
+
+    @Override
+    public void visitVarInsn(final int opcode, final int var) {
+        final StringBuilder sb = new StringBuilder();
+        appendOpcode(sb, opcode).append(' ').append(var).append('\n');
+        addText(sb);
+    }
+
+    @Override
+    public void visitTypeInsn(final int opcode, final String type) {
+        final StringBuilder sb = new StringBuilder();
+        appendOpcode(sb, opcode).append(' ');
+        appendDescriptor(sb, INTERNAL_NAME, type);
+        sb.append('\n');
+        addText(sb);
+    }
+
+    @Override
+    public void visitFieldInsn(final int opcode, final String owner, final String name, final String desc) {
+        final StringBuilder sb = new StringBuilder();
+        appendOpcode(sb, opcode).append(' ');
+        appendDescriptor(sb, INTERNAL_NAME, owner);
+        sb.append('.').append(name).append(" : ");
+        appendDescriptor(sb, FIELD_DESCRIPTOR, desc);
+        sb.append('\n');
+        addText(sb);
+    }
+
+    @Override
+    public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc, final boolean itf) {
+        final StringBuilder sb = new StringBuilder();
+        appendOpcode(sb, opcode).append(' ');
+        appendDescriptor(sb, INTERNAL_NAME, owner);
+        sb.append('.').append(name);
+        appendDescriptor(sb, METHOD_DESCRIPTOR, desc);
+        sb.append('\n');
+        addText(sb);
+    }
+
+    @Override
+    public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
+        final StringBuilder sb = new StringBuilder();
+
+        appendOpcode(sb, Opcodes.INVOKEDYNAMIC).append(' ');
+        sb.append(name);
+        appendDescriptor(sb, METHOD_DESCRIPTOR, desc);
+        final int len = sb.length();
+        for (int i = 0; i < 80 - len ; i++) {
+            sb.append(' ');
+        }
+        sb.append(" [");
+        appendHandle(sb, bsm);
+        sb.append(" args=");
+        if (bsmArgs.length == 0) {
+            sb.append("none");
+        } else {
+            for (final Object cst : bsmArgs) {
+                if (cst instanceof String) {
+                    appendStr(sb, (String)cst);
+                } else if (cst instanceof Type) {
+                    sb.append(((Type)cst).getDescriptor()).append(".class");
+                } else if (cst instanceof Handle) {
+                    appendHandle(sb, (Handle)cst);
+                } else if (cst instanceof Integer) {
+                    int c = (Integer)cst;
+                    int pp = c >> CALLSITE_PROGRAM_POINT_SHIFT;
+                    if (pp != 0) {
+                        sb.append("pp=").append(pp).append(' ');
+                    }
+                    sb.append("0x").append(Integer.toHexString(c & FLAGS_MASK));
+                } else {
+                    sb.append(cst);
+                }
+                sb.append(", ");
+            }
+            sb.setLength(sb.length() - 2);
+        }
+
+        sb.append("]\n");
+        addText(sb);
+    }
+
+    private static final boolean noFallThru(final int opcode) {
+        switch (opcode) {
+        case Opcodes.GOTO:
+        case Opcodes.ATHROW:
+        case Opcodes.ARETURN:
+        case Opcodes.IRETURN:
+        case Opcodes.LRETURN:
+        case Opcodes.FRETURN:
+        case Opcodes.DRETURN:
+            return true;
+        default:
+            return false;
+        }
+    }
+
+    private void checkNoFallThru(final int opcode, final String to) {
+        if (noFallThru(opcode)) {
+            graph.setNoFallThru(currentBlock);
+        }
+
+        if (currentBlock != null && to != null) {
+            graph.addEdge(currentBlock, to);
+        }
+    }
+
+    @Override
+    public void visitJumpInsn(final int opcode, final Label label) {
+        final StringBuilder sb = new StringBuilder();
+        appendOpcode(sb, opcode).append(' ');
+        String to = appendLabel(sb, label);
+        sb.append('\n');
+        addText(sb);
+        checkNoFallThru(opcode, to);
+    }
+
+    private void addText(final Object t) {
+        text.add(t);
+        if (currentBlock != null) {
+            graph.addText(currentBlock, t.toString());
+        }
+    }
+
+    @Override
+    public void visitLabel(final Label label) {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("\n");
+        String name = appendLabel(sb, label);
+        sb.append(" [bci=");
+        sb.append(label.info);
+        sb.append("]");
+        sb.append("\n");
+
+        graph.addNode(name);
+        if (currentBlock != null && !graph.isNoFallThru(currentBlock)) {
+            graph.addEdge(currentBlock, name);
+        }
+        currentBlock = name;
+        addText(sb);
+    }
+
+    @Override
+    public void visitLdcInsn(final Object cst) {
+        final StringBuilder sb = new StringBuilder();
+        appendOpcode(sb, Opcodes.LDC).append(' ');
+        if (cst instanceof String) {
+            appendStr(sb, (String) cst);
+        } else if (cst instanceof Type) {
+            sb.append(((Type) cst).getDescriptor()).append(".class");
+        } else {
+            sb.append(cst);
+        }
+        sb.append('\n');
+        addText(sb);
+    }
+
+    @Override
+    public void visitIincInsn(final int var, final int increment) {
+        final StringBuilder sb = new StringBuilder();
+        appendOpcode(sb, Opcodes.IINC).append(' ');
+        sb.append(var).append(' ')
+                .append(increment).append('\n');
+        addText(sb);
+    }
+
+    @Override
+    public void visitTableSwitchInsn(final int min, final int max, final Label dflt, final Label... labels) {
+        final StringBuilder sb = new StringBuilder();
+        appendOpcode(sb, Opcodes.TABLESWITCH).append(' ');
+        for (int i = 0; i < labels.length; ++i) {
+            sb.append(tab3).append(min + i).append(": ");
+            final String to = appendLabel(sb, labels[i]);
+            graph.addEdge(currentBlock, to);
+            sb.append('\n');
+        }
+        sb.append(tab3).append("default: ");
+        appendLabel(sb, dflt);
+        sb.append('\n');
+        addText(sb);
+    }
+
+    @Override
+    public void visitLookupSwitchInsn(final Label dflt, final int[] keys, final Label[] labels) {
+        final StringBuilder sb = new StringBuilder();
+        appendOpcode(sb, Opcodes.LOOKUPSWITCH).append(' ');
+        for (int i = 0; i < labels.length; ++i) {
+            sb.append(tab3).append(keys[i]).append(": ");
+            final String to = appendLabel(sb, labels[i]);
+            graph.addEdge(currentBlock, to);
+            sb.append('\n');
+        }
+        sb.append(tab3).append("default: ");
+        final String to = appendLabel(sb, dflt);
+        graph.addEdge(currentBlock, to);
+        sb.append('\n');
+        addText(sb.toString());
+    }
+
+    @Override
+    public void visitMultiANewArrayInsn(final String desc, final int dims) {
+        final StringBuilder sb = new StringBuilder();
+        appendOpcode(sb, Opcodes.MULTIANEWARRAY).append(' ');
+        appendDescriptor(sb, FIELD_DESCRIPTOR, desc);
+        sb.append(' ').append(dims).append('\n');
+        addText(sb);
+    }
+
+    @Override
+    public void visitTryCatchBlock(final Label start, final Label end, final Label handler, final String type) {
+        final StringBuilder sb = new StringBuilder();
+        sb.append(tab2).append("try ");
+        final String from = appendLabel(sb, start);
+        sb.append(' ');
+        appendLabel(sb, end);
+        sb.append(' ');
+        final String to = appendLabel(sb, handler);
+        sb.append(' ');
+        appendDescriptor(sb, INTERNAL_NAME, type);
+        sb.append('\n');
+        addText(sb);
+        graph.setIsCatch(to, type);
+        graph.addTryCatch(from, to);
+    }
+
+    @Override
+    public void visitLocalVariable(final String name, final String desc,final String signature, final Label start, final Label end, final int index) {
+
+        final StringBuilder sb = new StringBuilder();
+        if (!localVarsStarted) {
+            text.add("\n");
+            localVarsStarted = true;
+            graph.addNode("vars");
+            currentBlock = "vars";
+        }
+
+        sb.append(tab2).append("local ").append(name).append(' ');
+        final int len = sb.length();
+        for (int i = 0; i < 25 - len; i++) {
+            sb.append(' ');
+        }
+        String label;
+
+        label = appendLabel(sb, start);
+        for (int i = 0; i < 5 - label.length(); i++) {
+            sb.append(' ');
+        }
+        label = appendLabel(sb, end);
+        for (int i = 0; i < 5 - label.length(); i++) {
+            sb.append(' ');
+        }
+
+        sb.append(index).append(tab2);
+
+        appendDescriptor(sb, FIELD_DESCRIPTOR, desc);
+        sb.append('\n');
+
+        if (signature != null) {
+            sb.append(tab2);
+            appendDescriptor(sb, FIELD_SIGNATURE, signature);
+
+            TraceSignatureVisitor sv = new TraceSignatureVisitor(0);
+            SignatureReader r = new SignatureReader(signature);
+            r.acceptType(sv);
+            sb.append(tab2).append("// declaration: ")
+                    .append(sv.getDeclaration()).append('\n');
+        }
+        addText(sb.toString());
+    }
+
+    @Override
+    public void visitLineNumber(final int line, final Label start) {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("<line ");
+        sb.append(line);
+        sb.append(">\n");
+        addText(sb.toString());
+    }
+
+    @Override
+    public void visitMaxs(final int maxStack, final int maxLocals) {
+        final StringBuilder sb = new StringBuilder();
+        sb.append('\n');
+        sb.append(tab2).append("max stack  = ").append(maxStack);
+        sb.append(", max locals = ").append(maxLocals).append('\n');
+        addText(sb.toString());
+    }
+
+    private void printToDir(final Graph g) {
+        if (env._print_code_dir != null) {
+            File dir = new File(env._print_code_dir);
+            if (!dir.exists() && !dir.mkdirs()) {
+                throw new RuntimeException(dir.toString());
+            }
+
+            File file;
+            int uniqueId = 0;
+            do {
+                String fileName = g.getName() + (uniqueId == 0 ? "" : "_" + uniqueId) +  ".dot";
+                file = new File(dir, fileName);
+                uniqueId++;
+            } while (file.exists());
+
+            try (PrintWriter pw = new PrintWriter(new FileOutputStream(file))) {
+                pw.println(g);
+            } catch (FileNotFoundException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    @Override
+    public void visitMethodEnd() {
+        //here we need to do several bytecode guesses best upon the ldc instructions.
+        //for each instruction, assign bci. for an ldc/w/2w, guess a byte and keep
+        //iterating. if the next label is wrong, backtrack.
+        if (env._print_code_func == null || env._print_code_func.equals(graph.getName())) {
+            if (env._print_code_dir != null) {
+                printToDir(graph);
+            }
+        }
+    }
+
+    /**
+     * Creates a new TraceVisitor instance.
+     *
+     * @return a new TraceVisitor.
+     */
+    protected NashornTextifier createNashornTextifier() {
+        return new NashornTextifier(env, cr, labelIter, graph);
+    }
+
+    private static void appendDescriptor(final StringBuilder sb, final int type, final String desc) {
+        if (desc != null) {
+            if (type == CLASS_SIGNATURE || type == FIELD_SIGNATURE || type == METHOD_SIGNATURE) {
+                sb.append("// signature ").append(desc).append('\n');
+            } else {
+                appendShortDescriptor(sb, desc);
+            }
+        }
+    }
+
+    private String appendLabel(final StringBuilder sb, final Label l) {
+        if (labelNames == null) {
+            labelNames = new HashMap<>();
+        }
+        String name = labelNames.get(l);
+        if (name == null) {
+            name = "L" + labelNames.size();
+            labelNames.put(l, name);
+        }
+        sb.append(name);
+        return name;
+    }
+
+    private static void appendHandle(final StringBuilder sb, final Handle h) {
+        switch (h.getTag()) {
+        case Opcodes.H_GETFIELD:
+            sb.append("getfield");
+            break;
+        case Opcodes.H_GETSTATIC:
+            sb.append("getstatic");
+            break;
+        case Opcodes.H_PUTFIELD:
+            sb.append("putfield");
+            break;
+        case Opcodes.H_PUTSTATIC:
+            sb.append("putstatic");
+            break;
+        case Opcodes.H_INVOKEINTERFACE:
+            sb.append("interface");
+            break;
+        case Opcodes.H_INVOKESPECIAL:
+            sb.append("special");
+            break;
+        case Opcodes.H_INVOKESTATIC:
+            sb.append("static");
+            break;
+        case Opcodes.H_INVOKEVIRTUAL:
+            sb.append("virtual");
+            break;
+        case Opcodes.H_NEWINVOKESPECIAL:
+            sb.append("new_special");
+            break;
+        default:
+            assert false;
+            break;
+        }
+        sb.append(" '");
+        sb.append(h.getName());
+        sb.append("'");
+    }
+
+    private static void appendAccess(final StringBuilder sb, final int access) {
+        if ((access & Opcodes.ACC_PUBLIC) != 0) {
+            sb.append("public ");
+        }
+        if ((access & Opcodes.ACC_PRIVATE) != 0) {
+            sb.append("private ");
+        }
+        if ((access & Opcodes.ACC_PROTECTED) != 0) {
+            sb.append("protected ");
+        }
+        if ((access & Opcodes.ACC_FINAL) != 0) {
+            sb.append("final ");
+        }
+        if ((access & Opcodes.ACC_STATIC) != 0) {
+            sb.append("static ");
+        }
+        if ((access & Opcodes.ACC_SYNCHRONIZED) != 0) {
+            sb.append("synchronized ");
+        }
+        if ((access & Opcodes.ACC_VOLATILE) != 0) {
+            sb.append("volatile ");
+        }
+        if ((access & Opcodes.ACC_TRANSIENT) != 0) {
+            sb.append("transient ");
+        }
+        if ((access & Opcodes.ACC_ABSTRACT) != 0) {
+            sb.append("abstract ");
+        }
+        if ((access & Opcodes.ACC_STRICT) != 0) {
+            sb.append("strictfp ");
+        }
+        if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
+            sb.append("synthetic ");
+        }
+        if ((access & Opcodes.ACC_MANDATED) != 0) {
+            sb.append("mandated ");
+        }
+        if ((access & Opcodes.ACC_ENUM) != 0) {
+            sb.append("enum ");
+        }
+    }
+
+    private void appendFrameTypes(final StringBuilder sb, final int n, final Object[] o) {
+        for (int i = 0; i < n; ++i) {
+            if (i > 0) {
+                sb.append(' ');
+            }
+            if (o[i] instanceof String) {
+                String desc = (String) o[i];
+                if (desc.startsWith("[")) {
+                    appendDescriptor(sb, FIELD_DESCRIPTOR, desc);
+                } else {
+                    appendDescriptor(sb, INTERNAL_NAME, desc);
+                }
+            } else if (o[i] instanceof Integer) {
+                switch (((Integer)o[i]).intValue()) {
+                case 0:
+                    appendDescriptor(sb, FIELD_DESCRIPTOR, "T");
+                    break;
+                case 1:
+                    appendDescriptor(sb, FIELD_DESCRIPTOR, "I");
+                    break;
+                case 2:
+                    appendDescriptor(sb, FIELD_DESCRIPTOR, "F");
+                    break;
+                case 3:
+                    appendDescriptor(sb, FIELD_DESCRIPTOR, "D");
+                    break;
+                case 4:
+                    appendDescriptor(sb, FIELD_DESCRIPTOR, "J");
+                    break;
+                case 5:
+                    appendDescriptor(sb, FIELD_DESCRIPTOR, "N");
+                    break;
+                case 6:
+                    appendDescriptor(sb, FIELD_DESCRIPTOR, "U");
+                    break;
+                default:
+                    assert false;
+                    break;
+                }
+            } else {
+                appendLabel(sb, (Label) o[i]);
+            }
+        }
+    }
+
+    private static void appendShortDescriptor(final StringBuilder sb, final String desc) {
+        //final StringBuilder buf = new StringBuilder();
+        if (desc.charAt(0) == '(') {
+            for (int i = 0; i < desc.length(); i++) {
+                if (desc.charAt(i) == 'L') {
+                    int slash = i;
+                    while (desc.charAt(i) != ';') {
+                        i++;
+                        if (desc.charAt(i) == '/') {
+                            slash = i;
+                        }
+                    }
+                    sb.append(desc.substring(slash + 1, i)).append(';');
+                } else {
+                    sb.append(desc.charAt(i));
+                }
+            }
+        } else {
+            int lastSlash = desc.lastIndexOf('/');
+            sb.append(lastSlash == -1 ? desc : desc.substring(lastSlash + 1));
+        }
+    }
+
+    private static void appendStr(final StringBuilder sb, final String s) {
+        sb.append('\"');
+        for (int i = 0; i < s.length(); ++i) {
+            char c = s.charAt(i);
+            if (c == '\n') {
+                sb.append("\\n");
+            } else if (c == '\r') {
+                sb.append("\\r");
+            } else if (c == '\\') {
+                sb.append("\\\\");
+            } else if (c == '"') {
+                sb.append("\\\"");
+            } else if (c < 0x20 || c > 0x7f) {
+                sb.append("\\u");
+                if (c < 0x10) {
+                    sb.append("000");
+                } else if (c < 0x100) {
+                    sb.append("00");
+                } else if (c < 0x1000) {
+                    sb.append('0');
+                }
+                sb.append(Integer.toString(c, 16));
+            } else {
+                sb.append(c);
+            }
+        }
+        sb.append('\"');
+    }
+
+    private static class Graph {
+        private final LinkedHashSet<String> nodes;
+        private final Map<String, StringBuilder> contents;
+        private final Map<String, Set<String>> edges;
+        private final Set<String> hasPreds;
+        private final Set<String> noFallThru;
+        private final Map<String, String> catches;
+        private final Map<String, Set<String>> exceptionMap; //maps catch nodes to all their trys that can reach them
+        private final String name;
+
+        private static final String LEFT_ALIGN      = "\\l";
+        private static final String COLOR_CATCH     = "\"#ee9999\"";
+        private static final String COLOR_ORPHAN    = "\"#9999bb\"";
+        private static final String COLOR_DEFAULT   = "\"#99bb99\"";
+        private static final String COLOR_LOCALVARS = "\"#999999\"";
+
+        Graph(String name) {
+            this.name         = name;
+            this.nodes        = new LinkedHashSet<>();
+            this.contents     = new HashMap<>();
+            this.edges        = new HashMap<>();
+            this.hasPreds     = new HashSet<>();
+            this.catches      = new HashMap<>();
+            this.noFallThru   = new HashSet<>();
+            this.exceptionMap = new HashMap<>();
+         }
+
+        void addEdge(String from, String to) {
+            Set<String> edgeSet = edges.get(from);
+            if (edgeSet == null) {
+                edgeSet = new LinkedHashSet<>();
+                edges.put(from, edgeSet);
+            }
+            edgeSet.add(to);
+            hasPreds.add(to);
+        }
+
+        void addTryCatch(String tryNode, String catchNode) {
+            Set<String> tryNodes = exceptionMap.get(catchNode);
+            if (tryNodes == null) {
+                tryNodes = new HashSet<>();
+                exceptionMap.put(catchNode, tryNodes);
+            }
+            if (!tryNodes.contains(tryNode)) {
+                addEdge(tryNode, catchNode);
+            }
+            tryNodes.add(tryNode);
+        }
+
+        void addNode(String node) {
+            assert !nodes.contains(node);
+            nodes.add(node);
+        }
+
+        void setNoFallThru(String node) {
+            noFallThru.add(node);
+        }
+
+        boolean isNoFallThru(String node) {
+            return noFallThru.contains(node);
+        }
+
+        void setIsCatch(String node, String exception) {
+            catches.put(node, exception);
+        }
+
+        String getName() {
+            return name;
+        }
+
+        void addText(final String node, final String text) {
+            StringBuilder sb = contents.get(node);
+            if (sb == null) {
+                sb = new StringBuilder();
+            }
+
+            for (int i = 0; i < text.length(); i++) {
+                switch (text.charAt(i)) {
+                case '\n':
+                    sb.append(LEFT_ALIGN);
+                    break;
+                case '"':
+                    sb.append("'");
+                    break;
+                default:
+                    sb.append(text.charAt(i));
+                    break;
+                }
+           }
+
+            contents.put(node, sb);
+        }
+
+        @Override
+        public String toString() {
+
+            StringBuilder sb = new StringBuilder();
+            sb.append("digraph " + name + " {");
+            sb.append("\n");
+            sb.append("\tgraph [fontname=courier]\n");
+            sb.append("\tnode [style=filled,color="+COLOR_DEFAULT+",fontname=courier]\n");
+            sb.append("\tedge [fontname=courier]\n\n");
+
+            for (final String node : nodes) {
+                sb.append("\t");
+                sb.append(node);
+                sb.append(" [");
+                sb.append("id=");
+                sb.append(node);
+                sb.append(", label=\"");
+                String c = contents.get(node).toString();
+                if (c.startsWith(LEFT_ALIGN)) {
+                    c = c.substring(LEFT_ALIGN.length());
+                }
+                final String ex = catches.get(node);
+                if (ex != null) {
+                    sb.append("*** CATCH: ").append(ex).append(" ***\n");
+                }
+                sb.append(c);
+                sb.append("\"]\n");
+            }
+
+            for (final String from : edges.keySet()) {
+                for (String to : edges.get(from)) {
+                    sb.append("\t");
+                    sb.append(from);
+                    sb.append(" -> ");
+                    sb.append(to);
+                    sb.append("[label=\"");
+                    sb.append(to);
+                    sb.append("\"");
+                    if (catches.get(to) != null) {
+                        sb.append(", color=red, style=dashed");
+                    }
+                    sb.append(']');
+                    sb.append(";\n");
+                }
+            }
+
+            sb.append("\n");
+            for (final String node : nodes) {
+                sb.append("\t");
+                sb.append(node);
+                sb.append(" [shape=box");
+                if (catches.get(node) != null) {
+                    sb.append(", color=" + COLOR_CATCH);
+                } else if ("vars".equals(node)) {
+                    sb.append(", shape=hexagon, color=" + COLOR_LOCALVARS);
+                } else if (!hasPreds.contains(node)) {
+                    sb.append(", color=" + COLOR_ORPHAN);
+                }
+                sb.append("]\n");
+            }
+
+            sb.append("}\n");
+            return sb.toString();
+        }
+    }
+
+    static class NashornLabel extends Label {
+        final Label label;
+        final int   bci;
+        final int   opcode;
+
+        NashornLabel(final Label label, final int bci) {
+            this.label = label;
+            this.bci   = bci;
+            this.opcode = -1;
+        }
+
+        //not an ASM label
+        NashornLabel(final int opcode, final int bci) {
+            this.opcode = opcode;
+            this.bci = bci;
+            this.label = null;
+        }
+
+        Label getLabel() {
+            return label;
+        }
+
+        @Override
+        public int getOffset() {
+            return bci;
+        }
+
+        @Override
+        public String toString() {
+            return "label " + bci;
+        }
+    }
+
+    @Override
+    public Printer visitAnnotationDefault() {
+        throw new AssertionError();
+    }
+
+    @Override
+    public Printer visitClassAnnotation(String arg0, boolean arg1) {
+        return this;
+    }
+
+    @Override
+    public void visitClassAttribute(Attribute arg0) {
+        throw new AssertionError();
+    }
+
+    @Override
+    public Printer visitFieldAnnotation(String arg0, boolean arg1) {
+        throw new AssertionError();
+    }
+
+    @Override
+    public void visitFieldAttribute(Attribute arg0) {
+        throw new AssertionError();
+    }
+
+    @Override
+    public Printer visitMethodAnnotation(String arg0, boolean arg1) {
+        return this;
+    }
+
+    @Override
+    public void visitMethodAttribute(Attribute arg0) {
+        throw new AssertionError();
+    }
+
+    @Override
+    public Printer visitParameterAnnotation(int arg0, String arg1, boolean arg2) {
+        throw new AssertionError();
+    }
+
+    @Override
+    public void visit(String arg0, Object arg1) {
+        throw new AssertionError();
+    }
+
+    @Override
+    public Printer visitAnnotation(String arg0, String arg1) {
+        throw new AssertionError();
+    }
+
+    @Override
+    public void visitAnnotationEnd() {
+        //empty
+    }
+
+    @Override
+    public Printer visitArray(String arg0) {
+        throw new AssertionError();
+    }
+
+    @Override
+    public void visitEnum(String arg0, String arg1, String arg2) {
+        throw new AssertionError();
+    }
+
+    @Override
+    public void visitInnerClass(String arg0, String arg1, String arg2, int arg3) {
+        throw new AssertionError();
+    }
+}
--- a/nashorn/src/jdk/nashorn/internal/ir/debug/PrintVisitor.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/ir/debug/PrintVisitor.java	Wed Feb 26 13:17:57 2014 +0100
@@ -215,7 +215,7 @@
         unaryNode.toString(sb, new Runnable() {
             @Override
             public void run() {
-                unaryNode.rhs().accept(PrintVisitor.this);
+                unaryNode.getExpression().accept(PrintVisitor.this);
             }
         });
         return false;
--- a/nashorn/src/jdk/nashorn/internal/lookup/Lookup.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/lookup/Lookup.java	Wed Feb 26 13:17:57 2014 +0100
@@ -68,6 +68,12 @@
     /** Method handle to the most generic of setters, the one that takes an Object */
     public static final MethodType SET_OBJECT_TYPE = MH.type(void.class, Object.class, Object.class);
 
+    /** Method handle to the primitive getters, the one that returns an long/int/double */
+    public static final MethodType GET_PRIMITIVE_TYPE = MH.type(long.class, Object.class);
+
+    /** Method handle to the primitive getters, the one that returns an long/int/double */
+    public static final MethodType SET_PRIMITIVE_TYPE = MH.type(void.class, Object.class, long.class);
+
     private Lookup() {
     }
 
--- a/nashorn/src/jdk/nashorn/internal/lookup/MethodHandleFactory.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/lookup/MethodHandleFactory.java	Wed Feb 26 13:17:57 2014 +0100
@@ -125,8 +125,11 @@
         return FUNC;
     }
 
-    private static final MethodHandle TRACE        = STANDARD.findStatic(LOOKUP, MethodHandleFactory.class, "traceArgs",   MethodType.methodType(void.class, DebugLogger.class, String.class, int.class, Object[].class));
-    private static final MethodHandle TRACE_RETURN = STANDARD.findStatic(LOOKUP, MethodHandleFactory.class, "traceReturn", MethodType.methodType(Object.class, DebugLogger.class, Object.class));
+    private static final MethodHandle TRACE             = STANDARD.findStatic(LOOKUP, MethodHandleFactory.class, "traceArgs",   MethodType.methodType(void.class, DebugLogger.class, String.class, int.class, Object[].class));
+    private static final MethodHandle TRACE_RETURN      = STANDARD.findStatic(LOOKUP, MethodHandleFactory.class, "traceReturn", MethodType.methodType(Object.class, DebugLogger.class, Object.class));
+    private static final MethodHandle TRACE_RETURN_VOID = STANDARD.findStatic(LOOKUP, MethodHandleFactory.class, "traceReturnVoid", MethodType.methodType(void.class, DebugLogger.class));
+
+    private static final String VOID_TAG = "[VOID]";
 
     /**
      * Tracer that is applied before a value is returned from the traced function. It will output the return
@@ -136,11 +139,19 @@
      * @return return value unmodified
      */
     static Object traceReturn(final DebugLogger logger, final Object value) {
-        final String str = "\treturn: " + stripName(value) + " [type=" + (value == null ? "null" : stripName(value.getClass()) + ']');
+        final String str = "\treturn" +
+                (VOID_TAG.equals(value) ?
+                    ";" :
+                    (" " + stripName(value) + "; // [type=" + (value == null ? "null" : stripName(value.getClass()) + ']')));
         logger.log(TRACE_LEVEL, str);
+        logger.log(TRACE_LEVEL, Debug.firstJSFrame());
         return value;
     }
 
+    static void traceReturnVoid(final DebugLogger logger) {
+        traceReturn(logger, VOID_TAG);
+    }
+
     /**
      * Tracer that is applied before a function is called, printing the arguments
      *
@@ -236,7 +247,9 @@
     public static MethodHandle addDebugPrintout(final DebugLogger logger, final MethodHandle mh, final int paramStart, final boolean printReturnValue, final Object tag) {
         final MethodType type = mh.type();
 
-        if (logger != null && logger.levelAbove(TRACE_LEVEL)) {
+        //if there is no logger, or if it's set to log only coarser events
+        //than the trace level, skip and return
+        if (logger != null && logger.levelCoarserThan(TRACE_LEVEL)) {
             return mh;
         }
 
@@ -253,11 +266,15 @@
                 asType(type.changeReturnType(void.class)));
 
         final Class<?> retType = type.returnType();
-        if (retType != void.class && printReturnValue) {
-            final MethodHandle traceReturn = MethodHandles.insertArguments(TRACE_RETURN, 0, logger);
-            trace = MethodHandles.filterReturnValue(trace,
-                    traceReturn.asType(
-                        traceReturn.type().changeParameterType(0, retType).changeReturnType(retType)));
+        if (printReturnValue) {
+            if (retType != void.class) {
+                final MethodHandle traceReturn = MethodHandles.insertArguments(TRACE_RETURN, 0, logger);
+                trace = MethodHandles.filterReturnValue(trace,
+                        traceReturn.asType(
+                            traceReturn.type().changeParameterType(0, retType).changeReturnType(retType)));
+            } else {
+                trace = MethodHandles.filterReturnValue(trace, MethodHandles.insertArguments(TRACE_RETURN_VOID, 0, logger));
+            }
         }
 
         return trace;
@@ -338,6 +355,11 @@
         }
 
         @Override
+        public MethodHandle catchException(final MethodHandle target, final Class<? extends Throwable> exType, final MethodHandle handler) {
+            return MethodHandles.catchException(target, exType, handler);
+        }
+
+        @Override
         public MethodHandle constant(final Class<?> type, final Object value) {
             return MethodHandles.constant(type, value);
         }
@@ -552,6 +574,12 @@
         }
 
         @Override
+        public MethodHandle catchException(final MethodHandle target, final Class<? extends Throwable> exType, final MethodHandle handler) {
+            final MethodHandle mh = super.catchException(target, exType, handler);
+            return debug(mh, "catchException", exType);
+        }
+
+        @Override
         public MethodHandle constant(final Class<?> type, final Object value) {
             final MethodHandle mh = super.constant(type, value);
             return debug(mh, "constant", type, value);
--- a/nashorn/src/jdk/nashorn/internal/lookup/MethodHandleFunctionality.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/lookup/MethodHandleFunctionality.java	Wed Feb 26 13:17:57 2014 +0100
@@ -152,6 +152,17 @@
     public MethodHandle throwException(Class<?> returnType, Class<? extends Throwable> exType);
 
     /**
+     * Wrapper for {@link java.lang.invoke.MethodHandles#catchException(MethodHandle, Class, MethodHandle)}
+     *
+     * @param target  target method
+     * @param exType  exception type
+     * @param handler the method handle to call when exception is thrown
+     *
+     * @return exception thrower method handle
+     */
+    public MethodHandle catchException(final MethodHandle target, final Class<? extends Throwable> exType, final MethodHandle handler);
+
+    /**
      * Wrapper for {@link java.lang.invoke.MethodHandles#constant(Class, Object)}
      *
      * @param type  type of constant
--- a/nashorn/src/jdk/nashorn/internal/objects/AccessorPropertyDescriptor.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/AccessorPropertyDescriptor.java	Wed Feb 26 13:17:57 2014 +0100
@@ -30,6 +30,7 @@
 import static jdk.nashorn.internal.runtime.ScriptRuntime.sameValue;
 
 import java.util.Objects;
+
 import jdk.nashorn.internal.objects.annotations.Property;
 import jdk.nashorn.internal.objects.annotations.ScriptClass;
 import jdk.nashorn.internal.runtime.JSType;
@@ -213,6 +214,11 @@
     }
 
     @Override
+    public String toString() {
+        return '[' + getClass().getSimpleName() + " {configurable=" + configurable + " enumerable=" + enumerable + " getter=" + get + " setter=" + set + "}]";
+    }
+
+    @Override
     public int hashCode() {
         int hash = 7;
         hash = 41 * hash + Objects.hashCode(this.configurable);
--- a/nashorn/src/jdk/nashorn/internal/objects/ArrayBufferView.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/ArrayBufferView.java	Wed Feb 26 13:17:57 2014 +0100
@@ -26,6 +26,7 @@
 package jdk.nashorn.internal.objects;
 
 import static jdk.nashorn.internal.runtime.ECMAErrors.rangeError;
+import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
 
 import jdk.nashorn.internal.objects.annotations.Attribute;
 import jdk.nashorn.internal.objects.annotations.Getter;
@@ -333,8 +334,9 @@
             length = lengthToInt(((NativeArray) arg0).getArray().length());
             dst = factory.construct(length);
         } else {
-            // Constructor(unsigned long length)
-            length = lengthToInt(JSType.toInt64(arg0));
+            // Constructor(unsigned long length). Treating infinity as 0 is a special case for ArrayBufferView.
+            final double dlen = JSType.toNumber(arg0);
+            length = lengthToInt(Double.isInfinite(dlen) ? 0L : JSType.toLong(dlen));
             return factory.construct(length);
         }
 
@@ -370,11 +372,11 @@
     private static void copyElements(final ArrayBufferView dest, final int length, final ScriptObject source, final int offset) {
         if (!dest.isFloatArray()) {
             for (int i = 0, j = offset; i < length; i++, j++) {
-                dest.set(j, source.getInt(i), false);
+                dest.set(j, source.getInt(i, INVALID_PROGRAM_POINT), false);
             }
         } else {
             for (int i = 0, j = offset; i < length; i++, j++) {
-                dest.set(j, source.getDouble(i), false);
+                dest.set(j, source.getDouble(i, INVALID_PROGRAM_POINT), false);
             }
         }
     }
--- a/nashorn/src/jdk/nashorn/internal/objects/DataPropertyDescriptor.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/DataPropertyDescriptor.java	Wed Feb 26 13:17:57 2014 +0100
@@ -28,6 +28,7 @@
 import static jdk.nashorn.internal.runtime.ScriptRuntime.sameValue;
 
 import java.util.Objects;
+
 import jdk.nashorn.internal.objects.annotations.Property;
 import jdk.nashorn.internal.objects.annotations.ScriptClass;
 import jdk.nashorn.internal.runtime.JSType;
@@ -201,6 +202,11 @@
     }
 
     @Override
+    public String toString() {
+        return '[' + getClass().getSimpleName() + " {configurable=" + configurable + " enumerable=" + enumerable + " writable=" + writable + " value=" + value + "}]";
+    }
+
+    @Override
     public int hashCode() {
         int hash = 5;
         hash = 43 * hash + Objects.hashCode(this.configurable);
--- a/nashorn/src/jdk/nashorn/internal/objects/GenericPropertyDescriptor.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/GenericPropertyDescriptor.java	Wed Feb 26 13:17:57 2014 +0100
@@ -26,6 +26,7 @@
 package jdk.nashorn.internal.objects;
 
 import java.util.Objects;
+
 import jdk.nashorn.internal.objects.annotations.Property;
 import jdk.nashorn.internal.objects.annotations.ScriptClass;
 import jdk.nashorn.internal.runtime.JSType;
@@ -180,6 +181,11 @@
     }
 
     @Override
+    public String toString() {
+        return '[' + getClass().getSimpleName() + " {configurable=" + configurable + " enumerable=" + enumerable + "}]";
+    }
+
+    @Override
     public int hashCode() {
         int hash = 7;
         hash = 97 * hash + Objects.hashCode(this.configurable);
--- a/nashorn/src/jdk/nashorn/internal/objects/Global.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/Global.java	Wed Feb 26 13:17:57 2014 +0100
@@ -74,6 +74,9 @@
  */
 @ScriptClass("Global")
 public final class Global extends ScriptObject implements GlobalObject, Scope {
+    // Placeholder value used in place of a location property (__FILE__, __DIR__, __LINE__)
+    private static final Object LOCATION_PROPERTY_PLACEHOLDER = new Object();
+
     private final InvokeByName TO_STRING = new InvokeByName("toString", ScriptObject.class);
     private final InvokeByName VALUE_OF  = new InvokeByName("valueOf",  ScriptObject.class);
 
@@ -139,11 +142,11 @@
 
     /** Value property NaN of the Global Object - ECMA 15.1.1.1 NaN */
     @Property(attributes = Attribute.NON_ENUMERABLE_CONSTANT)
-    public final Object NaN = Double.NaN;
+    public final double NaN = Double.NaN;
 
     /** Value property Infinity of the Global Object - ECMA 15.1.1.2 Infinity */
     @Property(attributes = Attribute.NON_ENUMERABLE_CONSTANT)
-    public final Object Infinity = Double.POSITIVE_INFINITY;
+    public final double Infinity = Double.POSITIVE_INFINITY;
 
     /** Value property Undefined of the Global Object - ECMA 15.1.1.3 Undefined */
     @Property(attributes = Attribute.NON_ENUMERABLE_CONSTANT)
@@ -303,15 +306,15 @@
 
     /** Nashorn extension: current script's file name */
     @Property(name = "__FILE__", attributes = Attribute.NON_ENUMERABLE_CONSTANT)
-    public Object __FILE__;
+    public final Object __FILE__ = LOCATION_PROPERTY_PLACEHOLDER;
 
     /** Nashorn extension: current script's directory */
     @Property(name = "__DIR__", attributes = Attribute.NON_ENUMERABLE_CONSTANT)
-    public Object __DIR__;
+    public final Object __DIR__ = LOCATION_PROPERTY_PLACEHOLDER;
 
     /** Nashorn extension: current source line number being executed */
     @Property(name = "__LINE__", attributes = Attribute.NON_ENUMERABLE_CONSTANT)
-    public Object __LINE__;
+    public final Object __LINE__ = LOCATION_PROPERTY_PLACEHOLDER;
 
     /** Used as Date.prototype's default value */
     public NativeDate   DEFAULT_DATE;
@@ -1550,6 +1553,26 @@
     }
 
     /**
+     * Called from generated to replace a location property placeholder with the actual location property value.
+     *
+     * @param  placeholder the value tested for being a placeholder for a location property
+     * @param  locationProperty the actual value for the location property
+     * @return locationProperty if placeholder is indeed a placeholder for a location property, the placeholder otherwise
+     */
+    public static Object replaceLocationPropertyPlaceholder(final Object placeholder, final Object locationProperty) {
+        return isLocationPropertyPlaceholder(placeholder) ? locationProperty : placeholder;
+    }
+
+    /**
+     * Called from runtime internals to check if the passed value is a location property placeholder.
+     * @param  placeholder the value tested for being a placeholder for a location property
+     * @return true if the value is a placeholder, false otherwise.
+     */
+    public static boolean isLocationPropertyPlaceholder(final Object placeholder) {
+        return placeholder == LOCATION_PROPERTY_PLACEHOLDER;
+    }
+
+    /**
      * Create a new RegExp object.
      *
      * @param expression Regular expression.
@@ -1633,7 +1656,6 @@
 
         // duplicate PropertyMaps of Native* classes
         copyInitialMaps(env);
-
         // initialize Function and Object constructor
         initFunctionAndObject();
 
@@ -1728,19 +1750,14 @@
 
         copyBuiltins();
 
-        // initialized with strings so that typeof will work as expected.
-        this.__FILE__ = "";
-        this.__DIR__  = "";
-        this.__LINE__ = 0.0;
-
         // expose script (command line) arguments as "arguments" property of global
         final List<String> arguments = env.getArguments();
         final Object argsObj = wrapAsObject(arguments.toArray());
-
-        addOwnProperty("arguments", Attribute.NOT_ENUMERABLE, argsObj);
+        final int flags = jdk.nashorn.internal.runtime.Property.IS_ALWAYS_OBJECT | Attribute.NOT_ENUMERABLE;
+        addOwnProperty("arguments", flags, argsObj);
         if (env._scripting) {
             // synonym for "arguments" in scripting mode
-            addOwnProperty("$ARG", Attribute.NOT_ENUMERABLE, argsObj);
+            addOwnProperty("$ARG", flags, argsObj);
         }
     }
 
@@ -1991,6 +2008,7 @@
         this.functionMap = ScriptFunctionImpl.getInitialMap().duplicate();
         this.anonymousFunctionMap = ScriptFunctionImpl.getInitialAnonymousMap().duplicate();
         this.strictFunctionMap = ScriptFunctionImpl.getInitialStrictMap().duplicate();
+        assert strictFunctionMap != null;
         this.boundFunctionMap = ScriptFunctionImpl.getInitialBoundMap().duplicate();
 
         // java
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeArray.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeArray.java	Wed Feb 26 13:17:57 2014 +0100
@@ -29,10 +29,12 @@
 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
 import static jdk.nashorn.internal.runtime.PropertyDescriptor.VALUE;
 import static jdk.nashorn.internal.runtime.PropertyDescriptor.WRITABLE;
+import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
 import static jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator.arrayLikeIterator;
 import static jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator.reverseArrayLikeIterator;
 
 import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodType;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -40,8 +42,11 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.concurrent.Callable;
-
+import jdk.internal.dynalink.CallSiteDescriptor;
+import jdk.internal.dynalink.linker.GuardedInvocation;
+import jdk.internal.dynalink.linker.LinkRequest;
 import jdk.nashorn.api.scripting.JSObject;
+import static jdk.nashorn.internal.lookup.Lookup.MH;
 import jdk.nashorn.internal.objects.annotations.Attribute;
 import jdk.nashorn.internal.objects.annotations.Constructor;
 import jdk.nashorn.internal.objects.annotations.Function;
@@ -50,6 +55,7 @@
 import jdk.nashorn.internal.objects.annotations.Setter;
 import jdk.nashorn.internal.objects.annotations.SpecializedConstructor;
 import jdk.nashorn.internal.objects.annotations.Where;
+import jdk.nashorn.internal.runtime.Context;
 import jdk.nashorn.internal.runtime.JSType;
 import jdk.nashorn.internal.runtime.PropertyDescriptor;
 import jdk.nashorn.internal.runtime.PropertyMap;
@@ -60,9 +66,11 @@
 import jdk.nashorn.internal.runtime.arrays.ArrayData;
 import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
 import jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator;
+import jdk.nashorn.internal.runtime.arrays.ContinuousArray;
 import jdk.nashorn.internal.runtime.arrays.IteratorAction;
 import jdk.nashorn.internal.runtime.linker.Bootstrap;
 import jdk.nashorn.internal.runtime.linker.InvokeByName;
+import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
 
 /**
  * Runtime representation of a JavaScript array. NativeArray only holds numeric
@@ -80,6 +88,122 @@
     private static final Object CALL_CMP                 = new Object();
     private static final Object TO_LOCALE_STRING         = new Object();
 
+    /*
+     * Constructors.
+     */
+    NativeArray() {
+        this(ArrayData.initialArray());
+    }
+
+    NativeArray(final long length) {
+        // TODO assert valid index in long before casting
+        this(ArrayData.allocate((int) length));
+    }
+
+    NativeArray(final int[] array) {
+        this(ArrayData.allocate(array));
+    }
+
+    NativeArray(final long[] array) {
+        this(ArrayData.allocate(array));
+    }
+
+    NativeArray(final double[] array) {
+        this(ArrayData.allocate(array));
+    }
+
+    NativeArray(final Object[] array) {
+        this(ArrayData.allocate(array.length));
+
+        ArrayData arrayData = this.getArray();
+        arrayData.ensure(array.length - 1);
+
+        for (int index = 0; index < array.length; index++) {
+            final Object value = array[index];
+
+            if (value == ScriptRuntime.EMPTY) {
+                arrayData = arrayData.delete(index);
+            } else {
+                arrayData = arrayData.set(index, value, false);
+            }
+        }
+
+        this.setArray(arrayData);
+    }
+
+    NativeArray(final ArrayData arrayData) {
+        this(arrayData, Global.instance());
+    }
+
+    NativeArray(final ArrayData arrayData, final Global global) {
+        super(global.getArrayPrototype(), global.getArrayMap());
+        this.setArray(arrayData);
+        this.setIsArray();
+    }
+
+    @Override
+    protected GuardedInvocation findGetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) {
+        final ArrayData  data       = getArray();
+        final MethodType callType   = desc.getMethodType();
+        final Class<?>   indexType  = callType.parameterType(1);
+        final Class<?>   returnType = callType.returnType();
+
+        if (data instanceof ContinuousArray && indexType == int.class) {
+            final Object[]   args       = request.getArguments();
+            final int        index      = (int)args[args.length - 1];
+
+            if (data.has(index)) {
+                final MethodHandle getArray     = ScriptObject.GET_ARRAY.methodHandle();
+                final int          programPoint = NashornCallSiteDescriptor.isOptimistic(desc) ? NashornCallSiteDescriptor.getProgramPoint(desc) : INVALID_PROGRAM_POINT;
+                MethodHandle       getElement   = ((ContinuousArray)data).getElementGetter(returnType, programPoint);
+                if (getElement != null) {
+                    getElement = MH.filterArguments(getElement, 0, MH.asType(getArray, getArray.type().changeReturnType(ContinuousArray.class)));
+                    //System.err.println("Relink fast GET "+ desc+ " "+  DynamicLinker.getLinkedCallSiteLocation());
+                    return new GuardedInvocation(getElement, null, null, ClassCastException.class);
+                }
+            }
+        }
+        //System.err.println("Relink slow GET "+ desc+ " "+  DynamicLinker.getLinkedCallSiteLocation());
+
+        return super.findGetIndexMethod(desc, request);
+    }
+
+    @Override
+    protected GuardedInvocation findSetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) { // array, index, value
+
+        final ArrayData  data        = getArray();
+        final MethodType callType    = desc.getMethodType();
+        final Class<?>   indexType   = callType.parameterType(1);
+        final Class<?>   elementType = callType.parameterType(2);
+
+        if (data instanceof ContinuousArray && indexType == int.class) {
+            final ContinuousArray cdata = (ContinuousArray)data;
+            final Object[]        args  = request.getArguments();
+            final int             index = (int)args[args.length - 2];
+
+            if (data.has(index)) {
+                MethodHandle hasGuard = cdata.getSetGuard();
+                hasGuard = MH.asType(hasGuard, hasGuard.type().changeParameterType(0, ContinuousArray.class));
+
+                MethodHandle setElement = cdata.getElementSetter(elementType); //Z(continuousarraydata, int, int), return true if successful
+                if (setElement != null) {
+                    //else we are dealing with a wider type than supported by this callsite
+                    MethodHandle getArray = ScriptObject.GET_ARRAY.methodHandle();
+                    getArray   = MH.asType(getArray, getArray.type().changeReturnType(ContinuousArray.class));
+                    setElement = MH.filterArguments(setElement, 0, getArray);
+                    hasGuard   = MH.filterArguments(hasGuard, 0, getArray);
+                    //if this is the first invocation we have to execute the setter ourselves, we know it will work
+
+                    //System.err.println("Relink fast SET "+ desc + " "+  DynamicLinker.getLinkedCallSiteLocation());
+                    return new GuardedInvocation(setElement, hasGuard, null, ClassCastException.class); //CCE if not a scriptObject anymore
+                }
+            }
+        }
+
+        //System.err.println("Relink slow SET "+ desc + " "+  DynamicLinker.getLinkedCallSiteLocation());
+        return super.findSetIndexMethod(desc, request);
+    }
+
     private static InvokeByName getJOIN() {
         return Global.instance().getInvokeByName(JOIN,
                 new Callable<InvokeByName>() {
@@ -160,59 +284,6 @@
         return $nasgenmap$;
     }
 
-    /*
-     * Constructors.
-     */
-    NativeArray() {
-        this(ArrayData.initialArray());
-    }
-
-    NativeArray(final long length) {
-        // TODO assert valid index in long before casting
-        this(ArrayData.allocate((int) length));
-    }
-
-    NativeArray(final int[] array) {
-        this(ArrayData.allocate(array));
-    }
-
-    NativeArray(final long[] array) {
-        this(ArrayData.allocate(array));
-    }
-
-    NativeArray(final double[] array) {
-        this(ArrayData.allocate(array));
-    }
-
-    NativeArray(final Object[] array) {
-        this(ArrayData.allocate(array.length));
-
-        ArrayData arrayData = this.getArray();
-        arrayData.ensure(array.length - 1);
-
-        for (int index = 0; index < array.length; index++) {
-            final Object value = array[index];
-
-            if (value == ScriptRuntime.EMPTY) {
-                arrayData = arrayData.delete(index);
-            } else {
-                arrayData = arrayData.set(index, value, false);
-            }
-        }
-
-        this.setArray(arrayData);
-    }
-
-    NativeArray(final ArrayData arrayData) {
-        this(arrayData, Global.instance());
-    }
-
-    NativeArray(final ArrayData arrayData, final Global global) {
-        super(global.getArrayPrototype(), global.getArrayMap());
-        this.setArray(arrayData);
-        this.setIsArray();
-    }
-
     @Override
     public String getClassName() {
         return "Array";
@@ -220,7 +291,11 @@
 
     @Override
     public Object getLength() {
-        return getArray().length() & JSType.MAX_UINT;
+        final long length = getArray().length() & JSType.MAX_UINT;
+        if(length < Integer.MAX_VALUE) {
+            return (int)length;
+        }
+        return length;
     }
 
     /**
@@ -464,6 +539,19 @@
     }
 
     /**
+     * Assert that an array is numeric, if not throw type error
+     * @param self self array to check
+     * @return true if numeric
+     */
+    @Function(attributes = Attribute.NOT_ENUMERABLE)
+    public static Object assertNumeric(final Object self) {
+        if(!(self instanceof NativeArray && ((NativeArray)self).getArray().getOptimisticType().isNumeric())) {
+            throw typeError("not.a.numeric.array", ScriptRuntime.safeToString(self));
+        }
+        return Boolean.TRUE;
+    }
+
+    /**
      * ECMA 15.4.4.3 Array.prototype.toLocaleString ( )
      *
      * @param self self reference
@@ -576,6 +664,21 @@
     /**
      * ECMA 15.4.2.2 new Array (len)
      *
+     * Specialized constructor for zero arguments - empty array
+     *
+     * @param newObj  was the new operator used to instantiate this array
+     * @param self    self reference
+     * @param element first element
+     * @return the new NativeArray
+     */
+    @SpecializedConstructor
+    public static Object construct(final boolean newObj, final Object self, final boolean element) {
+        return new NativeArray(new Object[] { element });
+    }
+
+    /**
+     * ECMA 15.4.2.2 new Array (len)
+     *
      * Specialized constructor for one integer argument (length)
      *
      * @param newObj was the new operator used to instantiate this array
@@ -746,7 +849,7 @@
      *
      * @param self self reference
      * @param args arguments to push
-     * @return array after pushes
+     * @return array length after pushes
      */
     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
     public static Object push(final Object self, final Object... args) {
@@ -770,7 +873,7 @@
 
             return len;
         } catch (final ClassCastException | NullPointerException e) {
-            throw typeError("not.an.object", ScriptRuntime.safeToString(self));
+            throw typeError(Context.getGlobal(), e, "not.an.object", ScriptRuntime.safeToString(self));
         }
     }
 
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeDebug.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeDebug.java	Wed Feb 26 13:17:57 2014 +0100
@@ -93,21 +93,6 @@
     }
 
     /**
-     * Nashorn extension: get spill vector from {@link ScriptObject}
-     *
-     * @param self self reference
-     * @param obj script object
-     * @return the spill vector for the given ScriptObject
-     */
-    @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
-    public static Object spill(final Object self, final Object obj) {
-        if (obj instanceof ScriptObject) {
-            return ((ScriptObject)obj).spill;
-        }
-        return UNDEFINED;
-    }
-
-    /**
      * Check object identity comparison regardless of type
      *
      * @param self self reference
@@ -121,6 +106,30 @@
     }
 
     /**
+     * Returns true if if the two objects are both property maps, and they have identical properties in the same order,
+     * but allows the properties to differ in their types.
+     * @param m1 first property map
+     * @param m2 second property map
+     * @return true if they have identical properties in same order, with possibly different types.
+     */
+    @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
+    public static Object equalWithoutType(final Object self, final Object m1, final Object m2) {
+        return ((PropertyMap)m1).equalsWithoutType((PropertyMap)m2);
+    }
+
+    /**
+     * Returns a diagnostic string representing the difference of two property maps.
+     * @param m1 first property map
+     * @param m2 second property map
+     * @return a diagnostic string representing the difference of two property maps.
+     */
+    @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
+    public static Object diffPropertyMaps(final Object self, final Object m1, final Object m2) {
+        return PropertyMap.diff((PropertyMap)m1, (PropertyMap)m2);
+    }
+
+
+    /**
      * Object util - getClass
      *
      * @param self self reference
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeFunction.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeFunction.java	Wed Feb 26 13:17:57 2014 +0100
@@ -25,11 +25,14 @@
 
 package jdk.nashorn.internal.objects;
 
+import static jdk.nashorn.internal.runtime.ECMAErrors.rangeError;
 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
 
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
 import java.util.List;
-
+import jdk.internal.dynalink.support.Lookup;
 import jdk.nashorn.api.scripting.JSObject;
 import jdk.nashorn.internal.objects.annotations.Attribute;
 import jdk.nashorn.internal.objects.annotations.Constructor;
@@ -40,6 +43,7 @@
 import jdk.nashorn.internal.runtime.JSType;
 import jdk.nashorn.internal.runtime.ParserException;
 import jdk.nashorn.internal.runtime.PropertyMap;
+import jdk.nashorn.internal.runtime.ScriptEnvironment;
 import jdk.nashorn.internal.runtime.ScriptFunction;
 import jdk.nashorn.internal.runtime.ScriptObject;
 import jdk.nashorn.internal.runtime.ScriptRuntime;
@@ -55,6 +59,9 @@
 @ScriptClass("Function")
 public final class NativeFunction {
 
+    /** apply arg converter handle */
+    public static final MethodHandle TO_APPLY_ARGS = Lookup.findOwnStatic(MethodHandles.lookup(), "toApplyArgs", Object[].class, Object.class);
+
     // initialized by nasgen
     @SuppressWarnings("unused")
     private static PropertyMap $nasgenmap$;
@@ -88,50 +95,75 @@
      */
     @Function(attributes = Attribute.NOT_ENUMERABLE)
     public static Object apply(final Object self, final Object thiz, final Object array) {
-        if (!(self instanceof ScriptFunction) && !(self instanceof JSObject)) {
-            throw typeError("not.a.function", ScriptRuntime.safeToString(self));
-        }
-
-        Object[] args = null;
-
-        if (array instanceof ScriptObject) {
-            // look for array-like object
-            final ScriptObject sobj = (ScriptObject)array;
-            final Object       len  = sobj.getLength();
-            final int n = (int)JSType.toUint32(len);
+        checkCallable(self);
 
-            args = new Object[n];
-            for (int i = 0; i < args.length; i++) {
-                args[i] = sobj.get(i);
-            }
-        } else if (array instanceof Object[]) {
-            args = (Object[])array;
-        } else if (array instanceof List) {
-            final List<?> list = (List<?>)array;
-            list.toArray(args = new Object[list.size()]);
-        } else if (array == null || array == UNDEFINED) {
-            args = ScriptRuntime.EMPTY_ARRAY;
-        } else if (array instanceof JSObject) {
-            // look for array-like JSObject object
-            final JSObject jsObj = (JSObject)array;
-            final Object       len  = jsObj.hasMember("length")? jsObj.getMember("length") : Integer.valueOf(0);
-            final int n = (int)JSType.toUint32(len);
-
-            args = new Object[n];
-            for (int i = 0; i < args.length; i++) {
-                args[i] = jsObj.hasSlot(i)? jsObj.getSlot(i) : UNDEFINED;
-            }
-        } else {
-            throw typeError("function.apply.expects.array");
-        }
+        Object[] args = toApplyArgs(array);
 
         if (self instanceof ScriptFunction) {
             return ScriptRuntime.apply((ScriptFunction)self, thiz, args);
         } else if (self instanceof JSObject) {
             return ((JSObject)self).call(thiz, args);
         }
+        throw new AssertionError("Should not reach here");
+    }
 
-        throw new AssertionError("should not reach here");
+     /**
+     * Given an array-like object, converts it into a Java object array suitable for invocation of ScriptRuntime.apply
+     * or for direct invocation of the applied function.
+     * @param array the array-like object. Can be null in which case a zero-length array is created.
+     * @return the Java array
+     */
+    public static Object[] toApplyArgs(final Object array) {
+        if (array instanceof NativeArguments) {
+            return ((NativeArguments)array).getArray().asObjectArray();
+        } else if (array instanceof ScriptObject) {
+            // look for array-like object
+            final ScriptObject sobj = (ScriptObject)array;
+            final int n = lengthToInt(sobj.getLength());
+
+            final Object[] args = new Object[n];
+            for (int i = 0; i < args.length; i++) {
+                args[i] = sobj.get(i);
+            }
+            return args;
+        } else if (array instanceof Object[]) {
+            return (Object[])array;
+        } else if (array instanceof List) {
+            final List<?> list = (List<?>)array;
+            return list.toArray(new Object[list.size()]);
+        } else if (array == null || array == UNDEFINED) {
+            return ScriptRuntime.EMPTY_ARRAY;
+        } else if (array instanceof JSObject) {
+            // look for array-like JSObject object
+            final JSObject jsObj = (JSObject)array;
+            final Object   len  = jsObj.hasMember("length")? jsObj.getMember("length") : Integer.valueOf(0);
+            final int n = lengthToInt(len);
+
+            final Object[] args = new Object[n];
+            for (int i = 0; i < args.length; i++) {
+                args[i] = jsObj.hasSlot(i)? jsObj.getSlot(i) : UNDEFINED;
+            }
+            return args;
+        } else {
+            throw typeError("function.apply.expects.array");
+        }
+    }
+
+    private static int lengthToInt(final Object len) {
+        final long ln = JSType.toUint32(len);
+        // NOTE: ECMASCript 5.1 section 15.3.4.3 says length should be treated as Uint32, but we wouldn't be able to
+        // allocate a Java array of more than MAX_VALUE elements anyway, so at this point we have to throw an error.
+        // People applying a function to more than 2^31 arguments will unfortunately be out of luck.
+        if (ln > Integer.MAX_VALUE) {
+            throw rangeError("range.error.inappropriate.array.length", JSType.toString(len));
+        }
+        return (int)ln;
+    }
+
+    private static void checkCallable(final Object self) {
+        if (!(self instanceof ScriptFunction || (self instanceof JSObject && ((JSObject)self).isFunction()))) {
+            throw typeError("not.a.function", ScriptRuntime.safeToString(self));
+        }
     }
 
     /**
@@ -143,9 +175,7 @@
      */
     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
     public static Object call(final Object self, final Object... args) {
-        if (!(self instanceof ScriptFunction) && !(self instanceof JSObject)) {
-            throw typeError("not.a.function", ScriptRuntime.safeToString(self));
-        }
+        checkCallable(self);
 
         Object thiz = (args.length == 0) ? UNDEFINED : args[0];
         Object[] arguments;
@@ -235,7 +265,7 @@
             funcBody = JSType.toString(args[args.length - 1]);
 
             final String paramList = paramListBuf.toString();
-            if (! paramList.isEmpty()) {
+            if (!paramList.isEmpty()) {
                 checkFunctionParameters(paramList);
                 sb.append(paramList);
             }
@@ -257,8 +287,9 @@
     }
 
     private static void checkFunctionParameters(final String params) {
-        final Source src = new Source("<function>", params);
-        final Parser parser = new Parser(Global.getEnv(), src, new Context.ThrowErrorManager());
+        final Source            src    = new Source("<function>", params);
+        final ScriptEnvironment env    = Global.getEnv();
+        final Parser            parser = new Parser(env, src, new Context.ThrowErrorManager(), env._strict);
         try {
             parser.parseFormalParameterList();
         } catch (final ParserException pe) {
@@ -267,8 +298,9 @@
     }
 
     private static void checkFunctionBody(final String funcBody) {
-        final Source src = new Source("<function>", funcBody);
-        final Parser parser = new Parser(Global.getEnv(), src, new Context.ThrowErrorManager());
+        final Source            src    = new Source("<function>", funcBody);
+        final ScriptEnvironment env    = Global.getEnv();
+        final Parser            parser = new Parser(env, src, new Context.ThrowErrorManager(), env._strict);
         try {
             parser.parseFunctionBody();
         } catch (final ParserException pe) {
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeJSAdapter.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeJSAdapter.java	Wed Feb 26 13:17:57 2014 +0100
@@ -25,9 +25,9 @@
 
 package jdk.nashorn.internal.objects;
 
+import static jdk.nashorn.internal.lookup.Lookup.MH;
 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
-import static jdk.nashorn.internal.lookup.Lookup.MH;
 
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
@@ -38,6 +38,7 @@
 import jdk.internal.dynalink.CallSiteDescriptor;
 import jdk.internal.dynalink.linker.GuardedInvocation;
 import jdk.internal.dynalink.linker.LinkRequest;
+import jdk.nashorn.internal.lookup.Lookup;
 import jdk.nashorn.internal.objects.annotations.Constructor;
 import jdk.nashorn.internal.objects.annotations.ScriptClass;
 import jdk.nashorn.internal.runtime.FindProperty;
@@ -46,8 +47,8 @@
 import jdk.nashorn.internal.runtime.ScriptFunction;
 import jdk.nashorn.internal.runtime.ScriptObject;
 import jdk.nashorn.internal.runtime.ScriptRuntime;
+import jdk.nashorn.internal.runtime.UnwarrantedOptimismException;
 import jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator;
-import jdk.nashorn.internal.lookup.Lookup;
 import jdk.nashorn.internal.scripts.JO;
 
 /**
@@ -172,63 +173,63 @@
     }
 
     @Override
-    public int getInt(final Object key) {
-        return (overrides && super.hasOwnProperty(key)) ? super.getInt(key) : callAdapteeInt(__get__, key);
+    public int getInt(final Object key, final int programPoint) {
+        return (overrides && super.hasOwnProperty(key)) ? super.getInt(key, programPoint) : callAdapteeInt(programPoint, __get__, key);
     }
 
     @Override
-    public int getInt(final double key) {
-        return (overrides && super.hasOwnProperty(key)) ? super.getInt(key) : callAdapteeInt(__get__, key);
+    public int getInt(final double key, final int programPoint) {
+        return (overrides && super.hasOwnProperty(key)) ? super.getInt(key, programPoint) : callAdapteeInt(programPoint, __get__, key);
     }
 
     @Override
-    public int getInt(final long key) {
-        return (overrides && super.hasOwnProperty(key)) ? super.getInt(key) : callAdapteeInt(__get__, key);
+    public int getInt(final long key, final int programPoint) {
+        return (overrides && super.hasOwnProperty(key)) ? super.getInt(key, programPoint) : callAdapteeInt(programPoint, __get__, key);
     }
 
     @Override
-    public int getInt(final int key) {
-        return (overrides && super.hasOwnProperty(key)) ? super.getInt(key) : callAdapteeInt(__get__, key);
+    public int getInt(final int key, final int programPoint) {
+        return (overrides && super.hasOwnProperty(key)) ? super.getInt(key, programPoint) : callAdapteeInt(programPoint, __get__, key);
     }
 
     @Override
-    public long getLong(final Object key) {
-        return (overrides && super.hasOwnProperty(key)) ? super.getLong(key) : callAdapteeLong(__get__, key);
+    public long getLong(final Object key, final int programPoint) {
+        return (overrides && super.hasOwnProperty(key)) ? super.getLong(key, programPoint) : callAdapteeLong(programPoint, __get__, key);
     }
 
     @Override
-    public long getLong(final double key) {
-        return (overrides && super.hasOwnProperty(key)) ? super.getLong(key) : callAdapteeLong(__get__, key);
+    public long getLong(final double key, final int programPoint) {
+        return (overrides && super.hasOwnProperty(key)) ? super.getLong(key, programPoint) : callAdapteeLong(programPoint, __get__, key);
     }
 
     @Override
-    public long getLong(final long key) {
-        return (overrides && super.hasOwnProperty(key)) ? super.getLong(key) : callAdapteeLong(__get__, key);
+    public long getLong(final long key, final int programPoint) {
+        return (overrides && super.hasOwnProperty(key)) ? super.getLong(key, programPoint) : callAdapteeLong(programPoint, __get__, key);
     }
 
     @Override
-    public long getLong(final int key) {
-        return (overrides && super.hasOwnProperty(key)) ? super.getLong(key) : callAdapteeLong(__get__, key);
+    public long getLong(final int key, final int programPoint) {
+        return (overrides && super.hasOwnProperty(key)) ? super.getLong(key, programPoint) : callAdapteeLong(programPoint, __get__, key);
     }
 
     @Override
-    public double getDouble(final Object key) {
-        return (overrides && super.hasOwnProperty(key)) ? super.getDouble(key) : callAdapteeDouble(__get__, key);
+    public double getDouble(final Object key, final int programPoint) {
+        return (overrides && super.hasOwnProperty(key)) ? super.getDouble(key, programPoint) : callAdapteeDouble(programPoint, __get__, key);
     }
 
     @Override
-    public double getDouble(final double key) {
-        return (overrides && super.hasOwnProperty(key)) ? super.getDouble(key) : callAdapteeDouble(__get__, key);
+    public double getDouble(final double key, final int programPoint) {
+        return (overrides && super.hasOwnProperty(key)) ? super.getDouble(key, programPoint) : callAdapteeDouble(programPoint, __get__, key);
     }
 
     @Override
-    public double getDouble(final long key) {
-        return (overrides && super.hasOwnProperty(key)) ? super.getDouble(key) : callAdapteeDouble(__get__, key);
+    public double getDouble(final long key, final int programPoint) {
+        return (overrides && super.hasOwnProperty(key)) ? super.getDouble(key, programPoint) : callAdapteeDouble(programPoint, __get__, key);
     }
 
     @Override
-    public double getDouble(final int key) {
-        return (overrides && super.hasOwnProperty(key)) ? super.getDouble(key) : callAdapteeDouble(__get__, key);
+    public double getDouble(final int key, final int programPoint) {
+        return (overrides && super.hasOwnProperty(key)) ? super.getDouble(key, programPoint) : callAdapteeDouble(programPoint, __get__, key);
     }
 
     @Override
@@ -629,8 +630,8 @@
                     // to name. Probably not a big deal, but if we can ever make it leaner, it'd be nice.
                     return new GuardedInvocation(MH.dropArguments(MH.constant(Object.class,
                             func.makeBoundFunction(this, new Object[] { name })), 0, Object.class),
-                            adaptee.getMap().getProtoGetSwitchPoint(adaptee.getProto(), __call__),
-                            testJSAdaptor(adaptee, null, null, null));
+                            testJSAdaptor(adaptee, null, null, null),
+                            adaptee.getMap().getProtoGetSwitchPoint(adaptee.getProto(), __call__));
                 }
             }
             throw typeError("no.such.function", desc.getNameToken(2), ScriptRuntime.safeToString(this));
@@ -662,16 +663,16 @@
         return callAdaptee(UNDEFINED, name, args);
     }
 
-    private double callAdapteeDouble(final String name, final Object... args) {
-        return JSType.toNumber(callAdaptee(name, args));
+    private double callAdapteeDouble(final int programPoint, final String name, final Object... args) {
+        return JSType.toNumberMaybeOptimistic(callAdaptee(name, args), programPoint);
     }
 
-    private long callAdapteeLong(final String name, final Object... args) {
-        return JSType.toLong(callAdaptee(name, args));
+    private long callAdapteeLong(final int programPoint, final String name, final Object... args) {
+        return JSType.toLongMaybeOptimistic(callAdaptee(name, args), programPoint);
     }
 
-    private int callAdapteeInt(final String name, final Object... args) {
-        return JSType.toInt32(callAdaptee(name, args));
+    private int callAdapteeInt(final int programPoint, final String name, final Object... args) {
+        return JSType.toInt32MaybeOptimistic(callAdaptee(name, args), programPoint);
     }
 
     private Object callAdaptee(final Object retValue, final String name, final Object... args) {
@@ -700,8 +701,8 @@
                 if (methodHandle != null) {
                     return new GuardedInvocation(
                             methodHandle,
-                            adaptee.getMap().getProtoGetSwitchPoint(adaptee.getProto(), hook),
-                            testJSAdaptor(adaptee, findData.getGetter(Object.class), findData.getOwner(), func));
+                            testJSAdaptor(adaptee, findData.getGetter(Object.class, UnwarrantedOptimismException.INVALID_PROGRAM_POINT), findData.getOwner(), func),
+                            adaptee.getMap().getProtoGetSwitchPoint(adaptee.getProto(), hook));
                 }
              }
         }
@@ -713,7 +714,7 @@
             final MethodHandle methodHandle = hook.equals(__put__) ?
             MH.asType(Lookup.EMPTY_SETTER, type) :
             Lookup.emptyGetter(type.returnType());
-            return new GuardedInvocation(methodHandle, adaptee.getMap().getProtoGetSwitchPoint(adaptee.getProto(), hook), testJSAdaptor(adaptee, null, null, null));
+            return new GuardedInvocation(methodHandle, testJSAdaptor(adaptee, null, null, null), adaptee.getMap().getProtoGetSwitchPoint(adaptee.getProto(), hook));
         }
     }
 
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeJavaImporter.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeJavaImporter.java	Wed Feb 26 13:17:57 2014 +0100
@@ -25,6 +25,8 @@
 
 package jdk.nashorn.internal.objects;
 
+import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
+
 import jdk.internal.dynalink.CallSiteDescriptor;
 import jdk.internal.dynalink.beans.StaticClass;
 import jdk.internal.dynalink.linker.GuardedInvocation;
@@ -37,6 +39,7 @@
 import jdk.nashorn.internal.runtime.NativeJavaPackage;
 import jdk.nashorn.internal.runtime.PropertyMap;
 import jdk.nashorn.internal.runtime.ScriptObject;
+import jdk.nashorn.internal.runtime.UnwarrantedOptimismException;
 
 /**
  * This is "JavaImporter" constructor. This constructor allows you to use Java types omitting explicit package names.
@@ -135,8 +138,12 @@
     }
 
     @Override
-    protected Object invokeNoSuchProperty(final String name) {
-        return createProperty(name);
+    protected Object invokeNoSuchProperty(final String name, final int programPoint) {
+        final Object retval = createProperty(name);
+        if (isValid(programPoint)) {
+            throw new UnwarrantedOptimismException(retval, programPoint);
+        }
+        return retval;
     }
 
     private boolean createAndSetProperty(final CallSiteDescriptor desc) {
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeNumber.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeNumber.java	Wed Feb 26 13:17:57 2014 +0100
@@ -25,12 +25,10 @@
 
 package jdk.nashorn.internal.objects;
 
+import static jdk.nashorn.internal.lookup.Lookup.MH;
 import static jdk.nashorn.internal.runtime.ECMAErrors.rangeError;
 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
-import static jdk.nashorn.internal.runtime.JSType.isRepresentableAsInt;
-import static jdk.nashorn.internal.runtime.JSType.isRepresentableAsLong;
 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
-import static jdk.nashorn.internal.lookup.Lookup.MH;
 
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
@@ -80,8 +78,6 @@
     public static final double POSITIVE_INFINITY = Double.POSITIVE_INFINITY;
 
     private final double  value;
-    private final boolean isInt;
-    private final boolean isLong;
 
     // initialized by nasgen
     private static PropertyMap $nasgenmap$;
@@ -93,8 +89,6 @@
     private NativeNumber(final double value, final ScriptObject proto, final PropertyMap map) {
         super(proto, map);
         this.value = value;
-        this.isInt  = isRepresentableAsInt(value);
-        this.isLong = isRepresentableAsLong(value);
     }
 
     NativeNumber(final double value, final Global global) {
@@ -132,30 +126,6 @@
         return value;
     }
 
-    /**
-     * Get the value of this Number as a {@code int}
-     * @return an {@code int} representing the Number value
-     * @throws ClassCastException If number is not representable as an {@code int}
-     */
-    public int intValue() throws ClassCastException {
-        if (isInt) {
-            return (int)value;
-        }
-        throw new ClassCastException();
-    }
-
-    /**
-     * Get the value of this Number as a {@code long}
-     * @return a {@code long} representing the Number value
-     * @throws ClassCastException If number is not representable as an {@code long}
-     */
-    public long longValue() throws ClassCastException {
-        if (isLong) {
-            return (long)value;
-        }
-        throw new ClassCastException();
-    }
-
     @Override
     public String getClassName() {
         return "Number";
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeObject.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeObject.java	Wed Feb 26 13:17:57 2014 +0100
@@ -38,6 +38,7 @@
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.Callable;
+
 import jdk.internal.dynalink.beans.BeansLinker;
 import jdk.internal.dynalink.beans.StaticClass;
 import jdk.internal.dynalink.linker.GuardedInvocation;
@@ -633,18 +634,23 @@
         final ScriptObject targetObj = (ScriptObject)target;
 
         if (source instanceof ScriptObject) {
-            final ScriptObject sourceObj = (ScriptObject)source;
-            final Property[] properties = sourceObj.getMap().getProperties();
+            final ScriptObject sourceObj  = (ScriptObject)source;
+
+            final PropertyMap  sourceMap  = sourceObj.getMap();
+            final Property[]   properties = sourceMap.getProperties();
+            //replace the map and blow up everything to objects to work with dual fields :-(
 
             // filter non-enumerable properties
             final ArrayList<Property> propList = new ArrayList<>();
-            for (Property prop : properties) {
+            for (final Property prop : properties) {
                 if (prop.isEnumerable()) {
+                    prop.setValue(sourceObj, sourceObj, sourceObj.get(prop.getKey()), false);
+                    prop.setCurrentType(Object.class);
                     propList.add(prop);
                 }
             }
 
-            if (! propList.isEmpty()) {
+            if (!propList.isEmpty()) {
                 targetObj.addBoundProperties(sourceObj, propList.toArray(new Property[propList.size()]));
             }
         } else if (source instanceof ScriptObjectMirror) {
@@ -662,7 +668,7 @@
                 final String name = keys[idx];
                 final MethodHandle getter = Bootstrap.createDynamicInvoker("dyn:getMethod|getProp|getElem:" + name, MIRROR_GETTER_TYPE);
                 final MethodHandle setter = Bootstrap.createDynamicInvoker("dyn:setProp|setElem:" + name, MIRROR_SETTER_TYPE);
-                props[idx] = (AccessorProperty.create(name, 0, getter, setter));
+                props[idx] = AccessorProperty.create(name, 0, getter, setter);
             }
 
             targetObj.addBoundProperties(source, props);
@@ -774,6 +780,6 @@
 
     private static LinkRequest createLinkRequest(String operation, MethodType methodType, Object source) {
         return new LinkRequestImpl(CallSiteDescriptorFactory.create(MethodHandles.publicLookup(), operation,
-                methodType), false, source);
+                methodType), null, false, source);
     }
 }
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeRegExp.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeRegExp.java	Wed Feb 26 13:17:57 2014 +0100
@@ -28,10 +28,11 @@
 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
 
+import java.lang.invoke.MethodHandle;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
-
+import java.util.concurrent.Callable;
 import jdk.nashorn.internal.objects.annotations.Attribute;
 import jdk.nashorn.internal.objects.annotations.Constructor;
 import jdk.nashorn.internal.objects.annotations.Function;
@@ -44,13 +45,14 @@
 import jdk.nashorn.internal.runtime.JSType;
 import jdk.nashorn.internal.runtime.ParserException;
 import jdk.nashorn.internal.runtime.PropertyMap;
-import jdk.nashorn.internal.runtime.regexp.RegExp;
-import jdk.nashorn.internal.runtime.regexp.RegExpFactory;
-import jdk.nashorn.internal.runtime.regexp.RegExpResult;
-import jdk.nashorn.internal.runtime.regexp.RegExpMatcher;
 import jdk.nashorn.internal.runtime.ScriptFunction;
 import jdk.nashorn.internal.runtime.ScriptObject;
 import jdk.nashorn.internal.runtime.ScriptRuntime;
+import jdk.nashorn.internal.runtime.linker.Bootstrap;
+import jdk.nashorn.internal.runtime.regexp.RegExp;
+import jdk.nashorn.internal.runtime.regexp.RegExpFactory;
+import jdk.nashorn.internal.runtime.regexp.RegExpMatcher;
+import jdk.nashorn.internal.runtime.regexp.RegExpResult;
 
 /**
  * ECMA 15.10 RegExp Objects.
@@ -653,7 +655,7 @@
      * @param replacement Replacement string.
      * @return String with substitutions.
      */
-    Object replace(final String string, final String replacement, final ScriptFunction function) {
+    Object replace(final String string, final String replacement, final ScriptFunction function) throws Throwable {
         final RegExpMatcher matcher = regexp.match(string);
 
         if (matcher == null) {
@@ -669,7 +671,8 @@
             sb.append(string, 0, matcher.start());
 
             if (function != null) {
-                sb.append(callReplaceValue(function, matcher, string));
+                final Object self = function.isStrict() ? UNDEFINED : Global.instance();
+                sb.append(callReplaceValue(getReplaceValueInvoker(), function, self, matcher, string));
             } else {
                 appendReplacement(matcher, string, replacement, sb);
             }
@@ -687,10 +690,13 @@
         int previousLastIndex = 0;
         final StringBuilder sb = new StringBuilder();
 
+        final MethodHandle invoker = function == null ? null : getReplaceValueInvoker();
+        final Object self = function == null || function.isStrict() ? UNDEFINED : Global.instance();
+
         do {
             sb.append(string, thisIndex, matcher.start());
             if (function != null) {
-                sb.append(callReplaceValue(function, matcher, string));
+                sb.append(callReplaceValue(invoker, function, self, matcher, string));
             } else {
                 appendReplacement(matcher, string, replacement, sb);
             }
@@ -788,16 +794,26 @@
         }
     }
 
-    private String callReplaceValue(final ScriptFunction function, final RegExpMatcher matcher, final String string) {
+    private static final Object REPLACE_VALUE = new Object();
+
+    private static final MethodHandle getReplaceValueInvoker() {
+        return Global.instance().getDynamicInvoker(REPLACE_VALUE,
+                new Callable<MethodHandle>() {
+                    @Override
+                    public MethodHandle call() {
+                        return Bootstrap.createDynamicInvoker("dyn:call", String.class, ScriptFunction.class, Object.class, Object[].class);
+                    }
+                });
+    }
+
+    private String callReplaceValue(final MethodHandle invoker, final ScriptFunction function, final Object self, final RegExpMatcher matcher, final String string) throws Throwable {
         final Object[] groups = groups(matcher);
         final Object[] args   = Arrays.copyOf(groups, groups.length + 2);
 
         args[groups.length]     = matcher.start();
         args[groups.length + 1] = string;
 
-        final Object self = function.isStrict() ? UNDEFINED : Global.instance();
-
-        return JSType.toString(ScriptRuntime.apply(function, self, args));
+        return (String)invoker.invokeExact(function, self, args);
     }
 
     /**
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeStrictArguments.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeStrictArguments.java	Wed Feb 26 13:17:57 2014 +0100
@@ -79,8 +79,9 @@
         final ScriptFunction func = Global.instance().getTypeErrorThrower();
         // We have to fill user accessor functions late as these are stored
         // in this object rather than in the PropertyMap of this object.
-        setUserAccessors("caller", func, func);
-        setUserAccessors("callee", func, func);
+        final int flags = Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE;
+        initUserAccessors("caller", flags, func, func);
+        initUserAccessors("callee", flags, func, func);
 
         setArray(ArrayData.allocate(values));
         this.length = values.length;
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeString.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeString.java	Wed Feb 26 13:17:57 2014 +0100
@@ -155,10 +155,9 @@
 
         if (returnType == Object.class && (self instanceof String || self instanceof ConsString)) {
             try {
-                MethodHandle mh = MH.findStatic(MethodHandles.lookup(), NativeString.class, "get", desc.getMethodType());
-                return new GuardedInvocation(mh, NashornGuards.getInstanceOf2Guard(String.class, ConsString.class));
+                return new GuardedInvocation(MH.findStatic(MethodHandles.lookup(), NativeString.class, "get", desc.getMethodType()), NashornGuards.getInstanceOf2Guard(String.class, ConsString.class));
             } catch (final LookupException e) {
-                // Shouldn't happen. Fall back to super
+                //empty. Shouldn't happen. Fall back to super
             }
         }
         return super.findGetIndexMethod(desc, request);
@@ -236,111 +235,111 @@
     }
 
     @Override
-    public int getInt(final Object key) {
-        return JSType.toInt32(get(key));
+    public int getInt(final Object key, final int programPoint) {
+        return JSType.toInt32MaybeOptimistic(get(key), programPoint);
     }
 
     @Override
-    public int getInt(final double key) {
-        return JSType.toInt32(get(key));
+    public int getInt(final double key, final int programPoint) {
+        return JSType.toInt32MaybeOptimistic(get(key), programPoint);
     }
 
     @Override
-    public int getInt(final long key) {
-        return JSType.toInt32(get(key));
+    public int getInt(final long key, final int programPoint) {
+        return JSType.toInt32MaybeOptimistic(get(key), programPoint);
     }
 
     @Override
-    public int getInt(final int key) {
-        return JSType.toInt32(get(key));
+    public int getInt(final int key, final int programPoint) {
+        return JSType.toInt32MaybeOptimistic(get(key), programPoint);
     }
 
     @Override
-    public long getLong(final Object key) {
-        return JSType.toUint32(get(key));
+    public long getLong(final Object key, final int programPoint) {
+        return JSType.toLongMaybeOptimistic(get(key), programPoint);
     }
 
     @Override
-    public long getLong(final double key) {
-        return JSType.toUint32(get(key));
+    public long getLong(final double key, final int programPoint) {
+        return JSType.toLongMaybeOptimistic(get(key), programPoint);
     }
 
     @Override
-    public long getLong(final long key) {
-        return JSType.toUint32(get(key));
+    public long getLong(final long key, final int programPoint) {
+        return JSType.toLongMaybeOptimistic(get(key), programPoint);
     }
 
     @Override
-    public long getLong(final int key) {
-        return JSType.toUint32(get(key));
+    public long getLong(final int key, final int programPoint) {
+        return JSType.toLongMaybeOptimistic(get(key), programPoint);
     }
 
     @Override
-    public double getDouble(final Object key) {
-        return JSType.toNumber(get(key));
+    public double getDouble(final Object key, final int programPoint) {
+        return JSType.toNumberMaybeOptimistic(get(key), programPoint);
     }
 
     @Override
-    public double getDouble(final double key) {
-        return JSType.toNumber(get(key));
+    public double getDouble(final double key, final int programPoint) {
+        return JSType.toNumberMaybeOptimistic(get(key), programPoint);
     }
 
     @Override
-    public double getDouble(final long key) {
-        return JSType.toNumber(get(key));
+    public double getDouble(final long key, final int programPoint) {
+        return JSType.toNumberMaybeOptimistic(get(key), programPoint);
     }
 
     @Override
-    public double getDouble(final int key) {
-        return JSType.toNumber(get(key));
+    public double getDouble(final int key, final int programPoint) {
+        return JSType.toNumberMaybeOptimistic(get(key), programPoint);
     }
 
     @Override
     public boolean has(final Object key) {
         final Object primitiveKey = JSType.toPrimitive(key, String.class);
         final int index = ArrayIndex.getArrayIndex(primitiveKey);
-        return isValid(index) || super.has(primitiveKey);
+        return isValidStringIndex(index) || super.has(primitiveKey);
     }
 
     @Override
     public boolean has(final int key) {
-        return isValid(key) || super.has(key);
+        return isValidStringIndex(key) || super.has(key);
     }
 
     @Override
     public boolean has(final long key) {
         final int index = ArrayIndex.getArrayIndex(key);
-        return isValid(index) || super.has(key);
+        return isValidStringIndex(index) || super.has(key);
     }
 
     @Override
     public boolean has(final double key) {
         final int index = ArrayIndex.getArrayIndex(key);
-        return isValid(index) || super.has(key);
+        return isValidStringIndex(index) || super.has(key);
     }
 
     @Override
     public boolean hasOwnProperty(final Object key) {
         final Object primitiveKey = JSType.toPrimitive(key, String.class);
         final int index = ArrayIndex.getArrayIndex(primitiveKey);
-        return isValid(index) || super.hasOwnProperty(primitiveKey);
+        return isValidStringIndex(index) || super.hasOwnProperty(primitiveKey);
     }
 
     @Override
     public boolean hasOwnProperty(final int key) {
-        return isValid(key) || super.hasOwnProperty(key);
+        return isValidStringIndex(key) || super.hasOwnProperty(key);
     }
 
     @Override
     public boolean hasOwnProperty(final long key) {
         final int index = ArrayIndex.getArrayIndex(key);
-        return isValid(index) || super.hasOwnProperty(key);
+        return isValidStringIndex(index) || super.hasOwnProperty(key);
     }
 
     @Override
     public boolean hasOwnProperty(final double key) {
         final int index = ArrayIndex.getArrayIndex(key);
-        return isValid(index) || super.hasOwnProperty(key);
+        return isValidStringIndex(index) || super.hasOwnProperty(key);
     }
 
     @Override
@@ -368,7 +367,7 @@
     }
 
     private boolean checkDeleteIndex(final int index, final boolean strict) {
-        if (isValid(index)) {
+        if (isValidStringIndex(index)) {
             if (strict) {
                 throw typeError("cant.delete.property", Integer.toString(index), ScriptRuntime.safeToString(this));
             }
@@ -442,11 +441,24 @@
      */
     @SpecializedFunction
     public static Object fromCharCode(final Object self, final Object value) {
-        try {
-            return "" + (char)JSType.toUint16(((Number)value).doubleValue());
-        } catch (final ClassCastException e) {
-            return fromCharCode(self, new Object[] { value });
+        if (value instanceof Integer) {
+            return fromCharCode(self, (int)value);
+        } else if (value instanceof String) {
+            return "" + (char)JSType.toUint16(value);
         }
+        return fromCharCode(self, new Object[] { value });
+    }
+
+    /**
+     * fromCharCode specialization
+     *
+     * @param self  self reference
+     * @param value one argument to be interpreted as char
+     * @return string with one charcode
+     */
+    @SpecializedFunction
+    public static Object fromCharCode(final Object self, final String value) {
+        return fromCharCode(self, new Object[] { value });
     }
 
     /**
@@ -743,9 +755,10 @@
      * @param string      item to replace
      * @param replacement item to replace it with
      * @return string after replacement
+     * @throws Throwable if replacement fails
      */
     @Function(attributes = Attribute.NOT_ENUMERABLE)
-    public static Object replace(final Object self, final Object string, final Object replacement) {
+    public static Object replace(final Object self, final Object string, final Object replacement) throws Throwable {
 
         final String str = checkObjectToString(self);
 
@@ -1191,6 +1204,23 @@
     }
 
     /**
+     * ECMA 15.5.2.1 new String ( [ value ] ) - special version with exactly one {@code boolean} arg
+     *
+     * Constructor
+     *
+     * @param newObj is this constructor invoked with the new operator
+     * @param self   self reference
+     * @param arg    the arg
+     *
+     * @return new NativeString containing the string representation of the arg
+     */
+    @SpecializedConstructor
+    public static Object constructor(final boolean newObj, final Object self, final boolean arg) {
+        final String str = JSType.toString(arg);
+        return newObj ? newObj(self, str) : str;
+    }
+
+    /**
      * Lookup the appropriate method for an invoke dynamic call.
      *
      * @param request  the link request
@@ -1250,11 +1280,12 @@
         }
     }
 
-    private boolean isValid(final int key) {
+    private boolean isValidStringIndex(final int key) {
         return key >= 0 && key < value.length();
     }
 
     private static MethodHandle findWrapFilter() {
         return MH.findStatic(MethodHandles.lookup(), NativeString.class, "wrapFilter", MH.type(NativeString.class, Object.class));
     }
+
 }
--- a/nashorn/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java	Wed Feb 26 13:17:57 2014 +0100
@@ -30,6 +30,7 @@
 
 import java.lang.invoke.MethodHandle;
 import java.util.ArrayList;
+import jdk.nashorn.internal.runtime.AccessorProperty;
 import jdk.nashorn.internal.runtime.GlobalFunctions;
 import jdk.nashorn.internal.runtime.Property;
 import jdk.nashorn.internal.runtime.PropertyMap;
@@ -37,8 +38,6 @@
 import jdk.nashorn.internal.runtime.ScriptFunction;
 import jdk.nashorn.internal.runtime.ScriptFunctionData;
 import jdk.nashorn.internal.runtime.ScriptObject;
-import jdk.nashorn.internal.lookup.Lookup;
-import jdk.nashorn.internal.runtime.AccessorProperty;
 
 /**
  * Concrete implementation of ScriptFunction. This sets correct map for the
@@ -292,14 +291,13 @@
 
         // We have to fill user accessor functions late as these are stored
         // in this object rather than in the PropertyMap of this object.
-
-        final ScriptFunction errorThrower = global.getTypeErrorThrower();
+        assert objectSpill == null;
+        final ScriptFunction typeErrorThrower = global.getTypeErrorThrower();
         if (findProperty("arguments", true) != null) {
-            setUserAccessors("arguments", errorThrower, errorThrower);
-        }
-
+            initUserAccessors("arguments", Property.NOT_CONFIGURABLE | Property.NOT_ENUMERABLE, typeErrorThrower, typeErrorThrower);
+       }
         if (findProperty("caller", true) != null) {
-            setUserAccessors("caller", errorThrower, errorThrower);
-        }
+            initUserAccessors("caller", Property.NOT_CONFIGURABLE | Property.NOT_ENUMERABLE, typeErrorThrower, typeErrorThrower);
+       }
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/objects/annotations/Optimistic.java	Wed Feb 26 13:17:57 2014 +0100
@@ -0,0 +1,62 @@
+/*
+ * 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.objects.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * The Optimistic annotation is used for builtins that can throw UnwarrantedOptimism
+ * exception if they are wrong, whose callsite is optimistic and contains a program
+ * point. Every optimistic builtin callsite needs to bind its programPoint to an
+ * argument of the implementation at compile time
+ *
+ * Typical use case would be something like: if you want to do an optimistic "push" function
+ * in NativeArray that takes an int, write
+ *
+ *  <pre>
+ *  @SpecializedFunction @Optimistic
+ *  public static int push(final Object self, final int x, final int programPoint) {
+ *    try {
+ *    //push code assuming that this is an int array
+ *    //return new length of array, assuming it's an int
+ *    } catch (ArrayWasWrong e) {
+ *       //undo any array modifications
+ *       throw new UnwarrantedOptimismExceptionArrayType(x, programPoint);
+ *    } catch (LengthOverFlowed e) {
+ *       //undo any array modifications
+ *       throw new UnwarratnedOptimismExceptionArrayLength(uint32lenThatOverFlowed, programPoint)
+ *    }
+ *  }
+ *  </pre>
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface Optimistic {
+    //empty
+}
--- a/nashorn/src/jdk/nashorn/internal/objects/annotations/Property.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/annotations/Property.java	Wed Feb 26 13:17:57 2014 +0100
@@ -40,21 +40,25 @@
 public @interface Property {
     /**
      * Name of the script property. If empty, the name is inferred.
+     * @return name
      */
     public String name() default "";
 
     /**
      * Attribute flags for this function.
+     * @return attribute
      */
     public int attributes() default DEFAULT_ATTRIBUTES;
 
     /**
      * Initialize this property with the object of given class.
+     * @return class
      */
     public String clazz() default "";
 
     /**
      * Where this property lives?
+     * @return location of property
      */
     public Where where() default Where.INSTANCE;
 }
--- a/nashorn/src/jdk/nashorn/internal/parser/AbstractParser.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/parser/AbstractParser.java	Wed Feb 26 13:17:57 2014 +0100
@@ -58,6 +58,9 @@
     /** Index of current token. */
     protected int k;
 
+    /** Previous token - accessible to sub classes */
+    protected long previousToken;
+
     /** Descriptor of current token. */
     protected long token;
 
@@ -85,17 +88,21 @@
     /** Is this parser running under strict mode? */
     protected boolean isStrictMode;
 
+    /** What should line numbers be counted from? */
+    protected final int lineOffset;
+
     /** //@ sourceURL or //# sourceURL */
     protected String sourceURL;
 
     /**
      * Construct a parser.
      *
-     * @param source  Source to parse.
-     * @param errors  Error reporting manager.
-     * @param strict  True if we are in strict mode
+     * @param source     Source to parse.
+     * @param errors     Error reporting manager.
+     * @param strict     True if we are in strict mode
+     * @param lineOffset Offset from which lines should be counted
      */
-    protected AbstractParser(final Source source, final ErrorManager errors, final boolean strict) {
+    protected AbstractParser(final Source source, final ErrorManager errors, final boolean strict, final int lineOffset) {
         this.source       = source;
         this.errors       = errors;
         this.k            = -1;
@@ -103,6 +110,7 @@
         this.type         = EOL;
         this.last         = EOL;
         this.isStrictMode = strict;
+        this.lineOffset   = lineOffset;
     }
 
     /**
@@ -199,6 +207,7 @@
             // Set up next token.
             k++;
             final long lastToken = token;
+            previousToken = token;
             token = getToken(k);
             type = Token.descType(token);
 
@@ -208,7 +217,7 @@
             }
 
             if (type == EOL) {
-                line = Token.descLength(token);
+                line         = Token.descLength(token);
                 linePosition = Token.descPosition(token);
             } else {
                 start = Token.descPosition(token);
--- a/nashorn/src/jdk/nashorn/internal/parser/JSONParser.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/parser/JSONParser.java	Wed Feb 26 13:17:57 2014 +0100
@@ -57,7 +57,7 @@
      * @param errors  the error manager
      */
     public JSONParser(final Source source, final ErrorManager errors) {
-        super(source, errors, false);
+        super(source, errors, false, 0);
     }
 
     /**
--- a/nashorn/src/jdk/nashorn/internal/parser/Lexer.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/parser/Lexer.java	Wed Feb 26 13:17:57 2014 +0100
@@ -48,6 +48,7 @@
 import jdk.nashorn.internal.runtime.ECMAErrors;
 import jdk.nashorn.internal.runtime.ErrorManager;
 import jdk.nashorn.internal.runtime.JSErrorType;
+import jdk.nashorn.internal.runtime.JSType;
 import jdk.nashorn.internal.runtime.ParserException;
 import jdk.nashorn.internal.runtime.Source;
 import jdk.nashorn.internal.runtime.options.Options;
@@ -76,7 +77,7 @@
     private final boolean nested;
 
     /** Pending new line number and position. */
-    private int pendingLine;
+    int pendingLine;
 
     /** Position of last EOL + 1. */
     private int linePosition;
@@ -181,8 +182,21 @@
      * @param scripting are we in scripting mode
      */
     public Lexer(final Source source, final TokenStream stream, final boolean scripting) {
-        super(source.getContent(), 1, 0, source.getLength());
+        this(source, 0, source.getLength(), stream, scripting);
+    }
 
+    /**
+     * Contructor
+     *
+     * @param source    the source
+     * @param start     start position in source from which to start lexing
+     * @param len       length of source segment to lex
+     * @param stream    token stream to lex
+     * @param scripting are we in scripting mode
+     */
+
+    public Lexer(final Source source, final int start, final int len, final TokenStream stream, final boolean scripting) {
+        super(source.getContent(), 1, start, len);
         this.source      = source;
         this.stream      = stream;
         this.scripting   = scripting;
@@ -1609,7 +1623,7 @@
      */
     Object getValueOf(final long token, final boolean strict) {
         final int start = Token.descPosition(token);
-        final int len = Token.descLength(token);
+        final int len   = Token.descLength(token);
 
         switch (Token.descType(token)) {
         case DECIMAL:
@@ -1619,7 +1633,23 @@
         case HEXADECIMAL:
             return Lexer.valueOf(source.getString(start + 2, len - 2), 16); // number
         case FLOATING:
-            return Double.valueOf(source.getString(start, len)); // number
+            final String str   = source.getString(start, len);
+            final double value = Double.valueOf(str);
+            if (str.indexOf('.') != -1) {
+                return value; //number
+            }
+            //anything without an explicit decimal point is still subject to a
+            //"representable as int or long" check. Then the programmer does not
+            //explicitly code something as a double. For example new Color(int, int, int)
+            //and new Color(float, float, float) will get ambiguous for cases like
+            //new Color(1.0, 1.5, 1.5) if we don't respect the decimal point.
+            //yet we don't want e.g. 1e6 to be a double unnecessarily
+            if (JSType.isRepresentableAsInt(value) && !JSType.isNegativeZero(value)) {
+                return (int)value;
+            } else if (JSType.isRepresentableAsLong(value) && !JSType.isNegativeZero(value)) {
+                return (long)value;
+            }
+            return value;
         case STRING:
             return source.getString(start, len); // String
         case ESCSTRING:
--- a/nashorn/src/jdk/nashorn/internal/parser/Parser.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/parser/Parser.java	Wed Feb 26 13:17:57 2014 +0100
@@ -25,9 +25,9 @@
 
 package jdk.nashorn.internal.parser;
 
+import static jdk.nashorn.internal.codegen.CompilerConstants.ANON_FUNCTION_PREFIX;
 import static jdk.nashorn.internal.codegen.CompilerConstants.EVAL;
-import static jdk.nashorn.internal.codegen.CompilerConstants.ANON_FUNCTION_PREFIX;
-import static jdk.nashorn.internal.codegen.CompilerConstants.RUN_SCRIPT;
+import static jdk.nashorn.internal.codegen.CompilerConstants.PROGRAM;
 import static jdk.nashorn.internal.parser.TokenType.ASSIGN;
 import static jdk.nashorn.internal.parser.TokenType.CASE;
 import static jdk.nashorn.internal.parser.TokenType.CATCH;
@@ -52,8 +52,10 @@
 import static jdk.nashorn.internal.parser.TokenType.TERNARY;
 import static jdk.nashorn.internal.parser.TokenType.WHILE;
 
+import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Deque;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -106,6 +108,7 @@
 import jdk.nashorn.internal.runtime.ErrorManager;
 import jdk.nashorn.internal.runtime.JSErrorType;
 import jdk.nashorn.internal.runtime.ParserException;
+import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
 import jdk.nashorn.internal.runtime.ScriptEnvironment;
 import jdk.nashorn.internal.runtime.ScriptingFunctions;
 import jdk.nashorn.internal.runtime.Source;
@@ -126,6 +129,7 @@
     private List<Statement> functionDeclarations;
 
     private final BlockLexicalContext lc = new BlockLexicalContext();
+    private final Deque<Object> defaultNames = new ArrayDeque<>();
 
     /** Namespace for function names where not explicitly given */
     private final Namespace namespace;
@@ -135,6 +139,8 @@
     /** to receive line information from Lexer when scanning multine literals. */
     protected final Lexer.LineInfoReceiver lineInfoReceiver;
 
+    private int nextFunctionId;
+
     /**
      * Constructor
      *
@@ -147,17 +153,32 @@
     }
 
     /**
+     * Constructor
+     *
+     * @param env     script environment
+     * @param source  source to parse
+     * @param errors  error manager
+     * @param strict  strict
+     */
+    public Parser(final ScriptEnvironment env, final Source source, final ErrorManager errors, final boolean strict) {
+        this(env, source, errors, strict, FunctionNode.FIRST_FUNCTION_ID, 0);
+    }
+
+    /**
      * Construct a parser.
      *
      * @param env     script environment
      * @param source  source to parse
      * @param errors  error manager
      * @param strict  parser created with strict mode enabled.
+     * @param nextFunctionId  starting value for assigning new unique ids to function nodes
+     * @param lineOffset line offset to start counting lines from
      */
-    public Parser(final ScriptEnvironment env, final Source source, final ErrorManager errors, final boolean strict) {
-        super(source, errors, strict);
-        this.env       = env;
+    public Parser(final ScriptEnvironment env, final Source source, final ErrorManager errors, final boolean strict, final int nextFunctionId, final int lineOffset) {
+        super(source, errors, strict, lineOffset);
+        this.env        = env;
         this.namespace = new Namespace(env.getNamespace());
+        this.nextFunctionId    = nextFunctionId;
         this.scripting = env._scripting;
         if (this.scripting) {
             this.lineInfoReceiver = new Lexer.LineInfoReceiver() {
@@ -175,17 +196,26 @@
     }
 
     /**
+     * Sets the name for the first function. This is only used when reparsing anonymous functions to ensure they can
+     * preserve their already assigned name, as that name doesn't appear in their source text.
+     * @param name the name for the first parsed function.
+     */
+    public void setFunctionName(final String name) {
+        defaultNames.push(new IdentNode(0, 0, name));
+    }
+
+    /**
      * Execute parse and return the resulting function node.
      * Errors will be thrown and the error manager will contain information
      * if parsing should fail
      *
      * This is the default parse call, which will name the function node
-     * "runScript" {@link CompilerConstants#RUN_SCRIPT}
+     * {code :program} {@link CompilerConstants#PROGRAM}
      *
      * @return function node resulting from successful parse
      */
     public FunctionNode parse() {
-        return parse(RUN_SCRIPT.symbolName());
+        return parse(PROGRAM.symbolName());
     }
 
     /**
@@ -198,19 +228,40 @@
      * @return function node resulting from successful parse
      */
     public FunctionNode parse(final String scriptName) {
+        return parse(scriptName, 0, source.getLength(), false);
+    }
+
+    /**
+     * Execute parse and return the resulting function node.
+     * Errors will be thrown and the error manager will contain information
+     * if parsing should fail
+     *
+     * This should be used to create one and only one function node
+     *
+     * @param scriptName name for the script, given to the parsed FunctionNode
+     * @param startPos start position in source
+     * @param len length of parse
+     * @param allowPropertyFunction if true, "get" and "set" are allowed as first tokens of the program, followed by
+     * a property getter or setter function. This is used when reparsing a function that can potentially be defined as a
+     * property getter or setter in an object literal.
+     *
+     * @return function node resulting from successful parse
+     */
+    public FunctionNode parse(final String scriptName, final int startPos, final int len, final boolean allowPropertyFunction) {
         final long t0 = Timing.isEnabled() ? System.currentTimeMillis() : 0L;
         LOG.info(this, " begin for '", scriptName, "'");
 
         try {
             stream = new TokenStream();
-            lexer  = new Lexer(source, stream, scripting && !env._no_syntax_extensions);
+            lexer  = new Lexer(source, startPos, len, stream, scripting && !env._no_syntax_extensions);
+            lexer.line = lexer.pendingLine = lineOffset + 1;
+            line = lineOffset;
 
             // Set up first token (skips opening EOL.)
             k = -1;
             next();
-
             // Begin parse.
-            return program(scriptName);
+            return program(scriptName, allowPropertyFunction);
         } catch (final Exception e) {
             handleParseException(e);
 
@@ -263,6 +314,7 @@
         try {
             stream = new TokenStream();
             lexer  = new Lexer(source, stream, scripting && !env._no_syntax_extensions);
+            int functionLine = line;
 
             // Set up first token (skips opening EOL.)
             k = -1;
@@ -274,12 +326,13 @@
 
             FunctionNode function = newFunctionNode(
                 functionToken,
-                new IdentNode(functionToken, Token.descPosition(functionToken), RUN_SCRIPT.symbolName()),
+                new IdentNode(functionToken, Token.descPosition(functionToken), PROGRAM.symbolName()),
                 new ArrayList<IdentNode>(),
-                FunctionNode.Kind.NORMAL);
+                FunctionNode.Kind.NORMAL,
+                functionLine);
 
             functionDeclarations = new ArrayList<>();
-            sourceElements();
+            sourceElements(false);
             addFunctionDeclarations(function);
             functionDeclarations = null;
 
@@ -380,7 +433,7 @@
      * @param ident Name of function.
      * @return New block.
      */
-    private FunctionNode newFunctionNode(final long startToken, final IdentNode ident, final List<IdentNode> parameters, final FunctionNode.Kind kind) {
+    private FunctionNode newFunctionNode(final long startToken, final IdentNode ident, final List<IdentNode> parameters, final FunctionNode.Kind kind, final int functionLine) {
         // Build function name.
         final StringBuilder sb = new StringBuilder();
 
@@ -393,12 +446,9 @@
         sb.append(ident.getName());
 
         final String name = namespace.uniqueName(sb.toString());
-        assert parentFunction != null || name.equals(RUN_SCRIPT.symbolName())  : "name = " + name;// must not rename runScript().
+        assert parentFunction != null || name.equals(PROGRAM.symbolName()) || name.startsWith(RecompilableScriptFunctionData.RECOMPILATION_PREFIX) : "name = " + name;
 
         int flags = 0;
-        if (parentFunction == null) {
-            flags |= FunctionNode.IS_PROGRAM;
-        }
         if (isStrictMode) {
             flags |= FunctionNode.IS_STRICT;
         }
@@ -407,12 +457,16 @@
                 flags |= FunctionNode.CAN_SPECIALIZE;
             }
         }
+        if (parentFunction == null) {
+            flags |= FunctionNode.IS_PROGRAM;
+        }
 
         // Start new block.
-        FunctionNode functionNode =
+        final FunctionNode functionNode =
             new FunctionNode(
                 source,
-                line, //TODO?
+                nextFunctionId++,
+                functionLine,
                 token,
                 Token.descPosition(token),
                 startToken,
@@ -446,9 +500,8 @@
         return lc.pop(functionNode).
             setBody(lc, newBody).
             setLastToken(lc, lastToken).
-            setState(lc, errors.hasErrors() ? CompilationState.PARSE_ERROR : CompilationState.PARSED).
-            snapshot(lc);
-        }
+            setState(lc, errors.hasErrors() ? CompilationState.PARSE_ERROR : CompilationState.PARSED);
+    }
 
     /**
      * Get the statements in a block.
@@ -619,19 +672,21 @@
      *
      * Parse the top level script.
      */
-    private FunctionNode program(final String scriptName) {
-        // Make a fake token for the script.
-        final long functionToken = Token.toDesc(FUNCTION, 0, source.getLength());
+    private FunctionNode program(final String scriptName, final boolean allowPropertyFunction) {
+        // Make a pseudo-token for the script holding its start and length.
+        final long functionToken = Token.toDesc(FUNCTION, getProgramStartPosition(token), source.getLength());
+        final int  functionLine  = line;
         // Set up the script to append elements.
 
         FunctionNode script = newFunctionNode(
             functionToken,
             new IdentNode(functionToken, Token.descPosition(functionToken), scriptName),
             new ArrayList<IdentNode>(),
-            FunctionNode.Kind.SCRIPT);
+            FunctionNode.Kind.SCRIPT,
+            functionLine);
 
         functionDeclarations = new ArrayList<>();
-        sourceElements();
+        sourceElements(allowPropertyFunction);
         addFunctionDeclarations(script);
         functionDeclarations = null;
 
@@ -650,6 +705,20 @@
     }
 
     /**
+     * Returns the start position of the program based on its first token. Normally returns the position of the token
+     * itself, except in case of string tokens which report their position past their opening delimiter and thus need
+     * to have one subtracted from their position.
+     * @param firstToken the first token of the program
+     * @return the start position of the program
+     */
+    private static int getProgramStartPosition(final long firstToken) {
+        final int start = Token.descPosition(firstToken);
+        switch(Token.descType(firstToken)) {
+            case STRING: case ESCSTRING: case EXECSTRING: return start - 1;
+            default: return start;
+        }
+    }
+    /**
      * Directive value or null if statement is not a directive.
      *
      * @param stmt Statement to be checked
@@ -682,10 +751,12 @@
      *
      * Parse the elements of the script or function.
      */
-    private void sourceElements() {
-        List<Node>    directiveStmts = null;
-        boolean       checkDirective = true;
-        final boolean oldStrictMode = isStrictMode;
+    private void sourceElements(final boolean shouldAllowPropertyFunction) {
+        List<Node>    directiveStmts        = null;
+        boolean       checkDirective        = true;
+        boolean       allowPropertyFunction = shouldAllowPropertyFunction;
+        final boolean oldStrictMode         = isStrictMode;
+
 
         try {
             // If is a script, then process until the end of the script.
@@ -697,7 +768,8 @@
 
                 try {
                     // Get the next element.
-                    statement(true);
+                    statement(true, allowPropertyFunction);
+                    allowPropertyFunction = false;
 
                     // check for directive prologues
                     if (checkDirective) {
@@ -780,13 +852,13 @@
      * Parse any of the basic statement types.
      */
     private void statement() {
-        statement(false);
+        statement(false, false);
     }
 
     /**
      * @param topLevel does this statement occur at the "top level" of a script or a function?
      */
-    private void statement(final boolean topLevel) {
+    private void statement(final boolean topLevel, final boolean allowPropertyFunction) {
         if (type == FUNCTION) {
             // As per spec (ECMA section 12), function declarations as arbitrary statement
             // is not "portable". Implementation can issue a warning or disallow the same.
@@ -854,6 +926,20 @@
                     labelStatement();
                     return;
                 }
+                if(allowPropertyFunction) {
+                    final String ident = (String)getValue();
+                    final long propertyToken = token;
+                    final int propertyLine = line;
+                    if("get".equals(ident)) {
+                        next();
+                        addPropertyFunctionStatement(propertyGetterFunction(propertyToken, propertyLine));
+                        return;
+                    } else if("set".equals(ident)) {
+                        next();
+                        addPropertyFunctionStatement(propertySetterFunction(propertyToken, propertyLine));
+                        return;
+                    }
+                }
             }
 
             expressionStatement();
@@ -861,6 +947,11 @@
         }
     }
 
+    private void addPropertyFunctionStatement(final PropertyFunction propertyFunction) {
+        final FunctionNode fn = propertyFunction.functionNode;
+        functionDeclarations.add(new ExpressionStatement(fn.getLineNumber(), fn.getToken(), finish, fn));
+    }
+
     /**
      * block :
      *      { StatementList? }
@@ -964,7 +1055,12 @@
                 next();
 
                 // Get initializer expression. Suppress IN if not statement.
-                init = assignmentExpression(!isStatement);
+                defaultNames.push(name);
+                try {
+                    init = assignmentExpression(!isStatement);
+                } finally {
+                    defaultNames.pop();
+                }
             }
 
             // Allocate var node.
@@ -2113,8 +2209,8 @@
     private PropertyNode propertyAssignment() {
         // Capture firstToken.
         final long propertyToken = token;
-
-        FunctionNode functionNode;
+        final int  functionLine  = line;
+
         PropertyKey propertyName;
 
         if (type == IDENT) {
@@ -2126,36 +2222,12 @@
 
                 switch (ident) {
                 case "get":
-                    final PropertyKey getIdent = propertyName();
-                    final String getterName = getIdent.getPropertyName();
-                    final IdentNode getNameNode = new IdentNode(((Node)getIdent).getToken(), finish, NameCodec.encode("get " + getterName));
-                    expect(LPAREN);
-                    expect(RPAREN);
-                    functionNode = functionBody(getSetToken, getNameNode, new ArrayList<IdentNode>(), FunctionNode.Kind.GETTER);
-                    return new PropertyNode(propertyToken, finish, getIdent, null, functionNode, null);
+                    final PropertyFunction getter = propertyGetterFunction(getSetToken, functionLine);
+                    return new PropertyNode(propertyToken, finish, getter.ident, null, getter.functionNode, null);
 
                 case "set":
-                    final PropertyKey setIdent = propertyName();
-                    final String setterName = setIdent.getPropertyName();
-                    final IdentNode setNameNode = new IdentNode(((Node)setIdent).getToken(), finish, NameCodec.encode("set " + setterName));
-                    expect(LPAREN);
-                    // be sloppy and allow missing setter parameter even though
-                    // spec does not permit it!
-                    final IdentNode argIdent;
-                    if (type == IDENT || isNonStrictModeIdent()) {
-                        argIdent = getIdent();
-                        verifyStrictIdent(argIdent, "setter argument");
-                    } else {
-                        argIdent = null;
-                    }
-                    expect(RPAREN);
-                    List<IdentNode> parameters = new ArrayList<>();
-                    if (argIdent != null) {
-                        parameters.add(argIdent);
-                    }
-                    functionNode = functionBody(getSetToken, setNameNode, parameters, FunctionNode.Kind.SETTER);
-                    return new PropertyNode(propertyToken, finish, setIdent, null, null, functionNode);
-
+                    final PropertyFunction setter = propertySetterFunction(getSetToken, functionLine);
+                    return new PropertyNode(propertyToken, finish, setter.ident, null, null, setter.functionNode);
                 default:
                     break;
                 }
@@ -2168,7 +2240,57 @@
 
         expect(COLON);
 
-        return new PropertyNode(propertyToken, finish, propertyName, assignmentExpression(false), null, null);
+        defaultNames.push(propertyName);
+        try {
+            return new PropertyNode(propertyToken, finish, propertyName, assignmentExpression(false), null, null);
+        } finally {
+            defaultNames.pop();
+        }
+    }
+
+    private PropertyFunction propertyGetterFunction(final long getSetToken, final int functionLine) {
+        final PropertyKey getIdent = propertyName();
+        final String getterName = getIdent.getPropertyName();
+        final IdentNode getNameNode = new IdentNode(((Node)getIdent).getToken(), finish, NameCodec.encode("get " + getterName));
+        expect(LPAREN);
+        expect(RPAREN);
+        final FunctionNode functionNode = functionBody(getSetToken, getNameNode, new ArrayList<IdentNode>(), FunctionNode.Kind.GETTER, functionLine);
+
+        return new PropertyFunction(getIdent, functionNode);
+    }
+
+    private PropertyFunction propertySetterFunction(final long getSetToken, final int functionLine) {
+        final PropertyKey setIdent = propertyName();
+        final String setterName = setIdent.getPropertyName();
+        final IdentNode setNameNode = new IdentNode(((Node)setIdent).getToken(), finish, NameCodec.encode("set " + setterName));
+        expect(LPAREN);
+        // be sloppy and allow missing setter parameter even though
+        // spec does not permit it!
+        final IdentNode argIdent;
+        if (type == IDENT || isNonStrictModeIdent()) {
+            argIdent = getIdent();
+            verifyStrictIdent(argIdent, "setter argument");
+        } else {
+            argIdent = null;
+        }
+        expect(RPAREN);
+        final List<IdentNode> parameters = new ArrayList<>();
+        if (argIdent != null) {
+            parameters.add(argIdent);
+        }
+        final FunctionNode functionNode = functionBody(getSetToken, setNameNode, parameters, FunctionNode.Kind.SETTER, functionLine);
+
+        return new PropertyFunction(setIdent, functionNode);
+    }
+
+    private static class PropertyFunction {
+        final PropertyKey ident;
+        final FunctionNode functionNode;
+
+        PropertyFunction(final PropertyKey ident, final FunctionNode function) {
+            this.ident = ident;
+            this.functionNode = function;
+        }
     }
 
     /**
@@ -2464,7 +2586,7 @@
         // name is null, generate anonymous name
         boolean isAnonymous = false;
         if (name == null) {
-            final String tmpName = ANON_FUNCTION_PREFIX.symbolName() + functionLine;
+            final String tmpName = getDefaultValidFunctionName(functionLine);
             name = new IdentNode(functionToken, Token.descPosition(functionToken), tmpName);
             isAnonymous = true;
         }
@@ -2473,7 +2595,7 @@
         final List<IdentNode> parameters = formalParameterList();
         expect(RPAREN);
 
-        FunctionNode functionNode = functionBody(functionToken, name, parameters, FunctionNode.Kind.NORMAL);
+        FunctionNode functionNode = functionBody(functionToken, name, parameters, FunctionNode.Kind.NORMAL, functionLine);
 
         if (isStatement) {
             if (topLevel) {
@@ -2539,6 +2661,47 @@
         return functionNode;
     }
 
+    private String getDefaultValidFunctionName(final int functionLine) {
+        final String defaultFunctionName = getDefaultFunctionName();
+        return isValidIdentifier(defaultFunctionName) ? defaultFunctionName : (ANON_FUNCTION_PREFIX.symbolName() + functionLine);
+    }
+
+    private static boolean isValidIdentifier(String name) {
+        if(name == null || name.isEmpty()) {
+            return false;
+        }
+        if(!Character.isJavaIdentifierStart(name.charAt(0))) {
+            return false;
+        }
+        for(int i = 1; i < name.length(); ++i) {
+            if(!Character.isJavaIdentifierPart(name.charAt(i))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private String getDefaultFunctionName() {
+        if(!defaultNames.isEmpty()) {
+            final Object nameExpr = defaultNames.peek();
+            if(nameExpr instanceof PropertyKey) {
+                markDefaultNameUsed();
+                return ((PropertyKey)nameExpr).getPropertyName();
+            } else if(nameExpr instanceof AccessNode) {
+                markDefaultNameUsed();
+                return ((AccessNode)nameExpr).getProperty().getName();
+            }
+        }
+        return null;
+    }
+
+    private void markDefaultNameUsed() {
+        defaultNames.pop();
+        // Can be any value as long as getDefaultFunctionName doesn't recognize it as something it can extract a value
+        // from. Can't be null
+        defaultNames.push("");
+    }
+
     /**
      * FormalParameterList :
      *      Identifier
@@ -2601,13 +2764,13 @@
      * Parse function body.
      * @return function node (body.)
      */
-    private FunctionNode functionBody(final long firstToken, final IdentNode ident, final List<IdentNode> parameters, final FunctionNode.Kind kind) {
+    private FunctionNode functionBody(final long firstToken, final IdentNode ident, final List<IdentNode> parameters, final FunctionNode.Kind kind, final int functionLine) {
         FunctionNode functionNode = null;
         long lastToken = 0L;
 
         try {
             // Create a new function block.
-            functionNode = newFunctionNode(firstToken, ident, parameters, kind);
+            functionNode = newFunctionNode(firstToken, ident, parameters, kind, functionLine);
 
             // Nashorn extension: expression closures
             if (!env._no_syntax_extensions && type != LBRACE) {
@@ -2620,11 +2783,13 @@
 
                 // just expression as function body
                 final Expression expr = assignmentExpression(true);
+                lastToken = previousToken;
                 assert lc.getCurrentBlock() == lc.getFunctionBody(functionNode);
-                final ReturnNode returnNode = new ReturnNode(functionNode.getLineNumber(), expr.getToken(), finish, expr);
+                // EOL uses length field to store the line number
+                final int lastFinish = Token.descPosition(lastToken) + (Token.descType(lastToken) == EOL ? 0 : Token.descLength(lastToken));
+                final ReturnNode returnNode = new ReturnNode(functionNode.getLineNumber(), expr.getToken(), lastFinish, expr);
                 appendStatement(returnNode);
-                lastToken = token;
-                functionNode.setFinish(Token.descPosition(token) + Token.descLength(token));
+                functionNode.setFinish(lastFinish);
 
             } else {
                 expect(LBRACE);
@@ -2633,7 +2798,7 @@
                 final List<Statement> prevFunctionDecls = functionDeclarations;
                 functionDeclarations = new ArrayList<>();
                 try {
-                    sourceElements();
+                    sourceElements(false);
                     addFunctionDeclarations(functionNode);
                 } finally {
                     functionDeclarations = prevFunctionDecls;
@@ -2934,19 +3099,28 @@
                 next();
 
                  // Get the next primary expression.
-                Expression rhs = unaryExpression();
-
-                // Get precedence of next operator.
-                int nextPrecedence = type.getPrecedence();
-
-                // Subtask greater precedence.
-                while (type.isOperator(noIn) &&
-                       (nextPrecedence > precedence ||
-                       nextPrecedence == precedence && !type.isLeftAssociative())) {
-                    rhs = expression(rhs, nextPrecedence, noIn);
-                    nextPrecedence = type.getPrecedence();
+                Expression rhs;
+                final boolean isAssign = Token.descType(op) == ASSIGN;
+                if(isAssign) {
+                    defaultNames.push(lhs);
                 }
-
+                try {
+                    rhs = unaryExpression();
+                    // Get precedence of next operator.
+                    int nextPrecedence = type.getPrecedence();
+
+                    // Subtask greater precedence.
+                    while (type.isOperator(noIn) &&
+                           (nextPrecedence > precedence ||
+                           nextPrecedence == precedence && !type.isLeftAssociative())) {
+                        rhs = expression(rhs, nextPrecedence, noIn);
+                        nextPrecedence = type.getPrecedence();
+                    }
+                } finally {
+                    if(isAssign) {
+                        defaultNames.pop();
+                    }
+                }
                 lhs = verifyAssignment(op, lhs, rhs);
             }
 
--- a/nashorn/src/jdk/nashorn/internal/runtime/AccessorProperty.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/AccessorProperty.java	Wed Feb 26 13:17:57 2014 +0100
@@ -25,23 +25,25 @@
 
 package jdk.nashorn.internal.runtime;
 
-import static jdk.nashorn.internal.codegen.ObjectClassGenerator.ACCESSOR_TYPES;
 import static jdk.nashorn.internal.codegen.ObjectClassGenerator.DEBUG_FIELDS;
 import static jdk.nashorn.internal.codegen.ObjectClassGenerator.LOG;
 import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY;
-import static jdk.nashorn.internal.codegen.ObjectClassGenerator.PRIMITIVE_TYPE;
+import static jdk.nashorn.internal.codegen.ObjectClassGenerator.PRIMITIVE_FIELD_TYPE;
 import static jdk.nashorn.internal.codegen.ObjectClassGenerator.createGetter;
-import static jdk.nashorn.internal.codegen.ObjectClassGenerator.createGuardBoxedPrimitiveSetter;
 import static jdk.nashorn.internal.codegen.ObjectClassGenerator.createSetter;
-import static jdk.nashorn.internal.codegen.ObjectClassGenerator.getAccessorType;
-import static jdk.nashorn.internal.codegen.ObjectClassGenerator.getAccessorTypeIndex;
-import static jdk.nashorn.internal.codegen.ObjectClassGenerator.getNumberOfAccessorTypes;
+import static jdk.nashorn.internal.codegen.ObjectClassGenerator.getFieldCount;
+import static jdk.nashorn.internal.codegen.ObjectClassGenerator.getFieldName;
+import static jdk.nashorn.internal.codegen.ObjectClassGenerator.shouldInstrument;
 import static jdk.nashorn.internal.lookup.Lookup.MH;
 import static jdk.nashorn.internal.lookup.MethodHandleFactory.stripName;
+import static jdk.nashorn.internal.runtime.JSType.getAccessorTypeIndex;
+import static jdk.nashorn.internal.runtime.JSType.getNumberOfAccessorTypes;
+import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
 
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
-import java.lang.invoke.MethodType;
+import java.util.logging.Level;
+
 import jdk.nashorn.internal.codegen.ObjectClassGenerator;
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.lookup.Lookup;
@@ -51,8 +53,8 @@
  * An AccessorProperty is the most generic property type. An AccessorProperty is
  * represented as fields in a ScriptObject class.
  */
-public final class AccessorProperty extends Property {
-    private static final MethodHandles.Lookup lookup = MethodHandles.lookup();
+public class AccessorProperty extends Property {
+    private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
     private static final MethodHandle REPLACE_MAP = findOwnMH("replaceMap", Object.class, Object.class, PropertyMap.class, String.class, Class.class, Class.class);
 
     private static final int NOOF_TYPES = getNumberOfAccessorTypes();
@@ -63,49 +65,56 @@
      * these are the most frequently retrieved ones, and lookup of method handle natives only registers in the profiler
      * for them.
      */
-    private static ClassValue<GettersSetters> GETTERS_SETTERS = new ClassValue<GettersSetters>() {
+    private static ClassValue<Accessors> GETTERS_SETTERS = new ClassValue<Accessors>() {
         @Override
-        protected GettersSetters computeValue(Class<?> structure) {
-            return new GettersSetters(structure);
+        protected Accessors computeValue(final Class<?> structure) {
+            return new Accessors(structure);
         }
     };
 
-    /** Property getter cache */
-    private MethodHandle[] getters = new MethodHandle[NOOF_TYPES];
-
-    private static final MethodType[] ACCESSOR_GETTER_TYPES = new MethodType[NOOF_TYPES];
-    private static final MethodType[] ACCESSOR_SETTER_TYPES = new MethodType[NOOF_TYPES];
-    private static final MethodType ACCESSOR_GETTER_PRIMITIVE_TYPE;
-    private static final MethodType ACCESSOR_SETTER_PRIMITIVE_TYPE;
-    private static final MethodHandle SPILL_ELEMENT_GETTER;
-    private static final MethodHandle SPILL_ELEMENT_SETTER;
-
-    private static final int SPILL_CACHE_SIZE = 8;
-    private static final MethodHandle[] SPILL_ACCESSORS = new MethodHandle[SPILL_CACHE_SIZE * 2];
+    private static class Accessors {
+        final MethodHandle[] objectGetters;
+        final MethodHandle[] objectSetters;
+        final MethodHandle[] primitiveGetters;
+        final MethodHandle[] primitiveSetters;
 
-    static {
-        MethodType getterPrimitiveType = null;
-        MethodType setterPrimitiveType = null;
+        /**
+         * Normal
+         * @param structure
+         */
+        Accessors(final Class<?> structure) {
+            final int fieldCount = getFieldCount(structure);
+            objectGetters    = new MethodHandle[fieldCount];
+            objectSetters    = new MethodHandle[fieldCount];
+            primitiveGetters = new MethodHandle[fieldCount];
+            primitiveSetters = new MethodHandle[fieldCount];
 
-        for (int i = 0; i < NOOF_TYPES; i++) {
-            final Type type = ACCESSOR_TYPES.get(i);
-            ACCESSOR_GETTER_TYPES[i] = MH.type(type.getTypeClass(), Object.class);
-            ACCESSOR_SETTER_TYPES[i] = MH.type(void.class, Object.class, type.getTypeClass());
+            for (int i = 0; i < fieldCount; i++) {
+                final String fieldName = getFieldName(i, Type.OBJECT);
+                final Class<?> typeClass = Type.OBJECT.getTypeClass();
+                objectGetters[i] = MH.asType(MH.getter(LOOKUP, structure, fieldName, typeClass), Lookup.GET_OBJECT_TYPE);
+                objectSetters[i] = MH.asType(MH.setter(LOOKUP, structure, fieldName, typeClass), Lookup.SET_OBJECT_TYPE);
+            }
 
-            if (type == PRIMITIVE_TYPE) {
-                getterPrimitiveType = ACCESSOR_GETTER_TYPES[i];
-                setterPrimitiveType = ACCESSOR_SETTER_TYPES[i];
+            if (!OBJECT_FIELDS_ONLY) {
+                for (int i = 0; i < fieldCount; i++) {
+                    final String fieldNamePrimitive = getFieldName(i, PRIMITIVE_FIELD_TYPE);
+                    final Class<?> typeClass = PRIMITIVE_FIELD_TYPE.getTypeClass();
+                    primitiveGetters[i] = MH.asType(MH.getter(LOOKUP, structure, fieldNamePrimitive, typeClass), Lookup.GET_PRIMITIVE_TYPE);
+                    primitiveSetters[i] = MH.asType(MH.setter(LOOKUP, structure, fieldNamePrimitive, typeClass), Lookup.SET_PRIMITIVE_TYPE);
+                }
             }
         }
-
-        ACCESSOR_GETTER_PRIMITIVE_TYPE = getterPrimitiveType;
-        ACCESSOR_SETTER_PRIMITIVE_TYPE = setterPrimitiveType;
+    }
 
-        final MethodType spillGetterType = MethodType.methodType(Object[].class, Object.class);
-        final MethodHandle spillGetter = MH.asType(MH.getter(MethodHandles.lookup(), ScriptObject.class, "spill", Object[].class), spillGetterType);
-        SPILL_ELEMENT_GETTER = MH.filterArguments(MH.arrayElementGetter(Object[].class), 0, spillGetter);
-        SPILL_ELEMENT_SETTER = MH.filterArguments(MH.arrayElementSetter(Object[].class), 0, spillGetter);
-    }
+    /**
+     * Property getter cache
+     *   Note that we can't do the same simple caching for optimistic getters,
+     *   due to the fact that they are bound to a program point, which will
+     *   produce different boun method handles wrapping the same access mechanism
+     *   depending on callsite
+     */
+    private MethodHandle[] GETTER_CACHE = new MethodHandle[NOOF_TYPES];
 
     /**
      * Create a new accessor property. Factory method used by nasgen generated code.
@@ -122,16 +131,16 @@
     }
 
     /** Seed getter for the primitive version of this field (in -Dnashorn.fields.dual=true mode) */
-    private MethodHandle primitiveGetter;
+    protected final MethodHandle primitiveGetter;
 
     /** Seed setter for the primitive version of this field (in -Dnashorn.fields.dual=true mode) */
-    private MethodHandle primitiveSetter;
+    protected final MethodHandle primitiveSetter;
 
     /** Seed getter for the Object version of this field */
-    private MethodHandle objectGetter;
+    protected final MethodHandle objectGetter;
 
     /** Seed setter for the Object version of this field */
-    private MethodHandle objectSetter;
+    protected final MethodHandle objectSetter;
 
     /**
      * Current type of this object, in object only mode, this is an Object.class. In dual-fields mode
@@ -141,6 +150,8 @@
     private Class<?> currentType;
 
     /**
+     * DELEGATE CONSTRUCTOR
+     *
      * Delegate constructor. This is used when adding properties to the Global scope, which
      * is necessary for outermost levels in a script (the ScriptObject is represented by
      * a JO-prefixed ScriptObject class, but the properties need to be in the Global scope
@@ -154,27 +165,38 @@
 
         this.primitiveGetter = bindTo(property.primitiveGetter, delegate);
         this.primitiveSetter = bindTo(property.primitiveSetter, delegate);
-        this.objectGetter    = bindTo(property.ensureObjectGetter(), delegate);
-        this.objectSetter    = bindTo(property.ensureObjectSetter(), delegate);
+        this.objectGetter    = bindTo(property.objectGetter, delegate);
+        this.objectSetter    = bindTo(property.objectSetter, delegate);
 
         setCurrentType(property.getCurrentType());
     }
 
     /**
+     * SPILL PROPERTY or USER ACCESSOR PROPERTY abstract constructor
+     *
      * Constructor for spill properties. Array getters and setters will be created on demand.
      *
      * @param key    the property key
      * @param flags  the property flags
      * @param slot   spill slot
+     * @param objectGetter
+     * @param objectSetter
+     * @param primitiveGetter
+     * @param primitiveSetter
      */
-    public AccessorProperty(final String key, final int flags, final int slot) {
+    protected AccessorProperty(final String key, final int flags, final int slot, final MethodHandle primitiveGetter, final MethodHandle primitiveSetter, final MethodHandle objectGetter, final MethodHandle objectSetter) {
         super(key, flags, slot);
-        assert (flags & IS_SPILL) == IS_SPILL;
-
-        setCurrentType(Object.class);
+        assert getClass() != AccessorProperty.class;
+        this.primitiveGetter = primitiveGetter;
+        this.primitiveSetter = primitiveSetter;
+        this.objectGetter    = objectGetter;
+        this.objectSetter    = objectSetter;
+        initializeType();
     }
 
     /**
+     * NASGEN constructor
+     *
      * Constructor. Similar to the constructor with both primitive getters and setters, the difference
      * here being that only one getter and setter (setter is optional for non writable fields) is given
      * to the constructor, and the rest are created from those. Used e.g. by Nasgen classes
@@ -185,8 +207,9 @@
      * @param getter the property getter
      * @param setter the property setter or null if non writable, non configurable
      */
-    AccessorProperty(final String key, final int flags, final int slot, final MethodHandle getter, final MethodHandle setter) {
-        super(key, flags, slot);
+    private AccessorProperty(final String key, final int flags, final int slot, final MethodHandle getter, final MethodHandle setter) {
+        super(key, flags | (getter.type().returnType().isPrimitive() ? IS_NASGEN_PRIMITIVE : 0), slot);
+        assert !isSpill();
 
         // we don't need to prep the setters these will never be invalidated as this is a nasgen
         // or known type getter/setter. No invalidations will take place
@@ -195,40 +218,31 @@
         final Class<?> setterType = setter == null ? null : setter.type().parameterType(1);
 
         assert setterType == null || setterType == getterType;
-
-        if (getterType.isPrimitive()) {
-            for (int i = 0; i < NOOF_TYPES; i++) {
-                getters[i] = MH.asType(
-                    Lookup.filterReturnType(
-                        getter,
-                        getAccessorType(i).getTypeClass()),
-                    ACCESSOR_GETTER_TYPES[i]);
+        if (OBJECT_FIELDS_ONLY) {
+            primitiveGetter = primitiveSetter = null;
+        } else {
+            if (getterType == int.class || getterType == long.class) {
+                primitiveGetter = MH.asType(getter, Lookup.GET_PRIMITIVE_TYPE);
+                primitiveSetter = setter == null ? null : MH.asType(setter, Lookup.SET_PRIMITIVE_TYPE);
+            } else if (getterType == double.class) {
+                primitiveGetter = MH.asType(MH.filterReturnValue(getter, ObjectClassGenerator.PACK_DOUBLE), Lookup.GET_PRIMITIVE_TYPE);
+                primitiveSetter = setter == null ? null : MH.asType(MH.filterArguments(setter, 1, ObjectClassGenerator.UNPACK_DOUBLE), Lookup.SET_PRIMITIVE_TYPE);
+            } else {
+                primitiveGetter = primitiveSetter = null;
             }
-        } else {
-            objectGetter = getter.type() != Lookup.GET_OBJECT_TYPE ? MH.asType(getter, Lookup.GET_OBJECT_TYPE) : getter;
-            objectSetter = setter != null && setter.type() != Lookup.SET_OBJECT_TYPE ? MH.asType(setter, Lookup.SET_OBJECT_TYPE) : setter;
         }
 
-        setCurrentType(getterType);
-    }
-
-    private static class GettersSetters {
-        final MethodHandle[] getters;
-        final MethodHandle[] setters;
+        assert primitiveGetter == null || primitiveGetter.type() == Lookup.GET_PRIMITIVE_TYPE : primitiveGetter + "!=" + Lookup.GET_PRIMITIVE_TYPE;
+        assert primitiveSetter == null || primitiveSetter.type() == Lookup.SET_PRIMITIVE_TYPE : primitiveSetter;
 
-        public GettersSetters(Class<?> structure) {
-            final int fieldCount = ObjectClassGenerator.getFieldCount(structure);
-            getters = new MethodHandle[fieldCount];
-            setters = new MethodHandle[fieldCount];
-            for(int i = 0; i < fieldCount; ++i) {
-                final String fieldName = ObjectClassGenerator.getFieldName(i, Type.OBJECT);
-                getters[i] = MH.asType(MH.getter(lookup, structure, fieldName, Type.OBJECT.getTypeClass()), Lookup.GET_OBJECT_TYPE);
-                setters[i] = MH.asType(MH.setter(lookup, structure, fieldName, Type.OBJECT.getTypeClass()), Lookup.SET_OBJECT_TYPE);
-            }
-        }
+        objectGetter  = getter.type() != Lookup.GET_OBJECT_TYPE ? MH.asType(getter, Lookup.GET_OBJECT_TYPE) : getter;
+        objectSetter  = setter != null && setter.type() != Lookup.SET_OBJECT_TYPE ? MH.asType(setter, Lookup.SET_OBJECT_TYPE) : setter;
+
+        setCurrentType(OBJECT_FIELDS_ONLY ? Object.class : getterType);
     }
 
     /**
+     * Normal ACCESS PROPERTY constructor given a structure glass.
      * Constructor for dual field AccessorPropertys.
      *
      * @param key              property key
@@ -244,61 +258,114 @@
          * works in dual field mode, it only means that the property never has a primitive
          * representation.
          */
-        primitiveGetter = null;
-        primitiveSetter = null;
 
         if (isParameter() && hasArguments()) {
-            final MethodHandle arguments   = MH.getter(lookup, structure, "arguments", ScriptObject.class);
-
+            //parameters are always stored in an object array, which may or may not be a good idea
+            final MethodHandle arguments = MH.getter(LOOKUP, structure, "arguments", ScriptObject.class);
             objectGetter = MH.asType(MH.insertArguments(MH.filterArguments(ScriptObject.GET_ARGUMENT.methodHandle(), 0, arguments), 1, slot), Lookup.GET_OBJECT_TYPE);
             objectSetter = MH.asType(MH.insertArguments(MH.filterArguments(ScriptObject.SET_ARGUMENT.methodHandle(), 0, arguments), 1, slot), Lookup.SET_OBJECT_TYPE);
+            primitiveGetter = null;
+            primitiveSetter = null;
         } else {
-            final GettersSetters gs = GETTERS_SETTERS.get(structure);
-            objectGetter = gs.getters[slot];
-            objectSetter = gs.setters[slot];
-
-            if (!OBJECT_FIELDS_ONLY) {
-                final String fieldNamePrimitive = ObjectClassGenerator.getFieldName(slot, PRIMITIVE_TYPE);
-                final Class<?> typeClass = PRIMITIVE_TYPE.getTypeClass();
-                primitiveGetter = MH.asType(MH.getter(lookup, structure, fieldNamePrimitive, typeClass), ACCESSOR_GETTER_PRIMITIVE_TYPE);
-                primitiveSetter = MH.asType(MH.setter(lookup, structure, fieldNamePrimitive, typeClass), ACCESSOR_SETTER_PRIMITIVE_TYPE);
-            }
+            final Accessors gs = GETTERS_SETTERS.get(structure);
+            objectGetter    = gs.objectGetters[slot];
+            primitiveGetter = gs.primitiveGetters[slot];
+            objectSetter    = gs.objectSetters[slot];
+            primitiveSetter = gs.primitiveSetters[slot];
         }
 
-        Class<?> initialType = null;
-
-        if (OBJECT_FIELDS_ONLY || isAlwaysObject()) {
-            initialType = Object.class;
-        } else if (!canBePrimitive()) {
-            info(key + " cannot be primitive");
-            initialType = Object.class;
-        } else {
-            info(key + " CAN be primitive");
-            if (!canBeUndefined()) {
-                info(key + " is always defined");
-                initialType = int.class; //double works too for less type invalidation, but this requires experimentation, e.g. var x = 17; x += 2 will turn it into double now because of lack of range analysis
-            }
-        }
-
-        // is always object means "is never initialized to undefined, and always of object type
-        setCurrentType(initialType);
+        initializeType();
     }
 
     /**
-     * Copy constructor
+     * Constructor
+     *
+     * @param key          key
+     * @param flags        flags
+     * @param slot         field slot index
+     * @param owner        owner of property
+     * @param initialValue initial value to which the property can be set
+     */
+    protected AccessorProperty(final String key, final int flags, final int slot, final ScriptObject owner, final Object initialValue) {
+        this(key, flags, owner.getClass(), slot);
+        setInitialValue(owner, initialValue);
+    }
+
+    /**
+     * Normal access property constructor that overrides the type
+     * Override the initial type. Used for Object Literals
      *
-     * @param property  source property
+     * @param key          key
+     * @param flags        flags
+     * @param structure    structure to JO subclass
+     * @param slot         field slot index
+     * @param initialType  initial type of the property
      */
-    protected AccessorProperty(final AccessorProperty property) {
+    public AccessorProperty(final String key, final int flags, final Class<?> structure, final int slot, final Class<?> initialType) {
+        this(key, flags, structure, slot);
+        setCurrentType(OBJECT_FIELDS_ONLY ? Object.class : initialType);
+    }
+
+    /**
+     * Copy constructor that may change type and in that case clear the cache. Important to do that before
+     * type change or getters will be created already stale.
+     *
+     * @param property property
+     * @param newType  new type
+     */
+    protected AccessorProperty(final AccessorProperty property, final Class<?> newType) {
         super(property);
 
-        this.getters         = property.getters;
+        this.GETTER_CACHE    = newType != property.getCurrentType() ? new MethodHandle[NOOF_TYPES] : property.GETTER_CACHE;
         this.primitiveGetter = property.primitiveGetter;
         this.primitiveSetter = property.primitiveSetter;
         this.objectGetter    = property.objectGetter;
         this.objectSetter    = property.objectSetter;
 
-        setCurrentType(property.getCurrentType());
+        setCurrentType(newType);
+    }
+
+    /**
+     * COPY constructor
+     *
+     * @param property  source property
+     */
+    protected AccessorProperty(final AccessorProperty property) {
+        this(property, property.getCurrentType());
+    }
+
+    /**
+     * Set initial value of a script object's property
+     * @param owner        owner
+     * @param initialValue initial value
+     */
+    protected final void setInitialValue(final ScriptObject owner, final Object initialValue) {
+        setCurrentType(ObjectClassGenerator.unboxedFieldType(initialValue));
+        if (initialValue instanceof Integer) {
+            invokeSetter(owner, ((Integer)initialValue).intValue());
+        } else if (initialValue instanceof Long) {
+            invokeSetter(owner, ((Long)initialValue).longValue());
+        } else if (initialValue instanceof Double) {
+            invokeSetter(owner, ((Double)initialValue).doubleValue());
+        } else {
+            invokeSetter(owner, initialValue);
+        }
+    }
+
+    /**
+     * Initialize the type of a property
+     */
+    protected final void initializeType() {
+        Class<?> initialType = null;
+        if (OBJECT_FIELDS_ONLY || isAlwaysObject()) {
+            initialType = Object.class;
+        } else {
+            if (!canBeUndefined()) { //todo if !canBeUndefined it means that we have an exact initialType
+                initialType = int.class;
+            }
+            info(getKey(), canBeUndefined() ? " can be undefined" : " is always defined");
+        }
+        setCurrentType(initialType);
     }
 
     private static MethodHandle bindTo(final MethodHandle mh, final Object receiver) {
@@ -310,73 +377,198 @@
     }
 
     @Override
-    protected Property copy() {
+    public Property copy() {
         return new AccessorProperty(this);
     }
 
     @Override
-    public void setObjectValue(final ScriptObject self, final ScriptObject owner, final Object value, final boolean strict)  {
-        if (isSpill()) {
-            self.spill[getSlot()] = value;
-        } else {
-            try {
-                getSetter(Object.class, self.getMap()).invokeExact((Object)self, value);
-            } catch (final Error|RuntimeException e) {
-                throw e;
-            } catch (final Throwable e) {
-                throw new RuntimeException(e);
-            }
+    public Property copy(final Class<?> newType) {
+        return new AccessorProperty(this, newType);
+    }
+
+    @Override
+    public int getIntValue(final ScriptObject self, final ScriptObject owner) {
+        try {
+            return (int)getGetter(int.class).invokeExact((Object)self);
+        } catch (final Error | RuntimeException e) {
+            throw e;
+        } catch (final Throwable e) {
+            throw new RuntimeException(e);
+        }
+     }
+
+     @Override
+     public long getLongValue(final ScriptObject self, final ScriptObject owner) {
+        try {
+            return (long)getGetter(long.class).invokeExact((Object)self);
+        } catch (final Error | RuntimeException e) {
+            throw e;
+        } catch (final Throwable e) {
+            throw new RuntimeException(e);
         }
     }
 
-    @Override
-    public Object getObjectValue(final ScriptObject self, final ScriptObject owner) {
-        if (isSpill()) {
-            return self.spill[getSlot()];
+     @Override
+     public double getDoubleValue(final ScriptObject self, final ScriptObject owner) {
+        try {
+            return (double)getGetter(double.class).invokeExact((Object)self);
+        } catch (final Error | RuntimeException e) {
+            throw e;
+        } catch (final Throwable e) {
+            throw new RuntimeException(e);
         }
+    }
 
+     @Override
+     public Object getObjectValue(final ScriptObject self, final ScriptObject owner) {
         try {
             return getGetter(Object.class).invokeExact((Object)self);
-        } catch (final Error|RuntimeException e) {
+        } catch (final Error | RuntimeException e) {
+            throw e;
+        } catch (final Throwable e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+     /**
+      * Invoke setter for this property with a value
+      * @param self  owner
+      * @param value value
+      */
+    protected final void invokeSetter(final ScriptObject self, final int value) {
+        try {
+            getSetter(int.class, self.getMap()).invokeExact((Object)self, value);
+        } catch (final Error | RuntimeException e) {
+            throw e;
+        } catch (final Throwable e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Invoke setter for this property with a value
+     * @param self  owner
+     * @param value value
+     */
+    protected final void invokeSetter(final ScriptObject self, final long value) {
+        try {
+            getSetter(long.class, self.getMap()).invokeExact((Object)self, value);
+        } catch (final Error | RuntimeException e) {
             throw e;
         } catch (final Throwable e) {
             throw new RuntimeException(e);
         }
     }
 
-    // Spill getters and setters are lazily initialized, see JDK-8011630
-    private MethodHandle ensureObjectGetter() {
-        if (isSpill() && objectGetter == null) {
-            objectGetter = getSpillGetter();
+    /**
+     * Invoke setter for this property with a value
+     * @param self  owner
+     * @param value value
+     */
+    protected final void invokeSetter(final ScriptObject self, final double value) {
+        try {
+            getSetter(double.class, self.getMap()).invokeExact((Object)self, value);
+        } catch (final Error | RuntimeException e) {
+            throw e;
+        } catch (final Throwable e) {
+            throw new RuntimeException(e);
         }
-        return objectGetter;
     }
 
-    private MethodHandle ensureObjectSetter() {
-        if (isSpill() && objectSetter == null) {
-            objectSetter = getSpillSetter();
+    /**
+     * Invoke setter for this property with a value
+     * @param self  owner
+     * @param value value
+     */
+    protected final void invokeSetter(final ScriptObject self, final Object value) {
+        try {
+            getSetter(Object.class, self.getMap()).invokeExact((Object)self, value);
+        } catch (final Error | RuntimeException e) {
+            throw e;
+        } catch (final Throwable e) {
+            throw new RuntimeException(e);
         }
-        return objectSetter;
+    }
+
+    @Override
+    public void setValue(final ScriptObject self, final ScriptObject owner, final int value, final boolean strict)  {
+        assert isConfigurable() || isWritable() : getKey() + " is not writable or configurable";
+        invokeSetter(self, value);
+    }
+
+    @Override
+    public void setValue(final ScriptObject self, final ScriptObject owner, final long value, final boolean strict)  {
+        assert isConfigurable() || isWritable() : getKey() + " is not writable or configurable";
+        invokeSetter(self, value);
+    }
+
+    @Override
+    public void setValue(final ScriptObject self, final ScriptObject owner, final double value, final boolean strict)  {
+        assert isConfigurable() || isWritable() : getKey() + " is not writable or configurable";
+        invokeSetter(self, value);
+    }
+
+    @Override
+    public void setValue(final ScriptObject self, final ScriptObject owner, final Object value, final boolean strict)  {
+        //this is sometimes used for bootstrapping, hence no assert. ugly.
+        invokeSetter(self, value);
     }
 
     @Override
     public MethodHandle getGetter(final Class<?> type) {
         final int i = getAccessorTypeIndex(type);
-        ensureObjectGetter();
+
+        assert type == int.class ||
+                type == long.class ||
+                type == double.class ||
+                type == Object.class :
+                "invalid getter type " + type + " for " + getKey();
 
-        if (getters[i] == null) {
-            getters[i] = debug(
-                createGetter(currentType, type, primitiveGetter, objectGetter),
-                currentType, type, "get");
+        //all this does is add a return value filter for object fields only
+        if (GETTER_CACHE[i] == null) {
+            //System.err.println(this + " needs new getter for " + type);
+            GETTER_CACHE[i] = debug(
+                createGetter(
+                    getCurrentType(),
+                    type,
+                    primitiveGetter,
+                    objectGetter,
+                    INVALID_PROGRAM_POINT),
+                getCurrentType(),
+                type,
+                "get");
+       }
+       assert GETTER_CACHE[i].type().returnType() == type && GETTER_CACHE[i].type().parameterType(0) == Object.class;
+       return GETTER_CACHE[i];
+    }
+
+    @Override
+    public MethodHandle getOptimisticGetter(final Class<?> type, final int programPoint) {
+        // nasgen generated primitive fields like Math.PI have only one known unchangeable primitive type
+        if (objectGetter == null) {
+            return getOptimisticPrimitiveGetter(type, programPoint);
         }
 
-        return getters[i];
+        return debug(
+            createGetter(
+                getCurrentType(),
+                type,
+                primitiveGetter,
+                objectGetter,
+                programPoint),
+            getCurrentType(),
+            type,
+            "get");
+    }
+
+    private MethodHandle getOptimisticPrimitiveGetter(final Class<?> type, final int programPoint) {
+        final MethodHandle g = getGetter(getCurrentType());
+        return MH.asType(OptimisticReturnFilters.filterOptimisticReturnValue(g, type, programPoint), g.type().changeReturnType(type));
     }
 
     private Property getWiderProperty(final Class<?> type) {
-        final AccessorProperty newProperty = new AccessorProperty(this);
-        newProperty.invalidate(type);
-        return newProperty;
+        return copy(type); //invalidate cache of new property
+
     }
 
     private PropertyMap getWiderMap(final PropertyMap oldMap, final Property newProperty) {
@@ -389,27 +581,35 @@
     // the final three arguments are for debug printout purposes only
     @SuppressWarnings("unused")
     private static Object replaceMap(final Object sobj, final PropertyMap newMap, final String key, final Class<?> oldType, final Class<?> newType) {
-        if (DEBUG_FIELDS) {
+        if (DEBUG_FIELDS && shouldInstrument(key)) {
             final PropertyMap oldMap = ((ScriptObject)sobj).getMap();
             info("Type change for '" + key + "' " + oldType + "=>" + newType);
-            finest("setting map " + sobj + " from " + Debug.id(oldMap) + " to " + Debug.id(newMap) + " " + oldMap + " => " + newMap);
+            finest("setting map " + key+ " => " + sobj + " from " + Debug.id(oldMap) + " to " + Debug.id(newMap) + " " + oldMap + " => " + newMap);
         }
         ((ScriptObject)sobj).setMap(newMap);
         return sobj;
     }
 
     private MethodHandle generateSetter(final Class<?> forType, final Class<?> type) {
-        ensureObjectSetter();
         MethodHandle mh = createSetter(forType, type, primitiveSetter, objectSetter);
-        mh = debug(mh, currentType, type, "set");
+        mh = debug(mh, getCurrentType(), type, "set");
         return mh;
     }
 
+    /**
+     * Is this property of the undefined type?
+     * @return true if undefined
+     */
+    protected final boolean isUndefined() {
+        return getCurrentType() == null;
+    }
+
+    //TODO final
     @Override
     public MethodHandle getSetter(final Class<?> type, final PropertyMap currentMap) {
-        final int i            = getAccessorTypeIndex(type);
-        final int ci           = currentType == null ? -1 : getAccessorTypeIndex(currentType);
-        final Class<?> forType = currentType == null ? type : currentType;
+        final int      i       = getAccessorTypeIndex(type);
+        final int      ci      = isUndefined() ? -1 : getAccessorTypeIndex(getCurrentType());
+        final Class<?> forType = isUndefined() ? type : getCurrentType();
 
         //if we are asking for an object setter, but are still a primitive type, we might try to box it
         MethodHandle mh;
@@ -418,84 +618,56 @@
             final Property     newProperty = getWiderProperty(type);
             final PropertyMap  newMap      = getWiderMap(currentMap, newProperty);
             final MethodHandle widerSetter = newProperty.getSetter(type, newMap);
-            final MethodHandle explodeTypeSetter = MH.filterArguments(widerSetter, 0, MH.insertArguments(REPLACE_MAP, 1, newMap, getKey(), currentType, type));
-            if (currentType != null && currentType.isPrimitive() && type == Object.class) {
-                //might try a box check on this to avoid widening field to object storage
-                mh = createGuardBoxedPrimitiveSetter(currentType, generateSetter(currentType, currentType), explodeTypeSetter);
-            } else {
-                mh = explodeTypeSetter;
-            }
+            mh = MH.filterArguments(widerSetter, 0, MH.insertArguments(REPLACE_MAP, 1, newMap, getKey(), getCurrentType(), type));
         } else {
             mh = generateSetter(forType, type);
         }
 
+        assert mh.type().returnType() == void.class;
+
         return mh;
     }
 
     @Override
-    public boolean canChangeType() {
+    public final boolean canChangeType() {
         if (OBJECT_FIELDS_ONLY) {
             return false;
         }
-        return currentType != Object.class && (isConfigurable() || isWritable());
+        return getCurrentType() != Object.class && (isConfigurable() || isWritable());
     }
 
     private boolean needsInvalidator(final int ti, final int fti) {
         return canChangeType() && ti > fti;
     }
 
-    private void invalidate(final Class<?> newType) {
-        getters = new MethodHandle[NOOF_TYPES];
-        setCurrentType(newType);
-    }
-
-    private MethodHandle getSpillGetter() {
-        final int slot = getSlot();
-        MethodHandle getter = slot < SPILL_CACHE_SIZE ? SPILL_ACCESSORS[slot * 2] : null;
-        if (getter == null) {
-            getter = MH.insertArguments(SPILL_ELEMENT_GETTER, 1, slot);
-            if (slot < SPILL_CACHE_SIZE) {
-                SPILL_ACCESSORS[slot * 2 + 0] = getter;
-            }
-        }
-        return getter;
-    }
-
-    private MethodHandle getSpillSetter() {
-        final int slot = getSlot();
-        MethodHandle setter = slot < SPILL_CACHE_SIZE ? SPILL_ACCESSORS[slot * 2 + 1] : null;
-        if (setter == null) {
-            setter = MH.insertArguments(SPILL_ELEMENT_SETTER, 1, slot);
-            if (slot < SPILL_CACHE_SIZE) {
-                SPILL_ACCESSORS[slot * 2 + 1] = setter;
-            }
-        }
-        return setter;
-    }
-
-    private static void finest(final String str) {
-        if (DEBUG_FIELDS) {
-            LOG.finest(str);
+    // for loggers below, don't bother generating potentially expensive string concat
+    // operations that will be thrown away anyway, if the level isn't finer than ior
+    // equal to info
+    private static void finest(final String... strs) {
+        if (DEBUG_FIELDS && LOG.levelFinerThanOrEqual(Level.INFO)) {
+            LOG.finest((Object[])strs);
         }
     }
 
-    private static void info(final String str) {
-        if (DEBUG_FIELDS) {
-            LOG.info(str);
+    private static void info(final String... strs) {
+        if (DEBUG_FIELDS && LOG.levelFinerThanOrEqual(Level.INFO)) {
+            LOG.info((Object[])strs);
         }
     }
 
     private MethodHandle debug(final MethodHandle mh, final Class<?> forType, final Class<?> type, final String tag) {
-        if (DEBUG_FIELDS) {
+        if (DEBUG_FIELDS && LOG.levelFinerThanOrEqual(Level.INFO) && shouldInstrument(getKey())) {
            return MethodHandleFactory.addDebugPrintout(
                LOG,
                mh,
-               tag + " '" + getKey() + "' (property="+ Debug.id(this) + ", forType=" + stripName(forType) + ", type=" + stripName(type) + ')');
+               tag + " '" + getKey() + "' (property="+ Debug.id(this) + ", slot=" + getSlot() + " " + getClass().getSimpleName() + " forType=" + stripName(forType) + ", type=" + stripName(type) + ')');
         }
         return mh;
     }
 
-    private void setCurrentType(final Class<?> currentType) {
+    @Override
+    public final void setCurrentType(final Class<?> currentType) {
+        assert currentType != boolean.class : "no boolean storage support yet - fix this";
         this.currentType = currentType;
     }
 
@@ -505,7 +677,7 @@
     }
 
     private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
-        return MH.findStatic(lookup, AccessorProperty.class, name, MH.type(rtype, types));
+        return MH.findStatic(LOOKUP, AccessorProperty.class, name, MH.type(rtype, types));
     }
 
 }
--- a/nashorn/src/jdk/nashorn/internal/runtime/CompiledFunction.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/CompiledFunction.java	Wed Feb 26 13:17:57 2014 +0100
@@ -24,51 +24,198 @@
  */
 package jdk.nashorn.internal.runtime;
 
+import static jdk.nashorn.internal.lookup.Lookup.MH;
+import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
+import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
+
+import java.lang.invoke.CallSite;
 import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
-
+import java.lang.invoke.MutableCallSite;
+import java.lang.invoke.SwitchPoint;
+import java.util.Map;
+import java.util.TreeMap;
+import jdk.internal.dynalink.support.CatchExceptionCombinator;
+import jdk.nashorn.internal.codegen.types.ArrayType;
 import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.runtime.linker.Bootstrap;
+import jdk.nashorn.internal.runtime.options.Options;
 
 /**
  * An version of a JavaScript function, native or JavaScript.
  * Supports lazily generating a constructor version of the invocation.
  */
-final class CompiledFunction implements Comparable<CompiledFunction> {
-
-    /** The method type may be more specific than the invoker, if. e.g.
-     *  the invoker is guarded, and a guard with a generic object only
-     *  fallback, while the target is more specific, we still need the
-     *  more specific type for sorting */
-    private final MethodType   type;
-    private final MethodHandle invoker;
-    private MethodHandle constructor;
-
-    CompiledFunction(final MethodType type, final MethodHandle invoker) {
-        this(type, invoker, null);
+final class CompiledFunction {
+    private static final boolean USE_FAST_REWRITE = Options.getBooleanProperty("nashorn.fastrewrite");
+    static {
+        if(USE_FAST_REWRITE && CompiledFunction.class.getClassLoader() != null) {
+            throw new AssertionError("-Dnashorn.fastrewrite can only be used with Nashorn in boot class path! (Try -Xbootclasspath/p:dist/nashorn.jar )");
+        }
     }
 
-    CompiledFunction(final MethodType type, final MethodHandle invoker, final MethodHandle constructor) {
-        assert type != null;
-        this.type        = type;
-        this.invoker     = invoker;
+    private static final MethodHandle NEWFILTER = findOwnMH("newFilter", Object.class, Object.class, Object.class);
+    private static final MethodHandle RELINK_COMPOSABLE_INVOKER = findOwnMH("relinkComposableInvoker", void.class, CallSite.class, CompiledFunction.class, boolean.class);
+    private static final MethodHandle HANDLE_REWRITE_EXCEPTION = findOwnMH("handleRewriteException", MethodHandle.class, CompiledFunction.class, OptimismInfo.class, RewriteException.class);
+    private static final MethodHandle RESTOF_INVOKER = MethodHandles.exactInvoker(MethodType.methodType(Object.class, RewriteException.class));
+
+    private static final DebugLogger LOG = new DebugLogger("recompile", "nashorn.codegen.debug");
+
+    /**
+     * The method type may be more specific than the invoker, if. e.g.
+     * the invoker is guarded, and a guard with a generic object only
+     * fallback, while the target is more specific, we still need the
+     * more specific type for sorting
+     */
+    private MethodHandle invoker;
+    private MethodHandle constructor;
+    private OptimismInfo optimismInfo;
+
+    CompiledFunction(final MethodHandle invoker) {
+        this.invoker = invoker;
+    }
+
+    static CompiledFunction createBuiltInConstructor(final MethodHandle invoker) {
+        return new CompiledFunction(MH.insertArguments(invoker, 0, false),
+                createConstructorFromInvoker(MH.insertArguments(invoker, 0, true)));
+    }
+
+    CompiledFunction(final MethodHandle invoker, final MethodHandle constructor) {
+        this(invoker);
         this.constructor = constructor;
     }
 
+    CompiledFunction(final MethodHandle invoker, final RecompilableScriptFunctionData functionData, final boolean isOptimistic) {
+        this(invoker);
+        if(isOptimistic) {
+            optimismInfo = new OptimismInfo(functionData);
+        } else {
+            optimismInfo = null;
+        }
+    }
+
     @Override
     public String toString() {
-        return "<callSiteType= " + type + " invoker=" + invoker + " ctor=" + constructor + ">";
+        return "<callSiteType=" + invoker.type() + " invoker=" + invoker + " ctor=" + constructor + " weight=" + weight() + ">";
+    }
+
+    boolean needsCallee() {
+        return ScriptFunctionData.needsCallee(invoker);
     }
 
-    MethodHandle getInvoker() {
-        return invoker;
+    /**
+     * Returns an invoker method handle for this function. Note that the handle is safely composable in
+     * the sense that you can compose it with other handles using any combinators even if you can't affect call site
+     * invalidation. If this compiled function is non-optimistic, then it returns the same value as
+     * {@link #getInvoker()}. However, if the function is optimistic, then this handle will incur an overhead as it will
+     * add an intermediate internal call site that can relink itself when the function needs to regenerate its code to
+     * always point at the latest generated code version.
+     * @return a guaranteed composable invoker method handle for this function.
+     */
+    MethodHandle createComposableInvoker() {
+        return createComposableInvoker(false);
     }
 
+    /**
+     * Returns an invoker method handle for this function when invoked as a constructor. Note that the handle should be
+     * considered non-composable in the sense that you can only compose it with other handles using any combinators if
+     * you can ensure that the composition is guarded by {@link #getOptimisticAssumptionsSwitchPoint()} if it's
+     * non-null, and that you can relink the call site it is set into as a target if the switch point is invalidated. In
+     * all other cases, use {@link #createComposableConstructor()}.
+     * @return a direct constructor method handle for this function.
+     */
     MethodHandle getConstructor() {
+        if(constructor == null) {
+            constructor = createConstructorFromInvoker(createInvokerForPessimisticCaller());
+        }
+
         return constructor;
     }
 
-    void setConstructor(final MethodHandle constructor) {
-        this.constructor = constructor;
+    /**
+     * Creates a version of the invoker intended for a pessimistic caller (return type is Object, no caller optimistic
+     * program point available).
+     * @return a version of the invoker intended for a pessimistic caller.
+     */
+    private MethodHandle createInvokerForPessimisticCaller() {
+        return createInvoker(Object.class, INVALID_PROGRAM_POINT);
+    }
+
+    /**
+     * Compose a constructor from an invoker.
+     *
+     * @param invoker         invoker
+     * @param needsCallee  do we need to pass a callee
+     *
+     * @return the composed constructor
+     */
+    private static MethodHandle createConstructorFromInvoker(final MethodHandle invoker) {
+        final boolean needsCallee = ScriptFunctionData.needsCallee(invoker);
+        // If it was (callee, this, args...), permute it to (this, callee, args...). We're doing this because having
+        // "this" in the first argument position is what allows the elegant folded composition of
+        // (newFilter x constructor x allocator) further down below in the code. Also, ensure the composite constructor
+        // always returns Object.
+        final MethodHandle swapped = needsCallee ? swapCalleeAndThis(invoker) : invoker;
+
+        final MethodHandle returnsObject = MH.asType(swapped, swapped.type().changeReturnType(Object.class));
+
+        final MethodType ctorType = returnsObject.type();
+
+        // Construct a dropping type list for NEWFILTER, but don't include constructor "this" into it, so it's actually
+        // captured as "allocation" parameter of NEWFILTER after we fold the constructor into it.
+        // (this, [callee, ]args...) => ([callee, ]args...)
+        final Class<?>[] ctorArgs = ctorType.dropParameterTypes(0, 1).parameterArray();
+
+        // Fold constructor into newFilter that replaces the return value from the constructor with the originally
+        // allocated value when the originally allocated value is a JS primitive (String, Boolean, Number).
+        // (result, this, [callee, ]args...) x (this, [callee, ]args...) => (this, [callee, ]args...)
+        final MethodHandle filtered = MH.foldArguments(MH.dropArguments(NEWFILTER, 2, ctorArgs), returnsObject);
+
+        // allocate() takes a ScriptFunction and returns a newly allocated ScriptObject...
+        if (needsCallee) {
+            // ...we either fold it into the previous composition, if we need both the ScriptFunction callee object and
+            // the newly allocated object in the arguments, so (this, callee, args...) x (callee) => (callee, args...),
+            // or...
+            return MH.foldArguments(filtered, ScriptFunction.ALLOCATE);
+        }
+
+        // ...replace the ScriptFunction argument with the newly allocated object, if it doesn't need the callee
+        // (this, args...) filter (callee) => (callee, args...)
+        return MH.filterArguments(filtered, 0, ScriptFunction.ALLOCATE);
+    }
+
+    /**
+     * Permutes the parameters in the method handle from {@code (callee, this, ...)} to {@code (this, callee, ...)}.
+     * Used when creating a constructor handle.
+     * @param mh a method handle with order of arguments {@code (callee, this, ...)}
+     * @return a method handle with order of arguments {@code (this, callee, ...)}
+     */
+    private static MethodHandle swapCalleeAndThis(final MethodHandle mh) {
+        final MethodType type = mh.type();
+        assert type.parameterType(0) == ScriptFunction.class : type;
+        assert type.parameterType(1) == Object.class : type;
+        final MethodType newType = type.changeParameterType(0, Object.class).changeParameterType(1, ScriptFunction.class);
+        final int[] reorder = new int[type.parameterCount()];
+        reorder[0] = 1;
+        assert reorder[1] == 0;
+        for (int i = 2; i < reorder.length; ++i) {
+            reorder[i] = i;
+        }
+        return MethodHandles.permuteArguments(mh, newType, reorder);
+    }
+
+    /**
+     * Returns an invoker method handle for this function when invoked as a constructor. Note that the handle is safely
+     * composable in the sense that you can compose it with other handles using any combinators even if you can't affect
+     * call site invalidation. If this compiled function is non-optimistic, then it returns the same value as
+     * {@link #getConstructor()}. However, if the function is optimistic, then this handle will incur an overhead as it
+     * will add an intermediate internal call site that can relink itself when the function needs to regenerate its code
+     * to always point at the latest generated code version.
+     * @return a guaranteed composable constructor method handle for this function.
+     */
+    MethodHandle createComposableConstructor() {
+        return createComposableInvoker(true);
     }
 
     boolean hasConstructor() {
@@ -76,45 +223,10 @@
     }
 
     MethodType type() {
-        return type;
-    }
-
-    @Override
-    public int compareTo(final CompiledFunction o) {
-        return compareMethodTypes(type(), o.type());
+        return invoker.type();
     }
 
-    private static int compareMethodTypes(final MethodType ownType, final MethodType otherType) {
-        // Comparable interface demands that compareTo() should only return 0 if objects are equal.
-        // Failing to meet this requirement causes same weight functions to replace each other in TreeSet,
-        // so we go some lengths to come up with an ordering between same weight functions,
-        // first falling back to parameter count and then to hash code.
-        if (ownType.equals(otherType)) {
-            return 0;
-        }
-
-        final int diff = weight(ownType) - weight(otherType);
-        if (diff != 0) {
-            return diff;
-        }
-        if (ownType.parameterCount() != otherType.parameterCount()) {
-            return ownType.parameterCount() - otherType.parameterCount();
-        }
-        // We're just interested in not returning 0 here, not correct ordering
-        return ownType.hashCode() - otherType.hashCode();
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        return obj instanceof CompiledFunction && type().equals(((CompiledFunction)obj).type());
-    }
-
-    @Override
-    public int hashCode() {
-        return type().hashCode();
-    }
-
-    private int weight() {
+    int weight() {
         return weight(type());
     }
 
@@ -124,10 +236,14 @@
         }
 
         int weight = Type.typeFor(type.returnType()).getWeight();
-        for (final Class<?> paramType : type.parameterArray()) {
+        for (int i = 0 ; i < type.parameterCount() ; i++) {
+            final Class<?> paramType = type.parameterType(i);
             final int pweight = Type.typeFor(paramType).getWeight() * 2; //params are more important than call types as return values are always specialized
             weight += pweight;
         }
+
+        weight += type.parameterCount(); //more params outweigh few parameters
+
         return weight;
     }
 
@@ -136,64 +252,396 @@
         return type.parameterType(type.parameterCount() - 1) == Object[].class;
     }
 
-    boolean moreGenericThan(final CompiledFunction o) {
-        return weight() > o.weight();
+    static boolean moreGenericThan(final MethodType mt0, final MethodType mt1) {
+        return weight(mt0) > weight(mt1);
     }
 
-    boolean moreGenericThan(final MethodType mt) {
-        return weight() > weight(mt);
+    boolean betterThanFinal(final CompiledFunction other, final MethodType callSiteMethodType) {
+        // Prefer anything over nothing, as we can't compile new versions.
+        if (other == null) {
+            return true;
+        }
+        return betterThanFinal(type(), other.type(), callSiteMethodType);
     }
 
-    /**
-     * Check whether a given method descriptor is compatible with this invocation.
-     * It is compatible if the types are narrower than the invocation type so that
-     * a semantically equivalent linkage can be performed.
-     *
-     * @param mt type to check against
-     * @return true if types are compatible
-     */
-    boolean typeCompatible(final MethodType mt) {
-        final int wantedParamCount   = mt.parameterCount();
-        final int existingParamCount = type.parameterCount();
+    static boolean betterThanFinal(final MethodType thisMethodType, MethodType otherMethodType, final MethodType callSiteMethodType) {
+        final int thisParamCount = getParamCount(thisMethodType);
+        final int otherParamCount = getParamCount(otherMethodType);
+        final int callSiteRawParamCount = getParamCount(callSiteMethodType);
+        final boolean csVarArg = callSiteRawParamCount == Integer.MAX_VALUE;
+        // Subtract 1 for callee for non-vararg call sites
+        final int callSiteParamCount = csVarArg ? callSiteRawParamCount : callSiteRawParamCount - 1;
+
+        // Prefer the function that discards less parameters
+        final int thisDiscardsParams = Math.max(callSiteParamCount - thisParamCount, 0);
+        final int otherDiscardsParams = Math.max(callSiteParamCount - otherParamCount, 0);
+        if(thisDiscardsParams < otherDiscardsParams) {
+            return true;
+        }
+        if(thisDiscardsParams > otherDiscardsParams) {
+            return false;
+        }
+
+        final boolean thisVarArg = thisParamCount == Integer.MAX_VALUE;
+        final boolean otherVarArg = otherParamCount == Integer.MAX_VALUE;
+        if(!(thisVarArg && otherVarArg && csVarArg)) {
+            // At least one of them isn't vararg
+            final Type[] thisType = toTypeWithoutCallee(thisMethodType, 0); // Never has callee
+            final Type[] otherType = toTypeWithoutCallee(otherMethodType, 0); // Never has callee
+            final Type[] callSiteType = toTypeWithoutCallee(callSiteMethodType, 1); // Always has callee
 
-        //if we are not examining a varargs type, the number of parameters must be the same
-        if (wantedParamCount != existingParamCount && !isVarArgsType(mt)) {
+            int narrowWeightDelta = 0;
+            int widenWeightDelta = 0;
+            final int minParamsCount = Math.min(Math.min(thisParamCount, otherParamCount), callSiteParamCount);
+            for(int i = 0; i < minParamsCount; ++i) {
+                final int callSiteParamWeight = getParamType(i, callSiteType, csVarArg).getWeight();
+                // Delta is negative for narrowing, positive for widening
+                final int thisParamWeightDelta = getParamType(i, thisType, thisVarArg).getWeight() - callSiteParamWeight;
+                final int otherParamWeightDelta = getParamType(i, otherType, otherVarArg).getWeight() - callSiteParamWeight;
+                // Only count absolute values of narrowings
+                narrowWeightDelta += Math.max(-thisParamWeightDelta, 0) - Math.max(-otherParamWeightDelta, 0);
+                // Only count absolute values of widenings
+                widenWeightDelta += Math.max(thisParamWeightDelta, 0) - Math.max(otherParamWeightDelta, 0);
+            }
+
+            // If both functions accept more arguments than what is passed at the call site, account for ability
+            // to receive Undefined un-narrowed in the remaining arguments.
+            if(!thisVarArg) {
+                for(int i = callSiteParamCount; i < thisParamCount; ++i) {
+                    narrowWeightDelta += Math.max(Type.OBJECT.getWeight() - thisType[i].getWeight(), 0);
+                }
+            }
+            if(!otherVarArg) {
+                for(int i = callSiteParamCount; i < otherParamCount; ++i) {
+                    narrowWeightDelta -= Math.max(Type.OBJECT.getWeight() - otherType[i].getWeight(), 0);
+                }
+            }
+
+            // Prefer function that narrows less
+            if(narrowWeightDelta < 0) {
+                return true;
+            }
+            if(narrowWeightDelta > 0) {
+                return false;
+            }
+
+            // Prefer function that widens less
+            if(widenWeightDelta < 0) {
+                return true;
+            }
+            if(widenWeightDelta > 0) {
+                return false;
+            }
+        }
+
+        // Prefer the function that exactly matches the arity of the call site.
+        if(thisParamCount == callSiteParamCount && otherParamCount != callSiteParamCount) {
+            return true;
+        }
+        if(thisParamCount != callSiteParamCount && otherParamCount == callSiteParamCount) {
             return false;
         }
 
-        //we only go as far as the shortest array. the only chance to make this work if
-        //parameters lengths do not match is if our type ends with a varargs argument.
-        //then every trailing parameter in the given callsite can be folded into it, making
-        //us compatible (albeit slower than a direct specialization)
-        final int lastParamIndex = Math.min(wantedParamCount, existingParamCount);
-        for (int i = 0; i < lastParamIndex; i++) {
-            final Type w = Type.typeFor(mt.parameterType(i));
-            final Type e = Type.typeFor(type.parameterType(i));
+        // Otherwise, neither function matches arity exactly. We also know that at this point, they both can receive
+        // more arguments than call site, otherwise we would've already chosen the one that discards less parameters.
+        // Note that variable arity methods are preferred, as they actually match the call site arity better, since they
+        // really have arbitrary arity.
+        if(thisVarArg) {
+            if(!otherVarArg) {
+                return true; //
+            }
+        } else if(otherVarArg) {
+            return false;
+        }
+
+        // Neither is variable arity; chose the one that has less extra parameters.
+        final int fnParamDelta = thisParamCount - otherParamCount;
+        if(fnParamDelta < 0) {
+            return true;
+        }
+        if(fnParamDelta > 0) {
+            return false;
+        }
+
+        final int callSiteRetWeight = Type.typeFor(callSiteMethodType.returnType()).getWeight();
+        // Delta is negative for narrower return type, positive for wider return type
+        final int thisRetWeightDelta = Type.typeFor(thisMethodType.returnType()).getWeight() - callSiteRetWeight;
+        final int otherRetWeightDelta = Type.typeFor(otherMethodType.returnType()).getWeight() - callSiteRetWeight;
+
+        // Prefer function that returns a less wide return type
+        final int widenRetDelta = Math.max(thisRetWeightDelta, 0) - Math.max(otherRetWeightDelta, 0);
+        if(widenRetDelta < 0) {
+            return true;
+        }
+        if(widenRetDelta > 0) {
+            return false;
+        }
+
+        // Prefer function that returns a less narrow return type
+        final int narrowRetDelta = Math.max(-thisRetWeightDelta, 0) - Math.max(-otherRetWeightDelta, 0);
+        if(narrowRetDelta < 0) {
+            return true;
+        }
+        if(narrowRetDelta > 0) {
+            return false;
+        }
 
-            //don't specialize on booleans, we have the "true" vs int 1 ambiguity in resolution
-            //we also currently don't support boolean as a javascript function callsite type.
-            //it will always box.
-            if (w.isBoolean()) {
+        throw new AssertionError(thisMethodType + " identically applicable to " + otherMethodType + " for " + callSiteMethodType); // Signatures are identical
+    }
+
+    private static Type[] toTypeWithoutCallee(final MethodType type, int thisIndex) {
+        final int paramCount = type.parameterCount();
+        final Type[] t = new Type[paramCount - thisIndex];
+        for(int i = thisIndex; i < paramCount; ++i) {
+            t[i - thisIndex] = Type.typeFor(type.parameterType(i));
+        }
+        return t;
+    }
+
+    private static Type getParamType(int i, Type[] paramTypes, boolean isVarArg) {
+        final int fixParamCount = paramTypes.length - (isVarArg ? 1 : 0);
+        if(i < fixParamCount) {
+            return paramTypes[i];
+        }
+        assert isVarArg;
+        return ((ArrayType)(paramTypes[paramTypes.length - 1])).getElementType();
+    }
+
+    boolean matchesCallSite(final MethodType callSiteType) {
+        if(!ScriptEnvironment.globalOptimistic()) {
+            // Without optimistic recompilation, always choose the first eagerly compiled version.
+            return true;
+        }
+
+        final MethodType type = type();
+        final int fnParamCount = getParamCount(type);
+        final boolean isVarArg = fnParamCount == Integer.MAX_VALUE;
+        if(isVarArg) {
+            return true;
+        }
+
+        final int csParamCount = getParamCount(callSiteType);
+        final boolean csIsVarArg = csParamCount == Integer.MAX_VALUE;
+        final int thisThisIndex = needsCallee() ? 1 : 0; // Index of "this" parameter in this function's type
+
+        int fnParamCountNoCallee = fnParamCount - thisThisIndex;
+        int minParams = Math.min(csParamCount - 1, fnParamCountNoCallee); // callSiteType always has callee, so subtract 1
+        // We must match all incoming parameters, except "this". Starting from 1 to skip "this".
+        for(int i = 1; i < minParams; ++i) {
+            final Type fnType = Type.typeFor(type.parameterType(i + thisThisIndex));
+            final Type csType = csIsVarArg ? Type.OBJECT : Type.typeFor(callSiteType.parameterType(i + 1));
+            if(!fnType.isEquivalentTo(csType)) {
                 return false;
             }
+        }
 
-            //This callsite type has a vararg here. it will swallow all remaining args.
-            //for consistency, check that it's the last argument
-            if (e.isArray()) {
-                return true;
-            }
-
-            //Our arguments must be at least as wide as the wanted one, if not wider
-            if (Type.widest(w, e) != e) {
-                //e.g. this invocation takes double and callsite says "object". reject. won't fit
-                //but if invocation takes a double and callsite says "int" or "long" or "double", that's fine
+        // Must match any undefined parameters to Object type.
+        for(int i = minParams; i < fnParamCountNoCallee; ++i) {
+            if(!Type.typeFor(type.parameterType(i + thisThisIndex)).isEquivalentTo(Type.OBJECT)) {
                 return false;
             }
         }
 
-        return true; // anything goes for return type, take the convenient one and it will be upcasted thru dynalink magic.
+        return true;
+    }
+
+    private static int getParamCount(final MethodType type) {
+        final int paramCount = type.parameterCount();
+        return type.parameterType(paramCount - 1).isArray() ? Integer.MAX_VALUE : paramCount;
+    }
+
+    /**
+     * Returns the switch point embodying the optimistic assumptions in this compiled function. It should be used to
+     * guard any linking to the function's invoker or constructor.
+     * @return the switch point embodying the optimistic assumptions in this compiled function. Null is returned if the
+     * function has no optimistic assumptions.
+     */
+    SwitchPoint getOptimisticAssumptionsSwitchPoint() {
+        return isOptimistic() ? optimismInfo.optimisticAssumptions : null;
+    }
+
+    boolean isOptimistic() {
+        return optimismInfo != null;
+    }
+
+    private MethodHandle createComposableInvoker(boolean isConstructor) {
+        final MethodHandle handle = getInvokerOrConstructor(isConstructor);
+
+        // If compiled function is not optimistic, it can't ever change its invoker/constructor, so just return them
+        // directly.
+        if(!isOptimistic()) {
+            return handle;
+        }
+
+        // Otherwise, we need a new level of indirection; need to introduce a mutable call site that can relink itslef
+        // to the compiled function's changed target whenever the optimistic assumptions are invalidated.
+        final CallSite cs = new MutableCallSite(handle.type());
+        relinkComposableInvoker(cs, this, isConstructor);
+        return cs.dynamicInvoker();
+    }
+    private static void relinkComposableInvoker(final CallSite cs, final CompiledFunction inv, final boolean constructor) {
+        final MethodHandle handle = inv.getInvokerOrConstructor(constructor);
+        final SwitchPoint assumptions = inv.getOptimisticAssumptionsSwitchPoint();
+        final MethodHandle target;
+        if(assumptions == null) {
+            target = handle;
+        } else {
+            // This assertion can obviously fail in a multithreaded environment, as we can be in a situation where
+            // one thread is in the middle of a deoptimizing compilation when we hit this and thus, it has invalidated
+            // the old switch point, but hasn't created the new one yet. Note that the behavior of invalidating the old
+            // switch point before recompilation, and only creating the new one after recompilation is by design.
+            // TODO: We need to think about thread safety of CompiledFunction objects.
+            assert !assumptions.hasBeenInvalidated();
+            final MethodHandle relink = MethodHandles.insertArguments(RELINK_COMPOSABLE_INVOKER, 0, cs, inv, constructor);
+            target = assumptions.guardWithTest(handle, MethodHandles.foldArguments(cs.dynamicInvoker(), relink));
+        }
+        cs.setTarget(target);
+    }
+
+    private MethodHandle getInvokerOrConstructor(boolean selectCtor) {
+        return selectCtor ? getConstructor() : createInvokerForPessimisticCaller();
+    }
+
+    MethodHandle createInvoker(final Class<?> callSiteReturnType, final int callerProgramPoint) {
+        final boolean isOptimistic = isOptimistic();
+        MethodHandle handleRewriteException = isOptimistic ? createRewriteExceptionHandler() : null;
+
+        MethodHandle inv = invoker;
+        if(isValid(callerProgramPoint)) {
+            inv = OptimisticReturnFilters.filterOptimisticReturnValue(inv, callSiteReturnType, callerProgramPoint);
+            if(callSiteReturnType.isPrimitive() && handleRewriteException != null) {
+                // because handleRewriteException always returns Object
+                handleRewriteException = OptimisticReturnFilters.filterOptimisticReturnValue(handleRewriteException,
+                        callSiteReturnType, callerProgramPoint);
+            }
+        } else if(isOptimistic) {
+            // Required so that rewrite exception has the same return type. It'd be okay to do it even if we weren't
+            // optimistic, but it isn't necessary as the linker upstream will eventually convert the return type.
+            inv = changeReturnType(inv, callSiteReturnType);
+        }
+
+        if(isOptimistic) {
+            assert handleRewriteException != null;
+            final MethodHandle typedHandleRewriteException = changeReturnType(handleRewriteException, inv.type().returnType());
+            if(USE_FAST_REWRITE) {
+                return CatchExceptionCombinator.catchException(inv, RewriteException.class, typedHandleRewriteException);
+            }
+            return MH.catchException(inv, RewriteException.class, typedHandleRewriteException);
+        }
+        return inv;
+    }
+
+    private MethodHandle createRewriteExceptionHandler() {
+        return MH.foldArguments(RESTOF_INVOKER, MH.insertArguments(HANDLE_REWRITE_EXCEPTION, 0, this, optimismInfo));
+    }
+
+    private static MethodHandle changeReturnType(MethodHandle mh, Class<?> newReturnType) {
+        return Bootstrap.getLinkerServices().asType(mh, mh.type().changeReturnType(newReturnType));
+    }
+
+    @SuppressWarnings("unused")
+    private static MethodHandle handleRewriteException(final CompiledFunction function, final OptimismInfo oldOptimismInfo, final RewriteException re) {
+        return function.handleRewriteException(oldOptimismInfo, re);
     }
 
+    /**
+     * Handles a {@link RewriteException} raised during the execution of this function by recompiling (if needed) the
+     * function with an optimistic assumption invalidated at the program point indicated by the exception, and then
+     * executing a rest-of method to complete the execution with the deoptimized version.
+     * @param oldOptimismInfo the optimism info of this function. We must store it explicitly as a bound argument in the
+     * method handle, otherwise it would be null for handling a rewrite exception in an outer invocation of a recursive
+     * function when inner invocations of the function have completely deoptimized it.
+     * @param re the rewrite exception that was raised
+     * @return the method handle for the rest-of method, for folding composition.
+     */
+    private MethodHandle handleRewriteException(final OptimismInfo oldOptimismInfo, final RewriteException re) {
+        final MethodType type = type();
+        // Compiler needs a call site type as its input, which always has a callee parameter, so we must add it if
+        // this function doesn't have a callee parameter.
+        final MethodType callSiteType = type.parameterType(0) == ScriptFunction.class ? type : type.insertParameterTypes(0, ScriptFunction.class);
 
+        final FunctionNode fn = oldOptimismInfo.recompile(callSiteType, re);
+        LOG.info("    RewriteException ", re.getMessageShort());
+
+        // It didn't necessarily recompile, e.g. for an outer invocation of a recursive function if we already
+        // recompiled a deoptimized version for an inner invocation.
+
+        final boolean isOptimistic;
+        if (fn != null) {
+            //is recompiled
+            assert optimismInfo == oldOptimismInfo;
+            isOptimistic = fn.isOptimistic();
+            LOG.info("Recompiled '", fn.getName(), "' (", Debug.id(this), ")", isOptimistic ? " remains optimistic." : " is no longer optimistic.");
+            final MethodHandle newInvoker = oldOptimismInfo.data.lookup(fn);
+            invoker = newInvoker.asType(type.changeReturnType(newInvoker.type().returnType()));
+            constructor = null; // Will be regenerated when needed
+            // Note that we only adjust the switch point after we set the invoker/constructor. This is important.
+            if(isOptimistic) {
+                // Otherwise, set a new switch point.
+                oldOptimismInfo.newOptimisticAssumptions();
+            } else {
+                // If we got to a point where we no longer have optimistic assumptions, let the optimism info go.
+                optimismInfo = null;
+            }
+        } else {
+            isOptimistic = isOptimistic();
+            assert !isOptimistic || optimismInfo == oldOptimismInfo;
+        }
+
+        final MethodHandle restOf = changeReturnType(oldOptimismInfo.compileRestOfMethod(callSiteType, re), Object.class);
+        // If rest-of is itself optimistic, we must make sure that we can repeat a deoptimization if it, too hits an exception.
+        return isOptimistic ? MH.catchException(restOf, RewriteException.class, createRewriteExceptionHandler()) : restOf;
+    }
 
+    private static class OptimismInfo {
+        // TODO: this is pointing to its owning ScriptFunctionData. Re-evaluate if that's okay.
+        private final RecompilableScriptFunctionData data;
+        private final Map<Integer, Type> invalidatedProgramPoints = new TreeMap<>();
+        private SwitchPoint optimisticAssumptions;
+
+        OptimismInfo(final RecompilableScriptFunctionData data) {
+            this.data = data;
+            newOptimisticAssumptions();
+        }
+
+        private void newOptimisticAssumptions() {
+            optimisticAssumptions = new SwitchPoint();
+        }
+
+        FunctionNode recompile(final MethodType callSiteType, final RewriteException e) {
+            final Type retType = e.getReturnType();
+            final Type previousFailedType = invalidatedProgramPoints.put(e.getProgramPoint(), retType);
+            if (previousFailedType != null && !previousFailedType.narrowerThan(retType)) {
+                final StackTraceElement[] stack = e.getStackTrace();
+                final String functionId = stack.length == 0 ? data.getName() : (stack[0].getClassName() + "." + stack[0].getMethodName());
+                LOG.info("RewriteException for an already invalidated program point ", e.getProgramPoint(), " in ", functionId, ". This is okay for a recursive function invocation, but a bug otherwise.");
+                return null;
+            }
+            SwitchPoint.invalidateAll(new SwitchPoint[] { optimisticAssumptions });
+            return data.compile(callSiteType, invalidatedProgramPoints, "Deoptimizing recompilation");
+        }
+
+        MethodHandle compileRestOfMethod(final MethodType callSiteType, final RewriteException e) {
+            final int[] prevEntryPoints = e.getPreviousContinuationEntryPoints();
+            final int[] entryPoints;
+            if(prevEntryPoints == null) {
+                entryPoints = new int[1];
+            } else {
+                final int l = prevEntryPoints.length;
+                entryPoints = new int[l + 1];
+                System.arraycopy(prevEntryPoints, 0, entryPoints, 1, l);
+            }
+            entryPoints[0] = e.getProgramPoint();
+            return data.compileRestOfMethod(callSiteType, invalidatedProgramPoints, entryPoints);
+        }
+    }
+
+    @SuppressWarnings("unused")
+    private static Object newFilter(final Object result, final Object allocation) {
+        return (result instanceof ScriptObject || !JSType.isPrimitive(result))? result : allocation;
+    }
+
+    private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
+        return MH.findStatic(MethodHandles.lookup(), CompiledFunction.class, name, MH.type(rtype, types));
+    }
 }
--- a/nashorn/src/jdk/nashorn/internal/runtime/CompiledFunctions.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/CompiledFunctions.java	Wed Feb 26 13:17:57 2014 +0100
@@ -24,91 +24,115 @@
  */
 package jdk.nashorn.internal.runtime;
 
-import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodType;
-import java.util.Iterator;
-import java.util.TreeSet;
+import java.util.LinkedList;
 
 /**
  * This is a list of code versions of a function.
  * The list is sorted in ascending order of generic descriptors
  */
-@SuppressWarnings("serial")
-final class CompiledFunctions extends TreeSet<CompiledFunction> {
+final class CompiledFunctions {
 
-    private CompiledFunction generic;
+    private final String name;
+    final LinkedList<CompiledFunction> functions = new LinkedList<>();
 
-    CompiledFunction best(final MethodType type) {
-        final Iterator<CompiledFunction> iter = iterator();
-        while (iter.hasNext()) {
-            final CompiledFunction next = iter.next();
-            if (next.typeCompatible(type)) {
-                return next;
-            }
-        }
-        return generic();
+    CompiledFunctions(final String name) {
+        this.name = name;
+    }
+
+    void add(CompiledFunction f) {
+        functions.add(f);
     }
 
-    boolean needsCallee() {
-        return ScriptFunctionData.needsCallee(mostGeneric().getInvoker());
+    void addAll(CompiledFunctions fs) {
+        functions.addAll(fs.functions);
     }
 
-    CompiledFunction mostGeneric() {
-        return last();
+    boolean isEmpty() {
+        return functions.isEmpty();
     }
 
-    CompiledFunction generic() {
-        CompiledFunction gen = this.generic;
-        if (gen == null) {
-            gen = this.generic = makeGeneric(mostGeneric());
-        }
-        return gen;
+    int size() {
+        return functions.size();
     }
 
-    private static CompiledFunction makeGeneric(final CompiledFunction func) {
-        final MethodHandle invoker = composeGenericMethod(func.getInvoker());
-        final MethodHandle constructor = func.hasConstructor() ? composeGenericMethod(func.getConstructor()) : null;
-        return new CompiledFunction(invoker.type(), invoker, constructor);
+    @Override
+    public String toString() {
+        return '\'' + name + "' code=" + functions;
     }
 
     /**
-     * Takes a method handle, and returns a potentially different method handle that can be used in
-     * {@code ScriptFunction#invoke(Object, Object...)} or {code ScriptFunction#construct(Object, Object...)}.
-     * The returned method handle will be sure to return {@code Object}, and will have all its parameters turned into
-     * {@code Object} as well, except for the following ones:
-     * <ul>
-     *   <li>a last parameter of type {@code Object[]} which is used for vararg functions,</li>
-     *   <li>the first argument, which is forced to be {@link ScriptFunction}, in case the function receives itself
-     *   (callee) as an argument.</li>
-     * </ul>
-     *
-     * @param mh the original method handle
-     *
-     * @return the new handle, conforming to the rules above.
+     * Returns the compiled function best matching the requested call site method type
+     * @param callSiteType
+     * @param recompilable
+     * @param hasThis
+     * @return
      */
-    private static MethodHandle composeGenericMethod(final MethodHandle mh) {
-        final MethodType type = mh.type();
-        final boolean isVarArg = ScriptFunctionData.isVarArg(mh);
-        final int paramCount = isVarArg ? type.parameterCount() - 1 : type.parameterCount();
+    CompiledFunction best(final MethodType callSiteType, final boolean recompilable) {
+        assert callSiteType.parameterCount() >= 2 : callSiteType; // Must have at least (callee, this)
+        assert callSiteType.parameterType(0).isAssignableFrom(ScriptFunction.class) : callSiteType; // Callee must be assignable from script function
 
-        MethodType newType = MethodType.genericMethodType(paramCount, isVarArg);
+        if (recompilable) {
+            for (final CompiledFunction candidate: functions) {
+                if(candidate.matchesCallSite(callSiteType)) {
+                    return candidate;
+                }
+            }
+            return null;
+        }
 
-        if (ScriptFunctionData.needsCallee(mh)) {
-            newType = newType.changeParameterType(0, ScriptFunction.class);
+        CompiledFunction best = null;
+        for(final CompiledFunction candidate: functions) {
+            if(candidate.betterThanFinal(best, callSiteType)) {
+                best = candidate;
+            }
         }
-        return type.equals(newType) ? mh : mh.asType(newType);
+
+        return best;
     }
 
     /**
-     * Is the given type even more specific than this entire list? That means
-     * we have an opportunity for more specific versions of the method
-     * through lazy code generation
-     *
-     * @param type type to check against
-     * @return true if the given type is more specific than all invocations available
+     * Returns true if functions managed by this {@code CompiledFunctions} require a callee. This method is only safe to
+     * be invoked for a {@code CompiledFunctions} that is not empty. As such, it should only be used from
+     * {@link FinalScriptFunctionData} and not from {@link RecompilableScriptFunctionData}.
+     * @return true if the functions need a callee, false otherwise.
      */
-    boolean isLessSpecificThan(final MethodType type) {
-        return best(type).moreGenericThan(type);
+    boolean needsCallee() {
+        boolean needsCallee = functions.getFirst().needsCallee();
+        assert allNeedCallee(needsCallee);
+        return needsCallee;
+    }
+
+    private boolean allNeedCallee(final boolean needCallee) {
+        for (final CompiledFunction inv : functions) {
+            if(inv.needsCallee() != needCallee) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * If this CompiledFunctions object belongs to a {@code FinalScriptFunctionData}, get a method type for a generic
+     * invoker. It will either be a vararg type, if any of the contained functions is vararg, or a generic type of the
+     * arity of the largest arity of all functions.
+     * @return the method type for the generic invoker
+     */
+    MethodType getFinalGenericType() {
+        int max = 0;
+        for(final CompiledFunction fn: functions) {
+            final MethodType t = fn.type();
+            if(ScriptFunctionData.isVarArg(t)) {
+                // 2 for (callee, this, args[])
+                return MethodType.genericMethodType(2, true);
+            }
+            final int paramCount = t.parameterCount() - (ScriptFunctionData.needsCallee(t) ? 1 : 0);
+            if(paramCount > max) {
+                max = paramCount;
+            }
+        }
+        // +1 for callee
+        return MethodType.genericMethodType(max + 1);
     }
 
 }
--- a/nashorn/src/jdk/nashorn/internal/runtime/Context.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/Context.java	Wed Feb 26 13:17:57 2014 +0100
@@ -25,17 +25,15 @@
 
 package jdk.nashorn.internal.runtime;
 
-import static jdk.nashorn.internal.codegen.CompilerConstants.RUN_SCRIPT;
+import static jdk.nashorn.internal.codegen.CompilerConstants.CREATE_PROGRAM_FUNCTION;
 import static jdk.nashorn.internal.codegen.CompilerConstants.STRICT_MODE;
-import static jdk.nashorn.internal.lookup.Lookup.MH;
 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
 
 import java.io.File;
 import java.io.IOException;
 import java.io.PrintWriter;
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Modifier;
 import java.net.MalformedURLException;
 import java.net.URL;
@@ -51,7 +49,10 @@
 import jdk.internal.org.objectweb.asm.ClassReader;
 import jdk.internal.org.objectweb.asm.util.CheckClassAdapter;
 import jdk.nashorn.api.scripting.ScriptObjectMirror;
+import jdk.nashorn.internal.codegen.CompilationEnvironment;
+import jdk.nashorn.internal.codegen.CompilationEnvironment.CompilationPhases;
 import jdk.nashorn.internal.codegen.Compiler;
+import jdk.nashorn.internal.codegen.CompilerConstants;
 import jdk.nashorn.internal.codegen.ObjectClassGenerator;
 import jdk.nashorn.internal.ir.FunctionNode;
 import jdk.nashorn.internal.ir.debug.ASTWriter;
@@ -338,7 +339,7 @@
         // if user passed -classpath option, make a class loader with that and set it as
         // thread context class loader so that script can access classes from that path.
         final String classPath = options.getString("classpath");
-        if (! env._compile_only && classPath != null && !classPath.isEmpty()) {
+        if (!env._compile_only && classPath != null && !classPath.isEmpty()) {
             // make sure that caller can create a class loader.
             if (sm != null) {
                 sm.checkPermission(new RuntimePermission("createClassLoader"));
@@ -426,7 +427,6 @@
         final Source  source     = new Source(file, string);
         final boolean directEval = location != UNDEFINED; // is this direct 'eval' call or indirectly invoked eval?
         final ScriptObject global = Context.getGlobalTrusted();
-
         ScriptObject scope = initialScope;
 
         // ECMA section 10.1.1 point 2 says eval code is strict if it begins
@@ -468,7 +468,7 @@
             scope = strictEvalScope;
         }
 
-        ScriptFunction func = getRunScriptFunction(clazz, scope);
+        ScriptFunction func = getProgramFunction(clazz, scope);
         Object evalThis;
         if (directEval) {
             evalThis = (callThis instanceof ScriptObject || strictFlag) ? callThis : global;
@@ -626,11 +626,12 @@
      *
      * @throws ClassNotFoundException if structure class cannot be resolved
      */
-    public static Class<?> forStructureClass(final String fullName) throws ClassNotFoundException {
+    @SuppressWarnings("unchecked")
+    public static Class<? extends ScriptObject> forStructureClass(final String fullName) throws ClassNotFoundException {
         if (System.getSecurityManager() != null && !StructureLoader.isStructureClass(fullName)) {
             throw new ClassNotFoundException(fullName);
         }
-        return Class.forName(fullName, true, sharedLoader);
+        return (Class<? extends ScriptObject>)Class.forName(fullName, true, sharedLoader);
     }
 
     /**
@@ -802,7 +803,7 @@
      * @return the initialized global scope object.
      */
     public ScriptObject initGlobal(final ScriptObject global) {
-        if (! (global instanceof GlobalObject)) {
+        if (!(global instanceof GlobalObject)) {
             throw new IllegalArgumentException("not a global object!");
         }
 
@@ -889,36 +890,21 @@
         return ScriptRuntime.apply(script, thiz);
     }
 
-    private static ScriptFunction getRunScriptFunction(final Class<?> script, final ScriptObject scope) {
+    private static ScriptFunction getProgramFunction(final Class<?> script, final ScriptObject scope) {
         if (script == null) {
             return null;
         }
 
-        // Get run method - the entry point to the script
-        final MethodHandle runMethodHandle =
-                MH.findStatic(
-                    MethodHandles.lookup(),
-                    script,
-                    RUN_SCRIPT.symbolName(),
-                    MH.type(
-                        Object.class,
-                        ScriptFunction.class,
-                        Object.class));
-
-        boolean strict;
-
         try {
-            strict = script.getField(STRICT_MODE.symbolName()).getBoolean(null);
-        } catch (final NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
-            strict = false;
+            return (ScriptFunction)script.getMethod(CREATE_PROGRAM_FUNCTION.symbolName(), ScriptObject.class).invoke(null, scope);
+        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException
+                | SecurityException e) {
+            throw new RuntimeException("Failed to create a program function for " + script.getName(), e);
         }
-
-        // Package as a JavaScript function and pass function back to shell.
-        return ((GlobalObject)Context.getGlobalTrusted()).newScriptFunction(RUN_SCRIPT.symbolName(), runMethodHandle, scope, strict);
     }
 
     private ScriptFunction compileScript(final Source source, final ScriptObject scope, final ErrorManager errMan) {
-        return getRunScriptFunction(compile(source, errMan, this._strict), scope);
+        return getProgramFunction(compile(source, errMan, this._strict), scope);
     }
 
     private synchronized Class<?> compile(final Source source, final ErrorManager errMan, final boolean strict) {
@@ -959,9 +945,16 @@
         final CodeSource   cs     = new CodeSource(url, (CodeSigner[])null);
         final CodeInstaller<ScriptEnvironment> installer = new ContextCodeInstaller(this, loader, cs);
 
-        final Compiler compiler = new Compiler(installer, strict);
+        final CompilationPhases phases = CompilationEnvironment.CompilationPhases.EAGER;
+        final Compiler compiler = new Compiler(
+                new CompilationEnvironment(
+                    phases.
+                        makeOptimistic(
+                            ScriptEnvironment.globalOptimistic()),
+                    strict),
+                installer);
 
-        final FunctionNode newFunctionNode = compiler.compile(functionNode);
+        final FunctionNode newFunctionNode = compiler.compile(CompilerConstants.DEFAULT_SCRIPT_NAME.symbolName(), functionNode);
         script = compiler.install(newFunctionNode);
 
         if (global != null) {
--- a/nashorn/src/jdk/nashorn/internal/runtime/Debug.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/Debug.java	Wed Feb 26 13:17:57 2014 +0100
@@ -41,6 +41,29 @@
     }
 
     /**
+     * Return the topmost JavaScript frame in a stack trace
+     * @param e
+     * @return line describing the topmost JavaScript frame
+     */
+    public static String firstJSFrame(final Throwable e) {
+        for (final StackTraceElement ste : e.getStackTrace()) {
+            if(ECMAErrors.isScriptFrame(ste)) {
+                return ste.toString();
+            }
+        }
+        return "<native code>";
+    }
+
+    /**
+     * Return the topmost JavaScript frame from the current
+     * continuation
+     * @return line describing the topmost JavaScript frame
+     */
+    public static String firstJSFrame() {
+        return firstJSFrame(new Throwable());
+    }
+
+    /**
      * Return the system identity hashcode for an object as a human readable
      * string
      *
@@ -52,6 +75,17 @@
     }
 
     /**
+     * Same as {@link Debug#id} but returns the identity hashcode as
+     * an integer
+     *
+     * @param x object
+     * @return system identity hashcode
+     */
+    public static int intId(final Object x) {
+        return System.identityHashCode(x);
+    }
+
+    /**
      * Return a stack trace element description at a depth from where we are not
      *
      * @param depth depth
--- a/nashorn/src/jdk/nashorn/internal/runtime/DebugLogger.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/DebugLogger.java	Wed Feb 26 13:17:57 2014 +0100
@@ -129,14 +129,57 @@
     }
 
     /**
-     * Check if the logger is above of the level of detail given
+     * Check if the logger is above the level of detail given
      * @see java.util.logging.Level
      *
+     * The higher the level, the more severe the warning
+     *
+     * @param level logging level
+     * @return true if level is above the given one
+     */
+    public boolean levelCoarserThan(final Level level) {
+        return getLevel().intValue() > level.intValue();
+    }
+
+    /**
+     * Check if the logger is above or equal to the level
+     * of detail given
+     * @see java.util.logging.Level
+     *
+     * The higher the level, the more severe the warning
+     *
      * @param level logging level
      * @return true if level is above the given one
      */
-    public boolean levelAbove(final Level level) {
-        return getLevel().intValue() > level.intValue();
+    public boolean levelCoarserThanOrEqual(final Level level) {
+        return getLevel().intValue() >= level.intValue();
+    }
+
+    /**
+     * Check if the logger is below the level of detail given
+     * @see java.util.logging.Level
+     *
+     * The higher the level, the more severe the warning
+     *
+     * @param level logging level
+     * @return true if level is above the given one
+     */
+    public boolean levelFinerThan(final Level level) {
+        return getLevel().intValue() < level.intValue();
+    }
+
+    /**
+     * Check if the logger is below or equal to the level
+     * of detail given
+     * @see java.util.logging.Level
+     *
+     * The higher the level, the more severe the warning
+     *
+     * @param level logging level
+     * @return true if level is above the given one
+     */
+    public boolean levelFinerThanOrEqual(final Level level) {
+        return getLevel().intValue() <= level.intValue();
     }
 
     /**
--- a/nashorn/src/jdk/nashorn/internal/runtime/DebuggerSupport.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/DebuggerSupport.java	Wed Feb 26 13:17:57 2014 +0100
@@ -25,6 +25,8 @@
 
 package jdk.nashorn.internal.runtime;
 
+import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
+
 import java.util.HashSet;
 import java.util.Set;
 
@@ -176,7 +178,7 @@
 
         if (ScriptObject.isArray(object)) {
             sb.append('[');
-            final long length = object.getLong("length");
+            final long length = object.getLong("length", INVALID_PROGRAM_POINT);
 
             for (long i = 0; i < length; i++) {
                 if (object.has(i)) {
--- a/nashorn/src/jdk/nashorn/internal/runtime/DefaultPropertyAccess.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/DefaultPropertyAccess.java	Wed Feb 26 13:17:57 2014 +0100
@@ -34,63 +34,63 @@
 public abstract class DefaultPropertyAccess implements PropertyAccess {
 
     @Override
-    public int getInt(final Object key) {
+    public int getInt(final Object key, final int programPoint) {
         return JSType.toInt32(get(key));
     }
 
     @Override
-    public int getInt(final double key) {
-        return getInt(JSType.toObject(key));
+    public int getInt(final double key, final int programPoint) {
+        return getInt(JSType.toObject(key), programPoint);
     }
 
     @Override
-    public int getInt(final long key) {
-        return getInt(JSType.toObject(key));
+    public int getInt(final long key, final int programPoint) {
+        return getInt(JSType.toObject(key), programPoint);
     }
 
     @Override
-    public int getInt(final int key) {
-        return getInt(JSType.toObject(key));
+    public int getInt(final int key, final int programPoint) {
+        return getInt(JSType.toObject(key), programPoint);
     }
 
     @Override
-    public long getLong(final Object key) {
+    public long getLong(final Object key, final int programPoint) {
         return JSType.toLong(get(key));
     }
 
     @Override
-    public long getLong(final double key) {
-        return getLong(JSType.toObject(key));
+    public long getLong(final double key, final int programPoint) {
+        return getLong(JSType.toObject(key), programPoint);
     }
 
     @Override
-    public long getLong(final long key) {
-        return getLong(JSType.toObject(key));
+    public long getLong(final long key, final int programPoint) {
+        return getLong(JSType.toObject(key), programPoint);
     }
 
     @Override
-    public long getLong(final int key) {
-        return getLong(JSType.toObject(key));
+    public long getLong(final int key, final int programPoint) {
+        return getLong(JSType.toObject(key), programPoint);
     }
 
     @Override
-    public double getDouble(final Object key) {
+    public double getDouble(final Object key, final int programPoint) {
         return JSType.toNumber(get(key));
     }
 
     @Override
-    public double getDouble(final double key) {
-        return getDouble(JSType.toObject(key));
+    public double getDouble(final double key, final int programPoint) {
+        return getDouble(JSType.toObject(key), programPoint);
     }
 
     @Override
-    public double getDouble(final long key) {
-        return getDouble(JSType.toObject(key));
+    public double getDouble(final long key, final int programPoint) {
+        return getDouble(JSType.toObject(key), programPoint);
     }
 
     @Override
-    public double getDouble(final int key) {
-        return getDouble(JSType.toObject(key));
+    public double getDouble(final int key, final int programPoint) {
+        return getDouble(JSType.toObject(key), programPoint);
     }
 
     @Override
--- a/nashorn/src/jdk/nashorn/internal/runtime/ECMAErrors.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ECMAErrors.java	Wed Feb 26 13:17:57 2014 +0100
@@ -29,8 +29,8 @@
 import java.util.Locale;
 import java.util.ResourceBundle;
 import jdk.nashorn.api.scripting.NashornException;
+import jdk.nashorn.internal.codegen.CompilerConstants;
 import jdk.nashorn.internal.scripts.JS;
-import jdk.nashorn.internal.codegen.CompilerConstants;
 
 /**
  * Helper class to throw various standard "ECMA error" exceptions such as Error, ReferenceError, TypeError etc.
@@ -402,7 +402,7 @@
         final String className = frame.getClassName();
 
         // Look for script package in class name (into which compiler puts generated code)
-        if (className.startsWith(scriptPackage) && !frame.getMethodName().startsWith(CompilerConstants.INTERNAL_METHOD_PREFIX)) {
+        if (className.startsWith(scriptPackage) && !CompilerConstants.isInternalMethodName(frame.getMethodName())) {
             final String source = frame.getFileName();
             /*
              * Make sure that it is not some Java code that Nashorn has in that package!
--- a/nashorn/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java	Wed Feb 26 13:17:57 2014 +0100
@@ -25,9 +25,8 @@
 
 package jdk.nashorn.internal.runtime;
 
-import static jdk.nashorn.internal.lookup.Lookup.MH;
-
 import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodType;
 
 /**
  * This is a subclass that represents a script function that may not be regenerated.
@@ -45,8 +44,9 @@
      * @param isBuiltin     builtin
      * @param isConstructor constructor
      */
-    FinalScriptFunctionData(final String name, int arity, CompiledFunctions functions, final boolean isStrict, final boolean isBuiltin, final boolean isConstructor) {
-        super(name, arity, isStrict, isBuiltin, isConstructor);
+    FinalScriptFunctionData(final String name, int arity, CompiledFunctions functions, final boolean isStrict, final boolean isBuiltin, final boolean isConstructor, final boolean isVariableArity) {
+        super(name, arity, isStrict, isBuiltin, isConstructor, isVariableArity);
+        assert !functions.needsCallee();
         code.addAll(functions);
     }
 
@@ -62,7 +62,7 @@
      * @param isConstructor  constructor
      */
     FinalScriptFunctionData(final String name, final MethodHandle mh, final MethodHandle[] specs, final boolean isStrict, final boolean isBuiltin, final boolean isConstructor) {
-        super(name, arity(mh), isStrict, isBuiltin, isConstructor);
+        super(name, methodHandleArity(mh), isStrict, isBuiltin, isConstructor, isVarArg(mh));
 
         addInvoker(mh);
         if (specs != null) {
@@ -72,22 +72,45 @@
         }
     }
 
+    @Override
+    boolean isRecompilable() {
+        return false;
+    }
+
+    @Override
+    boolean needsCallee() {
+        return code.needsCallee();
+    }
+
+    @Override
+    MethodType getGenericType() {
+        // We need to ask the code for its generic type. We can't just rely on this function data's arity, as it's not
+        // actually correct for lots of built-ins. E.g. ECMAScript 5.1 section 15.5.3.2 prescribes that
+        // Script.fromCharCode([char0[, char1[, ...]]]) has a declared arity of 1 even though it's a variable arity
+        // method.
+        return code.getFinalGenericType();
+    }
+
     private void addInvoker(final MethodHandle mh) {
+        assert !needsCallee(mh);
         if (isConstructor(mh)) {
             // only nasgen constructors: (boolean, self, args) are subject to binding a boolean newObj. isConstructor
             // is too conservative a check. However, isConstructor(mh) always implies isConstructor param
             assert isConstructor();
+            code.add(CompiledFunction.createBuiltInConstructor(mh));
+/*=======
             final MethodHandle invoker = MH.insertArguments(mh, 0, false);
             final MethodHandle constructor = composeConstructor(MH.insertArguments(mh, 0, true));
             code.add(new CompiledFunction(mh.type(), invoker, constructor));
+>>>>>>> theirs*/
         } else {
-            code.add(new CompiledFunction(mh.type(), mh));
+            code.add(new CompiledFunction(mh));
         }
     }
 
-    private static int arity(final MethodHandle mh) {
+    private static int methodHandleArity(final MethodHandle mh) {
         if (isVarArg(mh)) {
-            return -1;
+            return MAX_ARITY;
         }
 
         //drop self, callee and boolean constructor flag to get real arity
--- a/nashorn/src/jdk/nashorn/internal/runtime/FindProperty.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/FindProperty.java	Wed Feb 26 13:17:57 2014 +0100
@@ -26,6 +26,7 @@
 package jdk.nashorn.internal.runtime;
 
 import static jdk.nashorn.internal.lookup.Lookup.MH;
+import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
 
 import java.lang.invoke.MethodHandle;
 import jdk.nashorn.internal.codegen.ObjectClassGenerator;
@@ -63,15 +64,28 @@
      * @see ObjectClassGenerator
      *
      * @param type type of getter, e.g. int.class if we want a function with {@code get()I} signature
+     * @param programPoint program point, or INVALID_PROGRAM_POINT if pessimistic
      * @return method handle for the getter
      */
-    public MethodHandle getGetter(final Class<?> type) {
-        MethodHandle getter = property.getGetter(type);
+    public MethodHandle getGetter(final Class<?> type, final int programPoint) {
+        final MethodHandle getter;
+        if (isValid(programPoint)) {
+            getter = property.getOptimisticGetter(type, programPoint);
+        } else {
+            getter = property.getGetter(type);
+        }
+        return getGetterInner(getter);
+    }
+
+    private MethodHandle getGetterInner(final MethodHandle getter) {
         if (property instanceof UserAccessorProperty) {
-            final UserAccessorProperty uc = (UserAccessorProperty)property;
-            getter = MH.insertArguments(getter, 0, isInherited() ? getOwner() : null, uc.getGetterSlot());
+            final UserAccessorProperty uc        = (UserAccessorProperty)property;
+            final ScriptObject         owner     = getOwner();
+            final ScriptObject         container = (owner != null) ? owner : self;
+            return MH.insertArguments(getter, 0, uc.getAccessors(container));
         }
         return getter;
+
     }
 
     /**
@@ -86,11 +100,12 @@
      * @return method handle for the getter
      */
     public MethodHandle getSetter(final Class<?> type, final boolean strict) {
-        MethodHandle setter = property.getSetter(type, getOwner().getMap());
+        final MethodHandle setter = property.getSetter(type, getOwner().getMap());
         if (property instanceof UserAccessorProperty) {
-            final UserAccessorProperty uc = (UserAccessorProperty) property;
-            setter = MH.insertArguments(setter, 0, isInherited() ? getOwner() : null,
-                    uc.getSetterSlot(), strict? property.getKey() : null);
+            final UserAccessorProperty uc        = (UserAccessorProperty)property;
+            final ScriptObject         owner     = getOwner();
+            final ScriptObject         container = (owner != null) ? owner : self;
+            return MH.insertArguments(setter, 0, uc.getAccessors(container), strict ? property.getKey() : null);
         }
 
         return setter;
@@ -155,7 +170,27 @@
 
     /**
      * Get the property value from self as object.
-     *
+     * @return the property value
+     */
+    public int getIntValue() {
+        return property.getIntValue(getGetterReceiver(), getOwner());
+    }
+    /**
+     * Get the property value from self as object.
+     * @return the property value
+     */
+    public long getLongValue() {
+        return property.getLongValue(getGetterReceiver(), getOwner());
+    }
+    /**
+     * Get the property value from self as object.
+     * @return the property value
+     */
+    public double getDoubleValue() {
+        return property.getDoubleValue(getGetterReceiver(), getOwner());
+    }
+    /**
+     * Get the property value from self as object.
      * @return the property value
      */
     public Object getObjectValue() {
@@ -168,8 +203,38 @@
      * @param value the new value
      * @param strict strict flag
      */
-    public void setObjectValue(final Object value, final boolean strict) {
-        property.setObjectValue(getSetterReceiver(), getOwner(), value, strict);
+    public void setValue(final int value, final boolean strict) {
+        property.setValue(getSetterReceiver(), getOwner(), value, strict);
+    }
+
+    /**
+     * Set the property value in self.
+     *
+     * @param value the new value
+     * @param strict strict flag
+     */
+    public void setValue(final long value, final boolean strict) {
+        property.setValue(getSetterReceiver(), getOwner(), value, strict);
+    }
+
+    /**
+     * Set the property value in self.
+     *
+     * @param value the new value
+     * @param strict strict flag
+     */
+    public void setValue(final double value, final boolean strict) {
+        property.setValue(getSetterReceiver(), getOwner(), value, strict);
+    }
+
+    /**
+     * Set the property value in self.
+     *
+     * @param value the new value
+     * @param strict strict flag
+     */
+    public void setValue(final Object value, final boolean strict) {
+        property.setValue(getSetterReceiver(), getOwner(), value, strict);
     }
 
     /**
@@ -187,5 +252,10 @@
         return length;
     }
 
+    @Override
+    public String toString() {
+        return "[FindProperty: " + property.getKey() + ']';
+    }
+
 }
 
--- a/nashorn/src/jdk/nashorn/internal/runtime/FunctionScope.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/FunctionScope.java	Wed Feb 26 13:17:57 2014 +0100
@@ -72,6 +72,19 @@
     }
 
     /**
+     * Constructor
+     *
+     * @param map            property map
+     * @param primitiveSpill primitive spill pool
+     * @param objectSpill    reference spill pool
+     */
+    public FunctionScope(final PropertyMap map, final long[] primitiveSpill, final Object[] objectSpill) {
+        super(map, primitiveSpill, objectSpill);
+        this.arguments = null;
+    }
+
+
+    /**
      * Get the current split state.
      * @return current split state
      */
--- a/nashorn/src/jdk/nashorn/internal/runtime/JSONFunctions.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/JSONFunctions.java	Wed Feb 26 13:17:57 2014 +0100
@@ -188,7 +188,7 @@
         } else if (node instanceof UnaryNode) {
             // UnaryNode used only to represent negative number JSON value
             final UnaryNode unaryNode = (UnaryNode)node;
-            return -((LiteralNode<?>)unaryNode.rhs()).getNumber();
+            return -((LiteralNode<?>)unaryNode.getExpression()).getNumber();
         } else {
             return null;
         }
--- a/nashorn/src/jdk/nashorn/internal/runtime/JSType.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/JSType.java	Wed Feb 26 13:17:57 2014 +0100
@@ -26,16 +26,21 @@
 package jdk.nashorn.internal.runtime;
 
 import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
+import static jdk.nashorn.internal.lookup.Lookup.MH;
 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
 
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.reflect.Array;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.Deque;
 import java.util.List;
+
 import jdk.internal.dynalink.beans.StaticClass;
 import jdk.nashorn.api.scripting.JSObject;
 import jdk.nashorn.internal.codegen.CompilerConstants.Call;
+import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.parser.Lexer;
 import jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator;
 import jdk.nashorn.internal.runtime.linker.Bootstrap;
@@ -71,58 +76,175 @@
     /** Max value for an uint32 in JavaScript */
     public static final long MAX_UINT = 0xFFFF_FFFFL;
 
-    private static final MethodHandles.Lookup myLookup = MethodHandles.lookup();
+    private static final MethodHandles.Lookup JSTYPE_LOOKUP = MethodHandles.lookup();
 
     /** JavaScript compliant conversion function from Object to boolean */
-    public static final Call TO_BOOLEAN = staticCall(myLookup, JSType.class, "toBoolean", boolean.class, Object.class);
+    public static final Call TO_BOOLEAN = staticCall(JSTYPE_LOOKUP, JSType.class, "toBoolean", boolean.class, Object.class);
 
     /** JavaScript compliant conversion function from number to boolean */
-    public static final Call TO_BOOLEAN_D = staticCall(myLookup, JSType.class, "toBoolean", boolean.class, double.class);
+    public static final Call TO_BOOLEAN_D = staticCall(JSTYPE_LOOKUP, JSType.class, "toBoolean", boolean.class, double.class);
 
     /** JavaScript compliant conversion function from Object to integer */
-    public static final Call TO_INTEGER = staticCall(myLookup, JSType.class, "toInteger", int.class, Object.class);
+    public static final Call TO_INTEGER = staticCall(JSTYPE_LOOKUP, JSType.class, "toInteger", int.class, Object.class);
 
     /** JavaScript compliant conversion function from Object to long */
-    public static final Call TO_LONG = staticCall(myLookup, JSType.class, "toLong", long.class, Object.class);
+    public static final Call TO_LONG = staticCall(JSTYPE_LOOKUP, JSType.class, "toLong", long.class, Object.class);
+
+    /** JavaScript compliant conversion function from double to long */
+    public static final Call TO_LONG_D = staticCall(JSTYPE_LOOKUP, JSType.class, "toLong", long.class, double.class);
 
     /** JavaScript compliant conversion function from Object to number */
-    public static final Call TO_NUMBER = staticCall(myLookup, JSType.class, "toNumber", double.class, Object.class);
+    public static final Call TO_NUMBER = staticCall(JSTYPE_LOOKUP, JSType.class, "toNumber", double.class, Object.class);
+
+    /** JavaScript compliant conversion function from Object to number with type check */
+    public static final Call TO_NUMBER_OPTIMISTIC = staticCall(JSTYPE_LOOKUP, JSType.class, "toNumberOptimistic", double.class, Object.class, int.class);
 
     /** JavaScript compliant conversion function from Object to String */
-    public static final Call TO_STRING = staticCall(myLookup, JSType.class, "toString", String.class, Object.class);
+    public static final Call TO_STRING = staticCall(JSTYPE_LOOKUP, JSType.class, "toString", String.class, Object.class);
 
     /** JavaScript compliant conversion function from Object to int32 */
-    public static final Call TO_INT32 = staticCall(myLookup, JSType.class, "toInt32", int.class, Object.class);
+    public static final Call TO_INT32 = staticCall(JSTYPE_LOOKUP, JSType.class, "toInt32", int.class, Object.class);
+
+    /** JavaScript compliant conversion function from Object to int32 */
+    public static final Call TO_INT32_L = staticCall(JSTYPE_LOOKUP, JSType.class, "toInt32", int.class, long.class);
+
+    /** JavaScript compliant conversion function from Object to int32 with type check */
+    public static final Call TO_INT32_OPTIMISTIC = staticCall(JSTYPE_LOOKUP, JSType.class, "toInt32Optimistic", int.class, Object.class, int.class);
 
     /** JavaScript compliant conversion function from double to int32 */
-    public static final Call TO_INT32_D = staticCall(myLookup, JSType.class, "toInt32", int.class, double.class);
+    public static final Call TO_INT32_D = staticCall(JSTYPE_LOOKUP, JSType.class, "toInt32", int.class, double.class);
 
     /** JavaScript compliant conversion function from Object to uint32 */
-    public static final Call TO_UINT32 = staticCall(myLookup, JSType.class, "toUint32", long.class, Object.class);
+    public static final Call TO_UINT32 = staticCall(JSTYPE_LOOKUP, JSType.class, "toUint32", long.class, Object.class);
+
+    /** JavaScript compliant conversion function from Object to long with type check */
+    public static final Call TO_LONG_OPTIMISTIC = staticCall(JSTYPE_LOOKUP, JSType.class, "toLongOptimistic", long.class, Object.class, int.class);
 
     /** JavaScript compliant conversion function from number to uint32 */
-    public static final Call TO_UINT32_D = staticCall(myLookup, JSType.class, "toUint32", long.class, double.class);
-
-    /** JavaScript compliant conversion function from Object to int64 */
-    public static final Call TO_INT64 = staticCall(myLookup, JSType.class, "toInt64", long.class, Object.class);
-
-    /** JavaScript compliant conversion function from number to int64 */
-    public static final Call TO_INT64_D = staticCall(myLookup, JSType.class, "toInt64", long.class, double.class);
+    public static final Call TO_UINT32_D = staticCall(JSTYPE_LOOKUP, JSType.class, "toUint32", long.class, double.class);
 
     /** JavaScript compliant conversion function from number to String */
-    public static final Call TO_STRING_D = staticCall(myLookup, JSType.class, "toString", String.class, double.class);
+    public static final Call TO_STRING_D = staticCall(JSTYPE_LOOKUP, JSType.class, "toString", String.class, double.class);
 
     /** Combined call to toPrimitive followed by toString. */
-    public static final Call TO_PRIMITIVE_TO_STRING = staticCall(myLookup, JSType.class, "toPrimitiveToString", String.class,  Object.class);
+    public static final Call TO_PRIMITIVE_TO_STRING = staticCall(JSTYPE_LOOKUP, JSType.class, "toPrimitiveToString", String.class, Object.class);
+
+    /** Throw an unwarranted optimism exception */
+    public static final Call THROW_UNWARRANTED = staticCall(JSTYPE_LOOKUP, JSType.class, "throwUnwarrantedOptimismException", Object.class, Object.class, int.class);
+
+    /** Add exact wrapper for potentially overflowing integer operations */
+    public static final Call ADD_EXACT       = staticCall(JSTYPE_LOOKUP, JSType.class, "addExact", int.class, int.class, int.class, int.class);
+
+    /** Sub exact wrapper for potentially overflowing integer operations */
+    public static final Call SUB_EXACT       = staticCall(JSTYPE_LOOKUP, JSType.class, "subExact", int.class, int.class, int.class, int.class);
+
+    /** Multiply exact wrapper for potentially overflowing integer operations */
+    public static final Call MUL_EXACT       = staticCall(JSTYPE_LOOKUP, JSType.class, "mulExact", int.class, int.class, int.class, int.class);
+
+    /** Div exact wrapper for potentially integer division that turns into float point */
+    public static final Call DIV_EXACT       = staticCall(JSTYPE_LOOKUP, JSType.class, "divExact", int.class, int.class, int.class, int.class);
+
+    /** Decrement exact wrapper for potentially overflowing integer operations */
+    public static final Call DECREMENT_EXACT = staticCall(JSTYPE_LOOKUP, JSType.class, "decrementExact",   int.class, int.class, int.class);
+
+    /** Increment exact wrapper for potentially overflowing integer operations */
+    public static final Call INCREMENT_EXACT = staticCall(JSTYPE_LOOKUP, JSType.class, "incrementExact",   int.class, int.class, int.class);
+
+    /** Negate exact exact wrapper for potentially overflowing long operations */
+    public static final Call NEGATE_EXACT         = staticCall(JSTYPE_LOOKUP, JSType.class, "negateExact", int.class, int.class, int.class);
+
+    /** Add exact wrapper for potentially overflowing long operations */
+    public static final Call ADD_EXACT_LONG       = staticCall(JSTYPE_LOOKUP, JSType.class, "addExact", long.class, long.class, long.class, int.class);
+
+    /** Sub exact wrapper for potentially overflowing long operations */
+    public static final Call SUB_EXACT_LONG       = staticCall(JSTYPE_LOOKUP, JSType.class, "subExact", long.class, long.class, long.class, int.class);
+
+    /** Multiply exact wrapper for potentially overflowing long operations */
+    public static final Call MUL_EXACT_LONG       = staticCall(JSTYPE_LOOKUP, JSType.class, "mulExact", long.class, long.class, long.class, int.class);
+
+    /** Div exact wrapper for potentially integer division that turns into float point */
+    public static final Call DIV_EXACT_LONG       = staticCall(JSTYPE_LOOKUP, JSType.class, "divExact", long.class, long.class, long.class, int.class);
+
+    /** Decrement exact wrapper for potentially overflowing long operations */
+    public static final Call DECREMENT_EXACT_LONG = staticCall(JSTYPE_LOOKUP, JSType.class, "decrementExact",  long.class, long.class, int.class);
+
+    /** Increment exact wrapper for potentially overflowing long operations */
+    public static final Call INCREMENT_EXACT_LONG = staticCall(JSTYPE_LOOKUP, JSType.class, "incrementExact",  long.class, long.class, int.class);
+
+    /** Negate exact exact wrapper for potentially overflowing long operations */
+    public static final Call NEGATE_EXACT_LONG    = staticCall(JSTYPE_LOOKUP, JSType.class, "negateExact",     long.class, long.class, int.class);
 
     /** Method handle to convert a JS Object to a Java array. */
-    public static final Call TO_JAVA_ARRAY = staticCall(myLookup, JSType.class, "toJavaArray", Object.class, Object.class, Class.class);
+    public static final Call TO_JAVA_ARRAY = staticCall(JSTYPE_LOOKUP, JSType.class, "toJavaArray", Object.class, Object.class, Class.class);
 
     /** Method handle to convert a JS Object to a Java List. */
-    public static final Call TO_JAVA_LIST = staticCall(myLookup, JSType.class, "toJavaList", List.class, Object.class);
+    public static final Call TO_JAVA_LIST = staticCall(JSTYPE_LOOKUP, JSType.class, "toJavaList", List.class, Object.class);
 
     /** Method handle to convert a JS Object to a Java deque. */
-   public static final Call TO_JAVA_DEQUE = staticCall(myLookup, JSType.class, "toJavaDeque", Deque.class, Object.class);
+    public static final Call TO_JAVA_DEQUE = staticCall(JSTYPE_LOOKUP, JSType.class, "toJavaDeque", Deque.class, Object.class);
+
+    /** Method handle for void returns. */
+    public static final Call VOID_RETURN = staticCall(JSTYPE_LOOKUP, JSType.class, "voidReturn", void.class);
+
+
+    /**
+     * The list of available accessor types in width order. This order is used for type guesses narrow{@literal ->} wide
+     *  in the dual--fields world
+     */
+    private static final List<Type> ACCESSOR_TYPES = Collections.unmodifiableList(
+            Arrays.asList(
+                Type.INT,
+                Type.LONG,
+                Type.NUMBER,
+                Type.OBJECT));
+
+    /** table index for undefined type - hard coded so it can be used in switches at compile time */
+    public static final int TYPE_UNDEFINED_INDEX = -1;
+    /** table index for integer type - hard coded so it can be used in switches at compile time */
+    public static final int TYPE_INT_INDEX    = 0; //getAccessorTypeIndex(int.class);
+    /** table index for long type - hard coded so it can be used in switches at compile time */
+    public static final int TYPE_LONG_INDEX   = 1; //getAccessorTypeIndex(long.class);
+    /** table index for double type - hard coded so it can be used in switches at compile time */
+    public static final int TYPE_DOUBLE_INDEX = 2; //getAccessorTypeIndex(double.class);
+    /** table index for object type - hard coded so it can be used in switches at compile time */
+    public static final int TYPE_OBJECT_INDEX = 3; //getAccessorTypeIndex(Object.class);
+
+    /** object conversion quickies with JS semantics - used for return value and parameter filter */
+    public static final MethodHandle[] CONVERT_OBJECT = {
+        JSType.TO_INT32.methodHandle(),
+        JSType.TO_UINT32.methodHandle(),
+        JSType.TO_NUMBER.methodHandle(),
+        null
+    };
+
+    /**
+     * object conversion quickies with JS semantics - used for return value and parameter filter, optimistic
+     * throws exception upon incompatible type (asking for a narrower one than the storage)
+     */
+    public static final MethodHandle[] CONVERT_OBJECT_OPTIMISTIC = {
+        JSType.TO_INT32_OPTIMISTIC.methodHandle(),
+        JSType.TO_LONG_OPTIMISTIC.methodHandle(),
+        JSType.TO_NUMBER_OPTIMISTIC.methodHandle(),
+        null
+    };
+
+    /** The value of Undefined cast to an int32 */
+    public static final int    UNDEFINED_INT    = 0;
+    /** The value of Undefined cast to a long */
+    public static final long   UNDEFINED_LONG   = 0L;
+    /** The value of Undefined cast to a double */
+    public static final double UNDEFINED_DOUBLE = Double.NaN;
+
+    /**
+     * Method handles for getters that return undefined coerced
+     * to the appropriate type
+     */
+    public static final MethodHandle[] GET_UNDEFINED = new MethodHandle[] {
+        MH.constant(int.class, UNDEFINED_INT),
+        MH.constant(long.class, UNDEFINED_LONG),
+        MH.constant(double.class, UNDEFINED_DOUBLE),
+        MH.constant(Object.class, Undefined.getUndefined()),
+    };
 
     private static final double INT32_LIMIT = 4294967296.0;
 
@@ -181,6 +303,14 @@
     }
 
     /**
+     * Void return method handle glue
+     */
+    public static void voidReturn() {
+        //empty
+        //TODO: fix up SetMethodCreator better so we don't need this stupid thing
+    }
+
+    /**
      * Returns true if double number can be represented as an int
      *
      * @param number a long to inspect
@@ -192,7 +322,8 @@
     }
 
     /**
-     * Returns true if double number can be represented as an int
+     * Returns true if double number can be represented as an int. Note that it returns true for negative zero. If you
+     * need to exclude negative zero, combine this check with {@link #isNegativeZero(double)}.
      *
      * @param number a double to inspect
      *
@@ -203,7 +334,22 @@
     }
 
     /**
-     * Returns true if double number can be represented as a long
+     * Returns true if Object can be represented as an int
+     *
+     * @param obj an object to inspect
+     *
+     * @return true for int representable objects
+     */
+    public static boolean isRepresentableAsInt(final Object obj) {
+        if (obj instanceof Number) {
+            return isRepresentableAsInt(((Number)obj).doubleValue());
+        }
+        return false;
+    }
+
+    /**
+     * Returns true if double number can be represented as a long. Note that it returns true for negative zero. If you
+     * need to exclude negative zero, combine this check with {@link #isNegativeZero(double)}.
      *
      * @param number a double to inspect
      * @return true for long representable doubles
@@ -213,13 +359,36 @@
     }
 
     /**
+     * Returns true if Object can be represented as a long
+     *
+     * @param obj an object to inspect
+     *
+     * @return true for long representable objects
+     */
+    public static boolean isRepresentableAsLong(final Object obj) {
+        if (obj instanceof Number) {
+            return isRepresentableAsLong(((Number)obj).doubleValue());
+        }
+        return false;
+    }
+
+    /**
+     * Returns true if the number is the negative zero ({@code -0.0d}).
+     * @param number the number to test
+     * @return true if it is the negative zero, false otherwise.
+     */
+    public static boolean isNegativeZero(final double number) {
+        return number == 0.0d && Double.doubleToRawLongBits(number) == 0x8000000000000000L;
+    }
+
+    /**
      * Check whether an object is primitive
      *
      * @param obj an object
      *
      * @return true if object is primitive (includes null and undefined)
      */
-   public static boolean isPrimitive(final Object obj) {
+    public static boolean isPrimitive(final Object obj) {
         return obj == null ||
                obj == ScriptRuntime.UNDEFINED ||
                obj instanceof Boolean ||
@@ -491,6 +660,35 @@
     }
 
     /**
+     * Optimistic number conversion - throws UnwarrantedOptimismException if Object
+     *
+     * @param obj           object to convert
+     * @param programPoint  program point
+     * @return double
+     */
+    public static double toNumberOptimistic(final Object obj, final int programPoint) {
+        if (obj != null) {
+            final Class<?> clz = obj.getClass();
+            if (clz == Double.class || clz == Integer.class || clz == Long.class) {
+                return ((Number)obj).doubleValue();
+            }
+        }
+        throw new UnwarrantedOptimismException(obj, programPoint);
+    }
+
+    /**
+     * Object to number conversion that delegates to either {@link #toNumber(Object)} or to
+     * {@link #toNumberOptimistic(Object, int)} depending on whether the program point is valid or not.
+     * @param obj the object to convert
+     * @param programPoint the program point; can be invalid.
+     * @return the value converted to a number
+     * @throws UnwarrantedOptimismException if the value can't be represented as a number and the program point is valid.
+     */
+    public static double toNumberMaybeOptimistic(final Object obj, final int programPoint) {
+        return UnwarrantedOptimismException.isValid(programPoint) ? toNumberOptimistic(obj, programPoint) : toNumber(obj);
+    }
+
+    /**
      * Digit representation for a character
      *
      * @param ch     a character
@@ -609,7 +807,7 @@
     }
 
     /**
-     * JavaScript compliant Object to long conversion. See ECMA 9.4 ToInteger
+     * Converts an Object to long.
      *
      * <p>Note that this returns {@link java.lang.Long#MAX_VALUE} or {@link java.lang.Long#MIN_VALUE}
      * for double values that exceed the long range, including positive and negative Infinity. It is the
@@ -619,7 +817,46 @@
      * @return a long
      */
     public static long toLong(final Object obj) {
-        return (long)toNumber(obj);
+        return obj instanceof Long ? ((Long)obj).longValue() : toLong(toNumber(obj));
+    }
+
+    /**
+     * Converts a double to long.
+     *
+     * @param num the double to convert
+     * @return the converted long value
+     */
+    public static long toLong(final double num) {
+        return (long)num;
+    }
+
+    /**
+     * Optimistic long conversion - throws UnwarrantedOptimismException if double or Object
+     *
+     * @param obj           object to convert
+     * @param programPoint  program point
+     * @return long
+     */
+    public static long toLongOptimistic(final Object obj, final int programPoint) {
+        if (obj != null) {
+            final Class<?> clz = obj.getClass();
+            if (clz == Long.class || clz == Integer.class) {
+                return ((Number)obj).longValue();
+            }
+        }
+        throw new UnwarrantedOptimismException(obj, programPoint);
+    }
+
+    /**
+     * Object to int conversion that delegates to either {@link #toLong(Object)} or to
+     * {@link #toLongOptimistic(Object, int)} depending on whether the program point is valid or not.
+     * @param obj the object to convert
+     * @param programPoint the program point; can be invalid.
+     * @return the value converted to long
+     * @throws UnwarrantedOptimismException if the value can't be represented as long and the program point is valid.
+     */
+    public static long toLongMaybeOptimistic(final Object obj, final int programPoint) {
+        return UnwarrantedOptimismException.isValid(programPoint) ? toLongOptimistic(obj, programPoint) : toLong(obj);
     }
 
     /**
@@ -634,15 +871,49 @@
     }
 
     /**
+     * Optimistic int conversion - throws UnwarrantedOptimismException if double, long or Object
+     *
+     * @param obj           object to convert
+     * @param programPoint  program point
+     * @return double
+     */
+    public static int toInt32Optimistic(final Object obj, final int programPoint) {
+        if (obj != null) {
+            final Class<?> clz = obj.getClass();
+            if (clz == Integer.class) {
+                return ((Integer)obj).intValue();
+            }
+        }
+        throw new UnwarrantedOptimismException(obj, programPoint);
+    }
+
+    /**
+     * Object to int conversion that delegates to either {@link #toInt32(Object)} or to
+     * {@link #toInt32Optimistic(Object, int)} depending on whether the program point is valid or not.
+     * @param obj the object to convert
+     * @param programPoint the program point; can be invalid.
+     * @return the value converted to int
+     * @throws UnwarrantedOptimismException if the value can't be represented as int and the program point is valid.
+     */
+    public static int toInt32MaybeOptimistic(final Object obj, final int programPoint) {
+        return UnwarrantedOptimismException.isValid(programPoint) ? toInt32Optimistic(obj, programPoint) : toInt32(obj);
+    }
+
+    // Minimum and maximum range between which every long value can be precisely represented as a double.
+    private static final long MAX_PRECISE_DOUBLE = (1L << 53);
+    private static final long MIN_PRECISE_DOUBLE = -MAX_PRECISE_DOUBLE;
+
+    /**
      * JavaScript compliant long to int32 conversion
      *
      * @param num a long
      * @return an int32
      */
     public static int toInt32(final long num) {
-        return (int)num;
+        return (int)(num >= MIN_PRECISE_DOUBLE && num <= MAX_PRECISE_DOUBLE ? num : (long)(num % INT32_LIMIT));
     }
 
+
     /**
      * JavaScript compliant number to int32 conversion
      *
@@ -654,29 +925,6 @@
     }
 
     /**
-     * JavaScript compliant Object to int64 conversion
-     *
-     * @param obj an object
-     * @return an int64
-     */
-    public static long toInt64(final Object obj) {
-        return toInt64(toNumber(obj));
-    }
-
-    /**
-     * JavaScript compliant number to int64 conversion
-     *
-     * @param num a number
-     * @return an int64
-     */
-    public static long toInt64(final double num) {
-        if (Double.isInfinite(num)) {
-            return 0L;
-        }
-        return (long)num;
-    }
-
-    /**
      * JavaScript compliant Object to uint32 conversion
      *
      * @param obj an object
@@ -1011,6 +1259,368 @@
         return str.substring(start);
     }
 
+    /**
+     * Throw an unwarranted optimism exception for a program point
+     * @param value         real return value
+     * @param programPoint  program point
+     * @return
+     */
+    @SuppressWarnings("unused")
+    private static Object throwUnwarrantedOptimismException(final Object value, final int programPoint) {
+        throw new UnwarrantedOptimismException(value, programPoint);
+    }
+
+    /**
+     * Wrapper for addExact
+     *
+     * Catches ArithmeticException and rethrows as UnwarrantedOptimismException
+     * containing the result and the program point of the failure
+     *
+     * @param x first term
+     * @param y second term
+     * @param programPoint program point id
+     * @return the result
+     * @throws UnwarrantedOptimismException if overflow occurs
+     */
+    public static int addExact(final int x, final int y, final int programPoint) throws UnwarrantedOptimismException {
+        try {
+            return Math.addExact(x, y);
+        } catch (ArithmeticException e) {
+            throw new UnwarrantedOptimismException((long)x + (long)y, programPoint);
+        }
+    }
+
+    /**
+     * Wrapper for addExact
+     *
+     * Catches ArithmeticException and rethrows as UnwarrantedOptimismException
+     * containing the result and the program point of the failure
+     *
+     * @param x first term
+     * @param y second term
+     * @param programPoint program point id
+     * @return the result
+     * @throws UnwarrantedOptimismException if overflow occurs
+     */
+    public static long addExact(final long x, final long y, final int programPoint) throws UnwarrantedOptimismException {
+        try {
+            return Math.addExact(x, y);
+        } catch (ArithmeticException e) {
+            throw new UnwarrantedOptimismException((double)x + (double)y, programPoint);
+        }
+    }
+
+    /**
+     * Wrapper for subExact
+     *
+     * Catches ArithmeticException and rethrows as UnwarrantedOptimismException
+     * containing the result and the program point of the failure
+     *
+     * @param x first term
+     * @param y second term
+     * @param programPoint program point id
+     * @return the result
+     * @throws UnwarrantedOptimismException if overflow occurs
+     */
+    public static int subExact(final int x, final int y, final int programPoint) throws UnwarrantedOptimismException {
+        try {
+            return Math.subtractExact(x, y);
+        } catch (ArithmeticException e) {
+            throw new UnwarrantedOptimismException((long)x - (long)y, programPoint);
+        }
+    }
+
+    /**
+     * Wrapper for subExact
+     *
+     * Catches ArithmeticException and rethrows as UnwarrantedOptimismException
+     * containing the result and the program point of the failure
+     *
+     * @param x first term
+     * @param y second term
+     * @param programPoint program point id
+     * @return the result
+     * @throws UnwarrantedOptimismException if overflow occurs
+     */
+    public static long subExact(final long x, final long y, final int programPoint) throws UnwarrantedOptimismException {
+        try {
+            return Math.subtractExact(x, y);
+        } catch (ArithmeticException e) {
+            throw new UnwarrantedOptimismException((double)x - (double)y, programPoint);
+        }
+    }
+
+    /**
+     * Wrapper for mulExact
+     *
+     * Catches ArithmeticException and rethrows as UnwarrantedOptimismException
+     * containing the result and the program point of the failure
+     *
+     * @param x first term
+     * @param y second term
+     * @param programPoint program point id
+     * @return the result
+     * @throws UnwarrantedOptimismException if overflow occurs
+     */
+    public static int mulExact(final int x, final int y, final int programPoint) throws UnwarrantedOptimismException {
+        try {
+            return Math.multiplyExact(x, y);
+        } catch (ArithmeticException e) {
+            throw new UnwarrantedOptimismException((long)x * (long)y, programPoint);
+        }
+    }
+
+    /**
+     * Wrapper for mulExact
+     *
+     * Catches ArithmeticException and rethrows as UnwarrantedOptimismException
+     * containing the result and the program point of the failure
+     *
+     * @param x first term
+     * @param y second term
+     * @param programPoint program point id
+     * @return the result
+     * @throws UnwarrantedOptimismException if overflow occurs
+     */
+    public static long mulExact(final long x, final long y, final int programPoint) throws UnwarrantedOptimismException {
+        try {
+            return Math.multiplyExact(x, y);
+        } catch (final ArithmeticException e) {
+            throw new UnwarrantedOptimismException((double)x * (double)y, programPoint);
+        }
+    }
+
+    /**
+     * Wrapper for divExact. Throws UnwarrantedOptimismException if the result of the division can't be represented as
+     * int.
+     *
+     * @param x first term
+     * @param y second term
+     * @param programPoint program point id
+     * @return the result
+     * @throws UnwarrantedOptimismException if the result of the division can't be represented as int.
+     */
+    public static int divExact(final int x, final int y, final int programPoint) throws UnwarrantedOptimismException {
+        final int res;
+        try {
+            res = x / y;
+        } catch (final ArithmeticException e) {
+            assert y == 0; // Only div by zero anticipated
+            throw new UnwarrantedOptimismException(x > 0 ? Double.POSITIVE_INFINITY : x < 0 ? Double.NEGATIVE_INFINITY : Double.NaN, programPoint);
+        }
+        final int rem = x % y;
+        if (rem == 0) {
+            return res;
+        }
+        // go directly to double here, as anything with non zero remainder is a floating point number in JavaScript
+        throw new UnwarrantedOptimismException((double)x / (double)y, programPoint);
+    }
+
+    /**
+     * Wrapper for divExact. Throws UnwarrantedOptimismException if the result of the division can't be represented as
+     * long.
+     *
+     * @param x first term
+     * @param y second term
+     * @param programPoint program point id
+     * @return the result
+     * @throws UnwarrantedOptimismException if the result of the division can't be represented as long.
+     */
+    public static long divExact(final long x, final long y, final int programPoint) throws UnwarrantedOptimismException {
+        final long res;
+        try {
+            res = x / y;
+        } catch (final ArithmeticException e) {
+            assert y == 0L; // Only div by zero anticipated
+            throw new UnwarrantedOptimismException(x > 0L ? Double.POSITIVE_INFINITY : x < 0L ? Double.NEGATIVE_INFINITY : Double.NaN, programPoint);
+        }
+        final long rem = x % y;
+        if (rem == 0L) {
+            return res;
+        }
+        throw new UnwarrantedOptimismException((double)x / (double)y, programPoint);
+    }
+
+    /**
+     * Wrapper for decrementExact
+     *
+     * Catches ArithmeticException and rethrows as UnwarrantedOptimismException
+     * containing the result and the program point of the failure
+     *
+     * @param x number to negate
+     * @param programPoint program point id
+     * @return the result
+     * @throws UnwarrantedOptimismException if overflow occurs
+     */
+    public static int decrementExact(final int x, final int programPoint) throws UnwarrantedOptimismException {
+        try {
+            return Math.decrementExact(x);
+        } catch (final ArithmeticException e) {
+            throw new UnwarrantedOptimismException((long)x - 1, programPoint);
+        }
+    }
+
+    /**
+     * Wrapper for decrementExact
+     *
+     * Catches ArithmeticException and rethrows as UnwarrantedOptimismException
+     * containing the result and the program point of the failure
+     *
+     * @param x number to negate
+     * @param programPoint program point id
+     * @return the result
+     * @throws UnwarrantedOptimismException if overflow occurs
+     */
+    public static long decrementExact(final long x, final int programPoint) throws UnwarrantedOptimismException {
+        try {
+            return Math.decrementExact(x);
+        } catch (final ArithmeticException e) {
+            throw new UnwarrantedOptimismException((double)x - 1L, programPoint);
+        }
+    }
+
+    /**
+     * Wrapper for incrementExact
+     *
+     * Catches ArithmeticException and rethrows as UnwarrantedOptimismException
+     * containing the result and the program point of the failure
+     *
+     * @param x the number to increment
+     * @param programPoint program point id
+     * @return the result
+     * @throws UnwarrantedOptimismException if overflow occurs
+     */
+    public static int incrementExact(final int x, final int programPoint) throws UnwarrantedOptimismException {
+        try {
+            return Math.incrementExact(x);
+        } catch (final ArithmeticException e) {
+            throw new UnwarrantedOptimismException((long)x + 1, programPoint);
+        }
+    }
+
+    /**
+     * Wrapper for incrementExact
+     *
+     * Catches ArithmeticException and rethrows as UnwarrantedOptimismException
+     * containing the result and the program point of the failure
+     *
+     * @param x the number to increment
+     * @param programPoint program point id
+     * @return the result
+     * @throws UnwarrantedOptimismException if overflow occurs
+     */
+    public static long incrementExact(final long x, final int programPoint) throws UnwarrantedOptimismException {
+        try {
+            return Math.incrementExact(x);
+        } catch (final ArithmeticException e) {
+            throw new UnwarrantedOptimismException((double)x + 1L, programPoint);
+        }
+    }
+
+    /**
+     * Wrapper for negateExact
+     *
+     * Catches ArithmeticException and rethrows as UnwarrantedOptimismException
+     * containing the result and the program point of the failure
+     *
+     * @param x the number to negate
+     * @param programPoint program point id
+     * @return the result
+     * @throws UnwarrantedOptimismException if overflow occurs
+     */
+    public static int negateExact(final int x, final int programPoint) throws UnwarrantedOptimismException {
+        try {
+            if (x == 0) {
+                throw new UnwarrantedOptimismException(-0.0, programPoint);
+            }
+            return Math.negateExact(x);
+        } catch (final ArithmeticException e) {
+            throw new UnwarrantedOptimismException(-(long)x, programPoint);
+        }
+    }
+
+    /**
+     * Wrapper for negateExact
+     *
+     * Catches ArithmeticException and rethrows as UnwarrantedOptimismException
+     * containing the result and the program point of the failure
+     *
+     * @param x the number to negate
+     * @param programPoint program point id
+     * @return the result
+     * @throws UnwarrantedOptimismException if overflow occurs
+     */
+    public static long negateExact(final long x, final int programPoint) throws UnwarrantedOptimismException {
+        try {
+            if (x == 0L) {
+                throw new UnwarrantedOptimismException(-0.0, programPoint);
+            }
+            return Math.negateExact(x);
+        } catch (final ArithmeticException e) {
+            throw new UnwarrantedOptimismException(-(double)x, programPoint);
+        }
+    }
+
+    /**
+     * Given a type of an accessor, return its index in [0..getNumberOfAccessorTypes())
+     *
+     * @param type the type
+     *
+     * @return the accessor index, or -1 if no accessor of this type exists
+     */
+    public static int getAccessorTypeIndex(final Type type) {
+        return getAccessorTypeIndex(type.getTypeClass());
+    }
+
+    /**
+     * Given a class of an accessor, return its index in [0..getNumberOfAccessorTypes())
+     *
+     * Note that this is hardcoded with respect to the dynamic contents of the accessor
+     * types array for speed. Hotspot got stuck with this as 5% of the runtime in
+     * a benchmark when it looped over values and increased an index counter. :-(
+     *
+     * @param type the type
+     *
+     * @return the accessor index, or -1 if no accessor of this type exists
+     */
+    public static int getAccessorTypeIndex(final Class<?> type) {
+        if (type == null) {
+            return TYPE_UNDEFINED_INDEX;
+        } else if (type == int.class) {
+            return TYPE_INT_INDEX;
+        } else if (type == long.class) {
+            return TYPE_LONG_INDEX;
+        } else if (type == double.class) {
+            return TYPE_DOUBLE_INDEX;
+        } else if (!type.isPrimitive()) {
+            return TYPE_OBJECT_INDEX;
+        }
+        return -1;
+    }
+
+    /**
+     * Return the accessor type based on its index in [0..getNumberOfAccessorTypes())
+     * Indexes are ordered narrower{@literal ->}wider / optimistic{@literal ->}pessimistic. Invalidations always
+     * go to a type of higher index
+     *
+     * @param index accessor type index
+     *
+     * @return a type corresponding to the index.
+     */
+
+    public static Type getAccessorType(final int index) {
+        return ACCESSOR_TYPES.get(index);
+    }
+
+    /**
+     * Return the number of accessor types available.
+     *
+     * @return number of accessor types in system
+     */
+    public static int getNumberOfAccessorTypes() {
+        return ACCESSOR_TYPES.size();
+    }
+
     private static double parseRadix(final char chars[], final int start, final int length, final int radix) {
         int pos = 0;
 
@@ -1071,4 +1681,5 @@
             throw new RuntimeException(t);
         }
     }
+
 }
--- a/nashorn/src/jdk/nashorn/internal/runtime/NativeJavaPackage.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/NativeJavaPackage.java	Wed Feb 26 13:17:57 2014 +0100
@@ -25,6 +25,8 @@
 
 package jdk.nashorn.internal.runtime;
 
+import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
+
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
@@ -203,8 +205,12 @@
     }
 
     @Override
-    protected Object invokeNoSuchProperty(final String name) {
-        return createProperty(name);
+    protected Object invokeNoSuchProperty(final String name, final int programPoint) {
+        final Object retval = createProperty(name);
+        if (isValid(programPoint)) {
+            throw new UnwarrantedOptimismException(retval, programPoint);
+        }
+        return retval;
     }
 
     @Override
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/runtime/OptimisticReturnFilters.java	Wed Feb 26 13:17:57 2014 +0100
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.runtime;
+
+import static jdk.nashorn.internal.lookup.Lookup.MH;
+import static jdk.nashorn.internal.runtime.JSType.getAccessorTypeIndex;
+import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import jdk.internal.dynalink.CallSiteDescriptor;
+import jdk.internal.dynalink.linker.GuardedInvocation;
+import jdk.internal.dynalink.support.TypeUtilities;
+import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
+
+/**
+ * Optimistic return value filters
+ */
+public final class OptimisticReturnFilters {
+    private static final MethodHandle[] ENSURE_INT;
+    private static final MethodHandle[] ENSURE_LONG;
+    private static final MethodHandle[] ENSURE_NUMBER;
+
+    private static final int BOOLEAN_TYPE_INDEX;
+    private static final int CHAR_TYPE_INDEX;
+    private static final int FLOAT_TYPE_INDEX;
+
+    static {
+        final MethodHandle INT_DOUBLE = findOwnMH("ensureInt", int.class, double.class, int.class);
+        ENSURE_INT = new MethodHandle[] {
+                null,
+                findOwnMH("ensureInt", int.class, long.class, int.class),
+                INT_DOUBLE,
+                findOwnMH("ensureInt", int.class, Object.class, int.class),
+                findOwnMH("ensureInt", int.class, boolean.class, int.class),
+                findOwnMH("ensureInt", int.class, char.class, int.class),
+                INT_DOUBLE.asType(INT_DOUBLE.type().changeParameterType(0, float.class)),
+        };
+
+        BOOLEAN_TYPE_INDEX = ENSURE_INT.length - 3;
+        CHAR_TYPE_INDEX = ENSURE_INT.length - 2;
+        FLOAT_TYPE_INDEX = ENSURE_INT.length - 1;
+
+        final MethodHandle LONG_DOUBLE = findOwnMH("ensureLong", long.class, double.class, int.class);
+        ENSURE_LONG = new MethodHandle[] {
+                null,
+                null,
+                LONG_DOUBLE,
+                findOwnMH("ensureLong", long.class, Object.class, int.class),
+                ENSURE_INT[BOOLEAN_TYPE_INDEX].asType(ENSURE_INT[BOOLEAN_TYPE_INDEX].type().changeReturnType(long.class)),
+                ENSURE_INT[CHAR_TYPE_INDEX].asType(ENSURE_INT[CHAR_TYPE_INDEX].type().changeReturnType(long.class)),
+                LONG_DOUBLE.asType(LONG_DOUBLE.type().changeParameterType(0, float.class)),
+            };
+
+        ENSURE_NUMBER = new MethodHandle[] {
+                null,
+                null,
+                null,
+                findOwnMH("ensureNumber", double.class, Object.class, int.class),
+                ENSURE_INT[BOOLEAN_TYPE_INDEX].asType(ENSURE_INT[BOOLEAN_TYPE_INDEX].type().changeReturnType(double.class)),
+                ENSURE_INT[CHAR_TYPE_INDEX].asType(ENSURE_INT[CHAR_TYPE_INDEX].type().changeReturnType(double.class)),
+                null
+        };
+    }
+
+    /**
+     * Given a method handle and an expected return type, perform return value filtering
+     * according to the optimistic type coercion rules
+     * @param mh method handle
+     * @param expectedReturnType expected return type
+     * @param programPoint program point
+     * @return filtered method
+     */
+    public static MethodHandle filterOptimisticReturnValue(final MethodHandle mh, final Class<?> expectedReturnType, final int programPoint) {
+        if(!isValid(programPoint)) {
+            return mh;
+        }
+
+        final MethodType type = mh.type();
+        final Class<?> actualReturnType = type.returnType();
+        if(TypeUtilities.isConvertibleWithoutLoss(actualReturnType, expectedReturnType)) {
+            return mh;
+        }
+
+        final MethodHandle guard = getOptimisticTypeGuard(expectedReturnType, actualReturnType);
+        return guard == null ? mh : MH.filterReturnValue(mh, MH.insertArguments(guard, 1, programPoint));
+    }
+
+    /**
+     * Given a guarded invocation and a callsite descriptor, perform return value filtering
+     * according to the optimistic type coercion rules, using the return value from the deccriptor
+     * @param inv the invocation
+     * @param desc the descriptor
+     * @return filtered invocation
+     */
+    public static GuardedInvocation filterOptimisticReturnValue(final GuardedInvocation inv, final CallSiteDescriptor desc) {
+        if(!NashornCallSiteDescriptor.isOptimistic(desc)) {
+            return inv;
+        }
+        return inv.replaceMethods(filterOptimisticReturnValue(inv.getInvocation(), desc.getMethodType().returnType(),
+                NashornCallSiteDescriptor.getProgramPoint(desc)), inv.getGuard());
+    }
+
+    private static MethodHandle getOptimisticTypeGuard(final Class<?> actual, final Class<?> provable) {
+        final MethodHandle guard;
+        final int provableTypeIndex = getProvableTypeIndex(provable);
+        if (actual == int.class) {
+            guard = ENSURE_INT[provableTypeIndex];
+        } else if (actual == long.class) {
+            guard = ENSURE_LONG[provableTypeIndex];
+        } else if (actual == double.class) {
+            guard = ENSURE_NUMBER[provableTypeIndex];
+        } else {
+            guard = null;
+            assert !actual.isPrimitive() : actual + ", " + provable;
+        }
+        if(guard != null && !(provable.isPrimitive())) {
+            // Make sure filtering a MethodHandle(...)String works with a filter MethodHandle(Object, int)... Note that
+            // if the return type of the method is incompatible with Number, then the guard will always throw an
+            // UnwarrantedOperationException when invoked, but we must link it anyway as we need the guarded function to
+            // successfully execute and return the non-convertible return value that it'll put into the thrown
+            // UnwarrantedOptimismException.
+            return guard.asType(guard.type().changeParameterType(0, provable));
+        }
+        return guard;
+    }
+
+    private static int getProvableTypeIndex(Class<?> provable) {
+        final int accTypeIndex = getAccessorTypeIndex(provable);
+        if(accTypeIndex != -1) {
+            return accTypeIndex;
+        } else if(provable == boolean.class) {
+            return BOOLEAN_TYPE_INDEX;
+        } else if(provable == byte.class || provable == short.class) {
+            return 0; // never needs a guard, as it's assignable to int
+        } else if(provable == char.class) {
+            return CHAR_TYPE_INDEX;
+        } else if(provable == float.class) {
+            return FLOAT_TYPE_INDEX;
+        }
+        throw new AssertionError(provable.getName());
+    }
+
+    //maps staticallyProvableCallSiteType to actualCallSiteType, throws exception if impossible
+    @SuppressWarnings("unused")
+    private static int ensureInt(final long arg, final int programPoint) {
+        if (JSType.isRepresentableAsInt(arg)) {
+            return (int)arg;
+        }
+        throw new UnwarrantedOptimismException(arg, programPoint);
+    }
+
+    @SuppressWarnings("unused")
+    private static int ensureInt(final double arg, final int programPoint) {
+        if (JSType.isRepresentableAsInt(arg) && !JSType.isNegativeZero(arg)) {
+            return (int)arg;
+        }
+        throw new UnwarrantedOptimismException(arg, programPoint);
+    }
+
+    /**
+     * Returns the argument value as an int. If the argument is not a Number object that can be exactly represented as
+     * an int, throw an {@link UnwarrantedOptimismException}.This method is only public so that generated script code
+     * can use it. See {code CodeGenerator.ENSURE_INT}.
+     * @param arg the original argument.
+     * @param programPoint the program point used in the exception
+     * @return the value of the argument as an int.
+     * @throws UnwarrantedOptimismException if the argument is not a Number that can be exactly represented as an int.
+     */
+    public static int ensureInt(final Object arg, final int programPoint) {
+        // NOTE: this doesn't delegate to ensureInt(double, int) as in that case if arg were a Long, it would throw a
+        // (potentially imprecise) Double in the UnwarrantedOptimismException. This way, it will put the correct valued
+        // Long into the exception.
+        if (arg instanceof Number) {
+            final double d = ((Number)arg).doubleValue();
+            if (JSType.isRepresentableAsInt(d) && !JSType.isNegativeZero(d)) {
+                return (int)d;
+            }
+        }
+        throw new UnwarrantedOptimismException(arg, programPoint);
+    }
+
+    @SuppressWarnings("unused")
+    private static int ensureInt(final boolean arg, final int programPoint) {
+        throw new UnwarrantedOptimismException(arg, programPoint, Type.OBJECT);
+    }
+
+    @SuppressWarnings("unused")
+    private static int ensureInt(final char arg, final int programPoint) {
+        throw new UnwarrantedOptimismException(arg, programPoint, Type.OBJECT);
+    }
+
+    private static long ensureLong(final double arg, final int programPoint) {
+        if (JSType.isRepresentableAsLong(arg) && !JSType.isNegativeZero(arg)) {
+            return (long)arg;
+        }
+        throw new UnwarrantedOptimismException(arg, programPoint);
+    }
+
+    /**
+     * Returns the argument value as a long. If the argument is not a Number object that can be exactly represented as
+     * a long, throw an {@link UnwarrantedOptimismException}.This method is only public so that generated script code
+     * can use it. See {code CodeGenerator.ENSURE_LONG}.
+     * @param arg the original argument.
+     * @param programPoint the program point used in the exception
+     * @return the value of the argument as a long.
+     * @throws UnwarrantedOptimismException if the argument is not a Number that can be exactly represented as a long.
+     */
+    public static long ensureLong(final Object arg, final int programPoint) {
+        if (arg instanceof Long) {
+            // Must check for Long separately, as Long.doubleValue() isn't precise.
+            return ((Long)arg).longValue();
+        } else if (arg instanceof Number) {
+            return ensureLong(((Number)arg).doubleValue(), programPoint);
+        }
+        throw new UnwarrantedOptimismException(arg, programPoint);
+    }
+
+    /**
+     * Returns the argument value as a double. If the argument is not a Number object, throw an
+     * {@link UnwarrantedOptimismException}.This method is only public so that generated script code can use it. See
+     * {code CodeGenerator.ENSURE_NUMBER}.
+     * @param arg the original argument.
+     * @param programPoint the program point used in the exception
+     * @return the value of the argument as a double.
+     * @throws UnwarrantedOptimismException if the argument is not a Number that can be exactly represented as a long.
+     */
+    public static double ensureNumber(final Object arg, final int programPoint) {
+        if (arg instanceof Number) { // arg == null -> false
+            return ((Number)arg).doubleValue();
+        }
+        throw new UnwarrantedOptimismException(arg, programPoint);
+    }
+
+    private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
+        return MH.findStatic(MethodHandles.lookup(), OptimisticReturnFilters.class, name, MH.type(rtype, types));
+    }
+}
--- a/nashorn/src/jdk/nashorn/internal/runtime/Property.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/Property.java	Wed Feb 26 13:17:57 2014 +0100
@@ -66,32 +66,34 @@
 
     private static final int MODIFY_MASK     = (NOT_WRITABLE | NOT_ENUMERABLE | NOT_CONFIGURABLE);
 
-    /** Is this a spill property? See {@link AccessorProperty} */
-    public static final int IS_SPILL         = 1 << 3;
-
     /** Is this a function parameter? */
-    public static final int IS_PARAMETER     = 1 << 4;
+    public static final int IS_PARAMETER     = 1 << 3;
 
     /** Is parameter accessed thru arguments? */
-    public static final int HAS_ARGUMENTS    = 1 << 5;
+    public static final int HAS_ARGUMENTS    = 1 << 4;
 
     /** Is this property always represented as an Object? See {@link ObjectClassGenerator} and dual fields flag. */
-    public static final int IS_ALWAYS_OBJECT = 1 << 6;
-
-    /** Can this property be primitive? */
-    public static final int CAN_BE_PRIMITIVE = 1 << 7;
+    public static final int IS_ALWAYS_OBJECT = 1 << 5;
 
     /** Can this property be undefined? */
-    public static final int CAN_BE_UNDEFINED = 1 << 8;
+    public static final int CAN_BE_UNDEFINED = 1 << 6;
+
+    /** Is this a function declaration property ? */
+    public static final int IS_FUNCTION_DECLARATION = 1 << 7;
 
-    /* Is this a function declaration property ? */
-    public static final int IS_FUNCTION_DECLARATION = 1 << 9;
+    /**
+     * Is this is a primitive field given to us by Nasgen, i.e.
+     * something we can be sure remains a constant whose type
+     * is narrower than object, e.g. Math.PI which is declared
+     * as a double
+     */
+    public static final int IS_NASGEN_PRIMITIVE = 1 << 8;
 
     /** Property key. */
     private final String key;
 
     /** Property flags. */
-    protected int flags;
+    private int flags;
 
     /** Property field number or spill slot. */
     private final int slot;
@@ -126,7 +128,15 @@
      *
      * @return cloned property
      */
-    abstract Property copy();
+    public abstract Property copy();
+
+    /**
+     * Copy function
+     *
+     * @param  newType new type
+     * @return cloned property with new type
+     */
+    public abstract Property copy(final Class<?> newType);
 
     /**
      * Property flag utility method for {@link PropertyDescriptor}s. Given two property descriptors,
@@ -248,19 +258,18 @@
      * @return true if spill property
      */
     public boolean isSpill() {
-        return (flags & IS_SPILL) == IS_SPILL;
+        return false;
     }
 
     /**
      * Does this property use any slots in the spill array described in
      * {@link Property#isSpill}? In that case how many. Currently a property
      * only uses max one spill slot, but this may change in future representations
-     * Only {@link AccessorProperty} instances use spill slots
      *
      * @return number of spill slots a property is using
      */
     public int getSpillCount() {
-        return isSpill() ? 1 : 0;
+        return 0;
     }
 
     /**
@@ -344,6 +353,14 @@
     public abstract MethodHandle getGetter(final Class<?> type);
 
     /**
+     * Get an optimistic getter that throws an exception if type is not the known given one
+     * @param type          type
+     * @param programPoint  program point
+     * @return getter
+     */
+    public abstract MethodHandle getOptimisticGetter(final Class<?> type, final int programPoint);
+
+    /**
      * Get the key for this property. This key is an ordinary string. The "name".
      * @return key for property
      */
@@ -360,6 +377,46 @@
     }
 
     /**
+     * get the Object value of this property from {@code owner}. This allows to bypass creation of the
+     * getter MethodHandle for spill and user accessor properties.
+     *
+     * @param self the this object
+     * @param owner the owner of the property
+     * @return  the property value
+     */
+    public abstract int getIntValue(final ScriptObject self, final ScriptObject owner);
+
+    /**
+     * get the Object value of this property from {@code owner}. This allows to bypass creation of the
+     * getter MethodHandle for spill and user accessor properties.
+     *
+     * @param self the this object
+     * @param owner the owner of the property
+     * @return  the property value
+     */
+    public abstract long getLongValue(final ScriptObject self, final ScriptObject owner);
+
+    /**
+     * get the Object value of this property from {@code owner}. This allows to bypass creation of the
+     * getter MethodHandle for spill and user accessor properties.
+     *
+     * @param self the this object
+     * @param owner the owner of the property
+     * @return  the property value
+     */
+    public abstract double getDoubleValue(final ScriptObject self, final ScriptObject owner);
+
+    /**
+     * get the Object value of this property from {@code owner}. This allows to bypass creation of the
+     * getter MethodHandle for spill and user accessor properties.
+     *
+     * @param self the this object
+     * @param owner the owner of the property
+     * @return  the property value
+     */
+    public abstract Object getObjectValue(final ScriptObject self, final ScriptObject owner);
+
+    /**
      * Set the value of this property in {@code owner}. This allows to bypass creation of the
      * setter MethodHandle for spill and user accessor properties.
      *
@@ -368,17 +425,40 @@
      * @param value the new property value
      * @param strict is this a strict setter?
      */
-    public abstract void setObjectValue(ScriptObject self, ScriptObject owner, Object value, boolean strict);
+    public abstract void setValue(final ScriptObject self, final ScriptObject owner, final int value, final boolean strict);
 
     /**
-     * Set the Object value of this property from {@code owner}. This allows to bypass creation of the
-     * getter MethodHandle for spill and user accessor properties.
+     * Set the value of this property in {@code owner}. This allows to bypass creation of the
+     * setter MethodHandle for spill and user accessor properties.
      *
      * @param self the this object
      * @param owner the owner object
-     * @return  the property value
+     * @param value the new property value
+     * @param strict is this a strict setter?
      */
-    public abstract Object getObjectValue(ScriptObject self, ScriptObject owner);
+    public abstract void setValue(final ScriptObject self, final ScriptObject owner, final long value, final boolean strict);
+
+    /**
+     * Set the value of this property in {@code owner}. This allows to bypass creation of the
+     * setter MethodHandle for spill and user accessor properties.
+     *
+     * @param self the this object
+     * @param owner the owner object
+     * @param value the new property value
+     * @param strict is this a strict setter?
+     */
+    public abstract void setValue(final ScriptObject self, final ScriptObject owner, final double value, final boolean strict);
+
+    /**
+     * Set the value of this property in {@code owner}. This allows to bypass creation of the
+     * setter MethodHandle for spill and user accessor properties.
+     *
+     * @param self the this object
+     * @param owner the owner object
+     * @param value the new property value
+     * @param strict is this a strict setter?
+     */
+    public abstract void setValue(final ScriptObject self, final ScriptObject owner, final Object value, final boolean strict);
 
     /**
      * Abstract method for retrieving the setter for the property. We do not know
@@ -442,10 +522,39 @@
 
         final Property otherProperty = (Property)other;
 
-        return getFlags()       == otherProperty.getFlags() &&
-               getSlot()        == otherProperty.getSlot() &&
-               getCurrentType() == otherProperty.getCurrentType() &&
-               getKey().equals(otherProperty.getKey());
+        return equalsWithoutType(otherProperty) &&
+               getCurrentType() == otherProperty.getCurrentType();
+    }
+
+    boolean equalsWithoutType(final Property otherProperty) {
+        return getFlags() == otherProperty.getFlags() &&
+                getSlot() == otherProperty.getSlot() &&
+                getKey().equals(otherProperty.getKey());
+    }
+
+    private static final String type(final Class<?> type) {
+        if (type == null) {
+            return "undef";
+        } else if (type == int.class) {
+            return "i";
+        } else if (type == long.class) {
+            return "j";
+        } else if (type == double.class) {
+            return "d";
+        } else {
+            return "o";
+        }
+    }
+
+    /**
+     * Short toString version
+     * @return short toString
+     */
+    public final String toStringShort() {
+        final StringBuilder sb   = new StringBuilder();
+        final Class<?>      type = getCurrentType();
+        sb.append(getKey()).append(" (").append(type(type)).append(')');
+        return sb.toString();
     }
 
     @Override
@@ -454,18 +563,21 @@
         final Class<?>      type = getCurrentType();
 
         sb.append(getKey()).
-            append("(0x").
+            append(" id=").
+            append(Debug.id(this)).
+            append(" (0x").
             append(Integer.toHexString(flags)).
             append(") ").
             append(getClass().getSimpleName()).
             append(" {").
-            append(type == null ? "UNDEFINED" : Type.typeFor(type).getDescriptor()).
+            append(type(type)).
             append('}');
 
         if (slot != -1) {
-            sb.append('[');
-            sb.append(slot);
-            sb.append(']');
+            sb.append('[').
+               append("slot=").
+               append(slot).
+               append(']');
         }
 
         return sb.toString();
@@ -479,9 +591,13 @@
      *
      * @return current type of property, null means undefined
      */
-    public Class<?> getCurrentType() {
-        return Object.class;
-    }
+    public abstract Class<?> getCurrentType();
+
+    /**
+     * Reset the current type of this property
+     * @param currentType new current type
+     */
+    public abstract void setCurrentType(final Class<?> currentType);
 
     /**
      * Check whether this Property can ever change its type. The default is false, and if
@@ -505,17 +621,6 @@
 
     /**
      * Check whether this property can be primitive. This is a conservative
-     * analysis result, so {@code false} might mean that it can still be
-     * primitive
-     *
-     * @return can be primitive status
-     */
-    public boolean canBePrimitive() {
-        return (flags & CAN_BE_PRIMITIVE) == CAN_BE_PRIMITIVE;
-    }
-
-    /**
-     * Check whether this property can be primitive. This is a conservative
      * analysis result, so {@code true} might mean that it can still be
      * defined, but it will never say that a property can not be undefined
      * if it can
--- a/nashorn/src/jdk/nashorn/internal/runtime/PropertyAccess.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/PropertyAccess.java	Wed Feb 26 13:17:57 2014 +0100
@@ -38,86 +38,98 @@
     /**
      * Get the value for a given key and return it as an int
      * @param key the key
+     * @param programPoint or INVALID_PROGRAM_POINT if pessimistic
      * @return the value
      */
-    public int getInt(Object key);
+    public int getInt(Object key, int programPoint);
 
     /**
      * Get the value for a given key and return it as an int
      * @param key the key
+     * @param programPoint or INVALID_PROGRAM_POINT if pessimistic
      * @return the value
      */
-    public int getInt(double key);
+    public int getInt(double key, int programPoint);
 
     /**
      * Get the value for a given key and return it as an int
      * @param key the key
+     * @param programPoint or INVALID_PROGRAM_POINT if pessimistic
      * @return the value
      */
-    public int getInt(final long key);
+    public int getInt(long key, int programPoint);
 
     /**
      * Get the value for a given key and return it as an int
      * @param key the key
+     * @param programPoint or INVALID_PROGRAM_POINT if pessimistic
      * @return the value
      */
-    public int getInt(int key);
+    public int getInt(int key, int programPoint);
 
     /**
      * Get the value for a given key and return it as a long
      * @param key the key
+     * @param programPoint or INVALID_PROGRAM_POINT if pessimistic
      * @return the value
      */
-    public long getLong(Object key);
+    public long getLong(Object key, int programPoint);
 
     /**
      * Get the value for a given key and return it as a long
      * @param key the key
+     * @param programPoint or INVALID_PROGRAM_POINT if pessimistic
      * @return the value
      */
-    public long getLong(double key);
+    public long getLong(double key, int programPoint);
 
     /**
      * Get the value for a given key and return it as a long
      * @param key the key
+     * @param programPoint or INVALID_PROGRAM_POINT if pessimistic
      * @return the value
      */
-    public long getLong(long key);
+    public long getLong(long key, int programPoint);
 
     /**
      * Get the value for a given key and return it as a long
      * @param key the key
+     * @param programPoint or INVALID_PROGRAM_POINT if pessimistic
      * @return the value
      */
-    public long getLong(int key);
+    public long getLong(int key, int programPoint);
 
     /**
      * Get the value for a given key and return it as a double
      * @param key the key
+     * @param programPoint or INVALID_PROGRAM_POINT if pessimistic
      * @return the value
      */
-    public double getDouble(Object key);
+    public double getDouble(Object key, int programPoint);
 
     /**
      * Get the value for a given key and return it as a double
      * @param key the key
+     * @param programPoint or INVALID_PROGRAM_POINT if pessimistic
      * @return the value
      */
-    public double getDouble(double key);
+    public double getDouble(double key, int programPoint);
 
     /**
      * Get the value for a given key and return it as a double
      * @param key the key
+     * @param programPoint or INVALID_PROGRAM_POINT if pessimistic
      * @return the value
      */
-    public double getDouble(long key);
+    public double getDouble(long key, int programPoint);
 
     /**
      * Get the value for a given key and return it as a double
      * @param key the key
+     * @param programPoint or INVALID_PROGRAM_POINT if pessimistic
      * @return the value
      */
-    public double getDouble(int key);
+    public double getDouble(int key, int programPoint);
 
     /**
      * Get the value for a given key and return it as an Object
--- a/nashorn/src/jdk/nashorn/internal/runtime/PropertyHashMap.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/PropertyHashMap.java	Wed Feb 26 13:17:57 2014 +0100
@@ -164,6 +164,20 @@
     }
 
     /**
+     * Clone a property map, replacing a property with a new one in the same place,
+     * which is important for property iterations if a property changes types
+     * @param property    old property
+     * @param newProperty new property
+     * @return new property map
+     */
+    public PropertyHashMap immutableReplace(final Property property, final Property newProperty) {
+        assert property.getKey().equals(newProperty.getKey()) : "replacing properties with different keys: '" + property.getKey() + "' != '" + newProperty.getKey() + "'";
+        assert property.hashCode() != newProperty.hashCode()  : "replacing identical properties: '" + property + "'";
+        assert findElement(property.getKey()) != null         : "replacing property that doesn't exist in map: '" + property.getKey() + "'";
+        return cloneMap().replaceNoClone(property.getKey(), newProperty);
+    }
+
+    /**
      * Clone a {@link PropertyHashMap} and add a {@link Property}.
      *
      * @param property {@link Property} to add.
@@ -327,8 +341,9 @@
         final Element[] newBins = new Element[binSize];
         for (Element element = list; element != null; element = element.getLink()) {
             final Property property = element.getProperty();
-            final String key = property.getKey();
-            final int binIndex = binIndex(newBins, key);
+            final String   key      = property.getKey();
+            final int      binIndex = binIndex(newBins, key);
+
             newBins[binIndex] = new Element(newBins[binIndex], property);
         }
         return newBins;
@@ -366,6 +381,11 @@
         return null;
     }
 
+
+    private PropertyHashMap cloneMap() {
+        return new PropertyHashMap(size, bins == null ? null : bins.clone(), list);
+    }
+
     /**
      * Clone {@link PropertyHashMap} to accommodate new size.
      *
@@ -385,6 +405,8 @@
         return new PropertyHashMap(newSize, newBins, list);
     }
 
+
+
     /**
      * Add a {@link Property} to a temporary {@link PropertyHashMap}, that has
      * been already cloned.  Removes duplicates if necessary.
@@ -416,6 +438,22 @@
         return new PropertyHashMap(newSize, bins, newList);
     }
 
+    private PropertyHashMap replaceNoClone(final String key, final Property property) {
+        if (bins != null) {
+            final int binIndex = binIndex(bins, key);
+            Element bin = bins[binIndex];
+    //        System.err.println("oldBin = " + bin);
+            bin = replaceInList(bin, key, property);
+    //        System.err.println("newBin = " + bin);
+            bins[binIndex] = bin;
+        }
+        Element newList = list;
+        //System.err.println("oldList = " + newList);
+        newList = replaceInList(newList, key, property);
+        //System.err.println("newList = " + newList);
+        return new PropertyHashMap(size, bins, newList);
+    }
+
     /**
      * Removes an {@link Element} from a specific list, avoiding duplication.
      *
@@ -446,6 +484,27 @@
         return list;
     }
 
+    // for element x. if x get link matches,
+    private static Element replaceInList(final Element list, final String key, final Property property) {
+        assert list != null;
+        final int hashCode = key.hashCode();
+
+        if (list.match(key, hashCode)) {
+            return new Element(list.getLink(), property);
+        }
+
+        Element previous = list;
+        for (Element element = list.getLink(); element != null; element = element.getLink()) {
+            if (element.match(key, hashCode)) {
+                previous.setLink(new Element(element.getLink(), property));
+                return list;
+            }
+            previous = element;
+        }
+        return list;
+    }
+
+
     /*
      * Map implementation
      */
@@ -617,6 +676,26 @@
             throw new UnsupportedOperationException("Immutable map.");
         }
 
+        @Override
+        public String toString() {
+            final StringBuffer sb = new StringBuffer();
+
+            sb.append('[');
+
+            Element elem = this;
+            do {
+                sb.append(elem.getValue().toStringShort());
+                elem = elem.link;
+                if (elem != null) {
+                    sb.append(" -> ");
+                }
+            } while (elem != null);
+
+            sb.append(']');
+
+            return sb.toString();
+        }
+
         /*
          * Accessors
          */
--- a/nashorn/src/jdk/nashorn/internal/runtime/PropertyMap.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/PropertyMap.java	Wed Feb 26 13:17:57 2014 +0100
@@ -297,7 +297,7 @@
             newMap = new PropertyMap(this, newProperties);
             addToHistory(property, newMap);
 
-            if(!property.isSpill()) {
+            if (!property.isSpill()) {
                 newMap.fieldCount = Math.max(newMap.fieldCount, property.getSlot() + 1);
             }
             if (isValidArrayIndex(getArrayIndex(property.getKey()))) {
@@ -339,10 +339,8 @@
      * @return New {@link PropertyMap} with {@link Property} replaced.
      */
     PropertyMap replaceProperty(final Property oldProperty, final Property newProperty) {
-        // Add replaces existing property.
-        final PropertyHashMap newProperties = properties.immutableAdd(newProperty);
+        final PropertyHashMap newProperties = properties.immutableReplace(oldProperty, newProperty);
         final PropertyMap newMap = new PropertyMap(this, newProperties);
-
         /*
          * See ScriptObject.modifyProperty and ScriptObject.setUserAccessors methods.
          *
@@ -361,7 +359,8 @@
         final boolean sameType = (oldProperty.getClass() == newProperty.getClass());
         assert sameType ||
                 (oldProperty instanceof AccessorProperty &&
-                newProperty instanceof UserAccessorProperty) : "arbitrary replaceProperty attempted";
+                newProperty instanceof UserAccessorProperty) :
+            "arbitrary replaceProperty attempted " + sameType + " oldProperty=" + oldProperty.getClass() + " newProperty=" + newProperty.getClass() + " [" + oldProperty.getCurrentType() + " => " + newProperty.getCurrentType() + "]";
 
         newMap.flags = getClonedFlags();
 
@@ -369,7 +368,7 @@
          * spillLength remains same in case (1) and (2) because of slot reuse. Only for case (3), we need
          * to add spill count of the newly added UserAccessorProperty property.
          */
-        newMap.spillLength = spillLength + (sameType? 0 : newProperty.getSpillCount());
+        newMap.spillLength = spillLength;
         return newMap;
     }
 
@@ -384,12 +383,7 @@
      * @return the newly created UserAccessorProperty
      */
     public UserAccessorProperty newUserAccessors(final String key, final int propertyFlags) {
-        int oldSpillLength = spillLength;
-
-        final int getterSlot = oldSpillLength++;
-        final int setterSlot = oldSpillLength++;
-
-        return new UserAccessorProperty(key, propertyFlags, getterSlot, setterSlot);
+        return new UserAccessorProperty(key, propertyFlags, spillLength);
     }
 
     /**
@@ -676,6 +670,30 @@
         return true;
     }
 
+    /**
+     * Returns true if the two maps have identical properties in the same order, but allows the properties to differ in
+     * their types. This method is mostly useful for tests.
+     * @param otherMap the other map
+     * @return true if this map has identical properties in the same order as the other map, allowing the properties to
+     * differ in type.
+     */
+    public boolean equalsWithoutType(final PropertyMap otherMap) {
+        if (properties.size() != otherMap.properties.size()) {
+            return false;
+        }
+
+        final Iterator<Property> iter      = properties.values().iterator();
+        final Iterator<Property> otherIter = otherMap.properties.values().iterator();
+
+        while (iter.hasNext() && otherIter.hasNext()) {
+            if (!iter.next().equalsWithoutType(otherIter.next())) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
@@ -689,16 +707,7 @@
             }
 
             isFirst = false;
-
-            sb.append(ScriptRuntime.safeToString(property.getKey()));
-            final Class<?> ctype = property.getCurrentType();
-            sb.append(" <").
-                append(property.getClass().getSimpleName()).
-                append(':').
-                append(ctype == null ?
-                    "undefined" :
-                    ctype.getSimpleName()).
-                append('>');
+            sb.append(property);
         }
 
         sb.append(']');
@@ -931,6 +940,57 @@
      * Debugging and statistics.
      */
 
+    /**
+     * Debug helper function that returns the diff of two property maps, only
+     * displaying the information that is different and in which map it exists
+     * compared to the other map. Can be used to e.g. debug map guards and
+     * investigate why they fail, causing relink
+     *
+     * @param map0 the first property map
+     * @param map1 the second property map
+     *
+     * @return property map diff as string
+     */
+    public static String diff(final PropertyMap map0, final PropertyMap map1) {
+        final StringBuilder sb = new StringBuilder();
+
+        if (map0 != map1) {
+           sb.append(">>> START: Map diff");
+           boolean found = false;
+
+           for (final Property p : map0.getProperties()) {
+               final Property p2 = map1.findProperty(p.getKey());
+               if (p2 == null) {
+                   sb.append("FIRST ONLY : [" + p + "]");
+                   found = true;
+               } else if (p2 != p) {
+                   sb.append("DIFFERENT  : [" + p + "] != [" + p2 + "]");
+                   found = true;
+               }
+           }
+
+           for (final Property p2 : map1.getProperties()) {
+               final Property p1 = map0.findProperty(p2.getKey());
+               if (p1 == null) {
+                   sb.append("SECOND ONLY: [" + p2 + "]");
+                   found = true;
+               }
+           }
+
+           //assert found;
+
+           if (!found) {
+                sb.append(map0).
+                    append("!=").
+                    append(map1);
+           }
+
+           sb.append("<<< END: Map diff\n");
+        }
+
+        return sb.toString();
+    }
+
     // counters updated only in debug mode
     private static int count;
     private static int clonedCount;
@@ -1003,4 +1063,5 @@
     private static void incrementSetProtoNewMapCount() {
         setProtoNewMapCount++;
     }
+
 }
--- a/nashorn/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java	Wed Feb 26 13:17:57 2014 +0100
@@ -30,19 +30,28 @@
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.LinkedList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
 import jdk.internal.dynalink.support.NameCodec;
-
+import jdk.nashorn.internal.codegen.CompilationEnvironment;
+import jdk.nashorn.internal.codegen.CompilationEnvironment.CompilationPhases;
+import jdk.nashorn.internal.codegen.CompileUnit;
 import jdk.nashorn.internal.codegen.Compiler;
 import jdk.nashorn.internal.codegen.CompilerConstants;
 import jdk.nashorn.internal.codegen.FunctionSignature;
+import jdk.nashorn.internal.codegen.ParamTypeMap;
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.FunctionNode;
-import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
+import jdk.nashorn.internal.ir.LexicalContext;
+import jdk.nashorn.internal.ir.visitor.NodeVisitor;
+import jdk.nashorn.internal.parser.Parser;
 import jdk.nashorn.internal.parser.Token;
 import jdk.nashorn.internal.parser.TokenType;
+import jdk.nashorn.internal.runtime.options.Options;
 
 /**
  * This is a subclass that represents a script function that may be regenerated,
@@ -51,9 +60,23 @@
  * unlike {@code FinalScriptFunctionData}
  */
 public final class RecompilableScriptFunctionData extends ScriptFunctionData {
+    /** Is lazy compilation enabled? TODO: this should be the default */
+    public static final boolean LAZY_COMPILATION = Options.getBooleanProperty("nashorn.lazy");
 
-    /** FunctionNode with the code for this ScriptFunction */
-    private FunctionNode functionNode;
+    /** Prefix used for all recompiled script classes */
+    public static final String RECOMPILATION_PREFIX = "Script$Recompilation$";
+
+    /** Unique function node id for this function node */
+    private final int functionNodeId;
+
+    private final String functionName;
+
+    // TODO: try to eliminate the need for this somehow, either by allowing Source to change its name, allowing a
+    // function to internally replace its Source with one of a different name, or storing this additional field in the
+    // Source object.
+    private final String sourceURL;
+
+    private final int lineNumber;
 
     /** Source from which FunctionNode was parsed. */
     private final Source source;
@@ -65,7 +88,7 @@
     private final PropertyMap allocatorMap;
 
     /** Code installer used for all further recompilation/specialization of this ScriptFunction */
-    private CodeInstaller<ScriptEnvironment> installer;
+    private final CodeInstaller<ScriptEnvironment> installer;
 
     /** Name of class where allocator function resides */
     private final String allocatorClassName;
@@ -73,24 +96,19 @@
     /** lazily generated allocator */
     private MethodHandle allocator;
 
+    private Map<Integer, RecompilableScriptFunctionData> nestedFunctions;
+
+    private final boolean isDeclared;
+    private final boolean isAnonymous;
+    private final boolean needsCallee;
+
     private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
 
-    /**
-     * Used for specialization based on runtime arguments. Whenever we specialize on
-     * callsite parameter types at runtime, we need to use a parameter type guard to
-     * ensure that the specialized version of the script function continues to be
-     * applicable for a particular callsite *
-     */
-    private static final MethodHandle PARAM_TYPE_GUARD = findOwnMH("paramTypeGuard", boolean.class, Type[].class,  Object[].class);
+    /** Unique id for classes needed to wrap recompiled script functions */
+    private static final AtomicInteger RECOMPILE_ID = new AtomicInteger(0);
 
-    /**
-     * It is usually a good gamble whever we detect a runtime callsite with a double
-     * (or java.lang.Number instance) to specialize the parameter to an integer, if the
-     * parameter in question can be represented as one. The double typically only exists
-     * because the compiler doesn't know any better than "a number type" and conservatively
-     * picks doubles when it can't prove that an integer addition wouldn't overflow
-     */
-    private static final MethodHandle ENSURE_INT = findOwnMH("ensureInt", int.class, Object.class);
+    private static final DebugLogger LOG = new DebugLogger("recompile");
+
 
     /**
      * Constructor - public as scripts use it
@@ -99,20 +117,35 @@
      * @param installer          installer for code regeneration versions of this function
      * @param allocatorClassName name of our allocator class, will be looked up dynamically if used as a constructor
      * @param allocatorMap       allocator map to seed instances with, when constructing
+     * @param nestedFunctions    nested function map
      */
-    public RecompilableScriptFunctionData(final FunctionNode functionNode, final CodeInstaller<ScriptEnvironment> installer, final String allocatorClassName, final PropertyMap allocatorMap) {
+    public RecompilableScriptFunctionData(
+        final FunctionNode functionNode,
+        final CodeInstaller<ScriptEnvironment> installer,
+        final String allocatorClassName,
+        final PropertyMap allocatorMap,
+        final Map<Integer, RecompilableScriptFunctionData> nestedFunctions,
+        final String sourceURL) {
         super(functionName(functionNode),
-              functionNode.getParameters().size(),
+              Math.min(functionNode.getParameters().size(), MAX_ARITY),
               functionNode.isStrict(),
               false,
-              true);
+              true,
+              functionNode.isVarArg());
 
-        this.functionNode       = functionNode;
+        this.functionName       = functionNode.getName();
+        this.lineNumber         = functionNode.getLineNumber();
+        this.isDeclared         = functionNode.isDeclared();
+        this.needsCallee        = functionNode.needsCallee();
+        this.isAnonymous        = functionNode.isAnonymous();
+        this.functionNodeId     = functionNode.getId();
         this.source             = functionNode.getSource();
         this.token              = tokenFor(functionNode);
         this.installer          = installer;
+        this.sourceURL          = sourceURL;
         this.allocatorClassName = allocatorClassName;
         this.allocatorMap       = allocatorMap;
+        this.nestedFunctions    = nestedFunctions;
     }
 
     @Override
@@ -131,7 +164,7 @@
         if (source != null) {
             sb.append(source.getName())
                 .append(':')
-                .append(functionNode.getLineNumber())
+                .append(lineNumber)
                 .append(' ');
         }
 
@@ -141,20 +174,20 @@
     private static String functionName(final FunctionNode fn) {
         if (fn.isAnonymous()) {
             return "";
-        } else {
-            final FunctionNode.Kind kind = fn.getKind();
-            if (kind == FunctionNode.Kind.GETTER || kind == FunctionNode.Kind.SETTER) {
-                final String name = NameCodec.decode(fn.getIdent().getName());
-                return name.substring(4); // 4 is "get " or "set "
-            } else {
-                return fn.getIdent().getName();
-            }
         }
+        final FunctionNode.Kind kind = fn.getKind();
+        if (kind == FunctionNode.Kind.GETTER || kind == FunctionNode.Kind.SETTER) {
+            final String name = NameCodec.decode(fn.getIdent().getName());
+            return name.substring(4); // 4 is "get " or "set "
+        }
+        return fn.getIdent().getName();
     }
 
     private static long tokenFor(final FunctionNode fn) {
-        final int  position   = Token.descPosition(fn.getFirstToken());
-        final int  length     = Token.descPosition(fn.getLastToken()) - position + Token.descLength(fn.getLastToken());
+        final int  position  = Token.descPosition(fn.getFirstToken());
+        final long lastToken = fn.getLastToken();
+        // EOL uses length field to store the line number
+        final int  length    = Token.descPosition(lastToken) - position + (Token.descType(lastToken) == TokenType.EOL ? 0 : Token.descLength(lastToken));
 
         return Token.toDesc(TokenType.FUNCTION, position, length);
     }
@@ -177,259 +210,271 @@
         }
     }
 
-    @Override
-    protected synchronized void ensureCodeGenerated() {
-         if (!code.isEmpty()) {
-             return; // nothing to do, we have code, at least some.
-         }
-
-         if (functionNode.isLazy()) {
-             Compiler.LOG.info("Trampoline hit: need to do lazy compilation of '", functionNode.getName(), "'");
-             final Compiler compiler = new Compiler(installer);
-             functionNode = compiler.compile(functionNode);
-             assert !functionNode.isLazy();
-             compiler.install(functionNode);
+    private FunctionNode reparse(final String scriptName) {
+        final boolean isProgram = functionNodeId == FunctionNode.FIRST_FUNCTION_ID;
+        // NOTE: If we aren't recompiling the top-level program, we decrease functionNodeId 'cause we'll have a synthetic program node
+        final int descPosition = Token.descPosition(token);
+        final Parser parser = new Parser(
+            installer.getOwner(),
+            source,
+            new Context.ThrowErrorManager(),
+            isStrict(),
+            functionNodeId - (isProgram ? 0 : 1),
+            lineNumber - 1); // source starts at line 0, so even though lineNumber is the correct declaration line, back off one to make it exclusive
 
-             /*
-              * We don't need to update any flags - varArgs and needsCallee are instrincic
-              * in the function world we need to get a destination node from the compile instead
-              * and replace it with our function node. TODO
-              */
-         }
+        if(isAnonymous) {
+            parser.setFunctionName(functionName);
+        }
+        final FunctionNode program = parser.parse(scriptName, descPosition, Token.descLength(token), true);
+        // Parser generates a program AST even if we're recompiling a single function, so when we are only recompiling a
+        // single function, extract it from the program.
+        return (isProgram ? program : extractFunctionFromScript(program)).setName(null, functionName).setSourceURL(null,  sourceURL);
+    }
 
-         /*
-          * We can't get to this program point unless we have bytecode, either from
-          * eager compilation or from running a lazy compile on the lines above
-          */
-
-         assert functionNode.hasState(CompilationState.EMITTED) : functionNode.getName() + " " + functionNode.getState() + " " + Debug.id(functionNode);
-
-         // code exists - look it up and add it into the automatically sorted invoker list
-         addCode(functionNode);
-
-         if (! functionNode.canSpecialize()) {
-             // allow GC to claim IR stuff that is not needed anymore
-             functionNode = null;
-             installer = null;
-         }
+    private static String stringifyInvalidations(final Map<Integer, Type> ipp) {
+        if (ipp == null) {
+            return "";
+        }
+        final StringBuilder sb = new StringBuilder();
+        final Iterator<Map.Entry<Integer, Type>> iter = ipp.entrySet().iterator();
+        while (iter.hasNext()) {
+            final Map.Entry<Integer, Type> entry = iter.next();
+            sb.append('[').
+                    append(entry.getKey()).
+                    append("=>").
+                    append(entry.getValue().getShortDescriptor()).
+                    append(']');
+            if (iter.hasNext()) {
+                sb.append(' ');
+            }
+        }
+        return sb.toString();
     }
 
-    private MethodHandle addCode(final FunctionNode fn) {
-        return addCode(fn, null, null, null);
+    MethodHandle compileRestOfMethod(final MethodType fnCallSiteType, final Map<Integer, Type> invalidatedProgramPoints, final int[] continuationEntryPoints) {
+        LOG.info("Rest-of compilation of '", functionName, "' signature: ", fnCallSiteType, " ", stringifyInvalidations(invalidatedProgramPoints));
+
+        final String scriptName = RECOMPILATION_PREFIX + RECOMPILE_ID.incrementAndGet() + "$restOf";
+        FunctionNode fn = reparse(scriptName);
+
+        final Compiler compiler = new Compiler(
+                new CompilationEnvironment(
+                    CompilationPhases.EAGER.makeOptimistic(),
+                    isStrict(),
+                    this,
+                    isVariableArity() ? null : new ParamTypeMap(functionNodeId, explicitParams(fnCallSiteType)),
+                    invalidatedProgramPoints,
+                    continuationEntryPoints
+                    ),
+                installer);
+
+        fn = compiler.compile(scriptName, fn);
+
+        compiler.install(fn);
+
+        return lookupWithExplicitType(fn, MethodType.methodType(fn.getReturnType().getTypeClass(), RewriteException.class));
+    }
+
+    private FunctionNode compileTypeSpecialization(MethodType actualCallSiteType) {
+        return compile(actualCallSiteType, null, "Type specialized compilation");
+    }
+
+    FunctionNode compile(final MethodType actualCallSiteType, final Map<Integer, Type> invalidatedProgramPoints, final String reason) {
+        final String scriptName = RECOMPILATION_PREFIX + RECOMPILE_ID.incrementAndGet();
+        final MethodType fnCallSiteType = actualCallSiteType == null ? null : actualCallSiteType.changeParameterType(0, ScriptFunction.class);
+        LOG.info(reason, " of '", functionName, "' signature: ", fnCallSiteType, " ", stringifyInvalidations(invalidatedProgramPoints));
+        FunctionNode fn = reparse(scriptName);
+
+        final CompilationPhases phases = CompilationPhases.EAGER;
+        final Compiler compiler = new Compiler(
+            new CompilationEnvironment(
+                phases.makeOptimistic(ScriptEnvironment.globalOptimistic()),
+                isStrict(),
+                this,
+                fnCallSiteType == null || isVariableArity() ?
+                    null :
+                    new ParamTypeMap(
+                        functionNodeId,
+                        explicitParams(fnCallSiteType)),
+                invalidatedProgramPoints),
+            installer);
+
+        fn = compiler.compile(scriptName, fn);
+
+        compiler.install(fn);
+
+        return fn;
     }
 
-    private MethodHandle addCode(final FunctionNode fn, final MethodType runtimeType, final MethodHandle guard, final MethodHandle fallback) {
-        final MethodType targetType = new FunctionSignature(fn).getMethodType();
-        MethodHandle target =
-            MH.findStatic(
-                    LOOKUP,
-                    fn.getCompileUnit().getCode(),
-                    fn.getName(),
-                    targetType);
+    private MethodType explicitParams(final MethodType callSiteType) {
+        assert !isVariableArity(); // Should not be invoked for varargs
+        final int arity = getArity();
+
+        final MethodType noCalleeThisType = callSiteType.dropParameterTypes(0, 2); // (callee, this) is always in call site type
+        final int callSiteParamCount = noCalleeThisType.parameterCount();
 
-        /*
-         * For any integer argument. a double that is representable as an integer is OK.
-         * otherwise the guard would have failed. in that case introduce a filter that
-         * casts the double to an integer, which we know will preserve all precision.
-         */
-        for (int i = 0; i < targetType.parameterCount(); i++) {
-            if (targetType.parameterType(i) == int.class) {
-                //representable as int
-                target = MH.filterArguments(target, i, ENSURE_INT);
+        // Widen parameters of reference types to Object as we currently don't care for specialization among reference
+        // types. E.g. call site saying (ScriptFunction, Object, String) should still link to (ScriptFunction, Object, Object)
+        final int minParams = Math.min(callSiteParamCount, arity);
+        final Class<?>[] paramTypes = noCalleeThisType.parameterArray();
+        boolean changed = false;
+        for(int i = 0; i < minParams; ++i) {
+            final Class<?> paramType = paramTypes[i];
+            if(!(paramType.isPrimitive() || paramType == Object.class)) {
+                paramTypes[i] = Object.class;
+                changed = true;
             }
         }
+        final MethodType generalized = changed ? MethodType.methodType(noCalleeThisType.returnType(), paramTypes) : noCalleeThisType;
 
-        MethodHandle mh = target;
-        if (guard != null) {
-            mh = MH.guardWithTest(MH.asCollector(guard, Object[].class, target.type().parameterCount()), MH.asType(target, fallback.type()), fallback);
+        // Match arity
+        if(callSiteParamCount < arity) {
+            return generalized.appendParameterTypes(Collections.<Class<?>>nCopies(arity - callSiteParamCount, Object.class));
+        } else if(callSiteParamCount > arity) {
+            return generalized.dropParameterTypes(arity, callSiteParamCount);
+        } else {
+            return generalized;
         }
+    }
 
-        final CompiledFunction cf = new CompiledFunction(runtimeType == null ? targetType : runtimeType, mh);
-        code.add(cf);
+    private FunctionNode extractFunctionFromScript(final FunctionNode script) {
+        final Set<FunctionNode> fns = new HashSet<>();
+        script.getBody().accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
+            @Override
+            public boolean enterFunctionNode(final FunctionNode fn) {
+                fns.add(fn);
+                return false;
+            }
+        });
+        assert fns.size() == 1 : "got back more than one method in recompilation";
+        final FunctionNode f = fns.iterator().next();
+        assert f.getId() == functionNodeId;
+        if(!isDeclared && f.isDeclared()) {
+            return f.clearFlag(null, FunctionNode.IS_DECLARED);
+        }
+        return f;
+    }
 
-        return cf.getInvoker();
+    MethodHandle lookup(final FunctionNode fn) {
+        return lookupWithExplicitType(fn, new FunctionSignature(fn).getMethodType());
+    }
+
+    private MethodHandle lookupWithExplicitType(final FunctionNode fn, final MethodType targetType) {
+        return lookupCodeMethod(fn.getCompileUnit(), targetType);
     }
 
-    private static Type runtimeType(final Object arg) {
-        if (arg == null) {
-            return Type.OBJECT;
+    private MethodHandle lookupCodeMethod(final CompileUnit compileUnit, final MethodType targetType) {
+        return MH.findStatic(LOOKUP, compileUnit.getCode(), functionName, targetType);
+    }
+
+    /**
+     * Initializes this function data with the eagerly generated version of the code. This method can only be invoked
+     * by the compiler internals in Nashorn and is public for implementation reasons only. Attempting to invoke it
+     * externally will result in an exception.
+     * @param functionNode the functionNode belonging to this data
+     */
+    public void initializeCode(final FunctionNode functionNode) {
+        // Since the method is public, we double-check that we aren't invoked with an inappropriate compile unit.
+        if(!(code.isEmpty() && functionNode.getCompileUnit().isInitializing(this, functionNode))) {
+            throw new IllegalStateException();
+        }
+        addCode(functionNode);
+    }
+
+    private CompiledFunction addCode(final MethodHandle target, final boolean isOptimistic) {
+        final CompiledFunction cfn = new CompiledFunction(target, this, isOptimistic);
+        code.add(cfn);
+        return cfn;
+    }
+
+    private CompiledFunction addCode(final FunctionNode fn) {
+        return addCode(lookup(fn), fn.isOptimistic());
+    }
+
+    /**
+     * Add code with specific call site type. It will adapt the type of the looked up method handle to fit the call site
+     * type. This is necessary because even if we request a specialization that takes an "int" parameter, we might end
+     * up getting one that takes a "double" etc. because of internal function logic causes widening (e.g. assignment of
+     * a wider value to the parameter variable). However, we use the method handle type for matching subsequent lookups
+     * for the same specialization, so we must adapt the handle to the expected type.
+     * @param fn the function
+     * @param callSiteType the call site type
+     * @return the compiled function object, with its type matching that of the call site type.
+     */
+    private CompiledFunction addCode(final FunctionNode fn, final MethodType callSiteType) {
+        if(fn.isVarArg()) {
+            return addCode(fn);
         }
 
-        final Class<?> clazz = arg.getClass();
-        assert !clazz.isPrimitive() : "always boxed";
-        if (clazz == Double.class) {
-            return JSType.isRepresentableAsInt((double)arg) ? Type.INT : Type.NUMBER;
-        } else if (clazz == Integer.class) {
-            return Type.INT;
-        } else if (clazz == Long.class) {
-            return Type.LONG;
-        } else if (clazz == String.class) {
-            return Type.STRING;
+        final MethodHandle handle = lookup(fn);
+        final MethodType fromType = handle.type();
+        MethodType toType = (needsCallee(fromType) ? callSiteType.changeParameterType(0, ScriptFunction.class) : callSiteType.dropParameterTypes(0, 1));
+        toType = toType.changeReturnType(fromType.returnType());
+
+        final int toCount = toType.parameterCount();
+        final int fromCount = fromType.parameterCount();
+        final int minCount = Math.min(fromCount, toCount);
+        for(int i = 0; i < minCount; ++i) {
+            final Class<?> fromParam = fromType.parameterType(i);
+            final Class<?>   toParam =   toType.parameterType(i);
+            // If method has an Object parameter, but call site had String, preserve it as Object. No need to narrow it
+            // artificially. Note that this is related to how CompiledFunction.matchesCallSite() works, specifically
+            // the fact that various reference types compare to equal (see "fnType.isEquivalentTo(csType)" there).
+            if(fromParam != toParam && !fromParam.isPrimitive() && !toParam.isPrimitive()) {
+                assert fromParam.isAssignableFrom(toParam);
+                toType = toType.changeParameterType(i, fromParam);
+            }
         }
-        return Type.OBJECT;
+        if(fromCount > toCount) {
+            toType = toType.appendParameterTypes(fromType.parameterList().subList(toCount, fromCount));
+        } else if(fromCount < toCount) {
+            toType = toType.dropParameterTypes(fromCount, toCount);
+        }
+
+        return addCode(lookup(fn).asType(toType), fn.isOptimistic());
     }
 
-    private static boolean canCoerce(final Object arg, final Type type) {
-        Type argType = runtimeType(arg);
-        if (Type.widest(argType, type) == type || arg == ScriptRuntime.UNDEFINED) {
-            return true;
+    @Override
+    CompiledFunction getBest(MethodType callSiteType) {
+        synchronized(code) {
+            final CompiledFunction existingBest = super.getBest(callSiteType);
+            // TODO: what if callSiteType is vararg?
+            return existingBest != null ? existingBest : addCode(compileTypeSpecialization(callSiteType), callSiteType);
         }
-        System.err.println(arg + " does not fit in "+ argType + " " + type + " " + arg.getClass());
-        new Throwable().printStackTrace();
-        return false;
     }
 
-    @SuppressWarnings("unused")
-    private static boolean paramTypeGuard(final Type[] paramTypes, final Object... args) {
-        final int length = args.length;
-        assert args.length >= paramTypes.length;
-
-        //i==start, skip the this, callee params etc
-        int start = args.length - paramTypes.length;
-        for (int i = start; i < args.length; i++) {
-            final Object arg = args[i];
-            if (!canCoerce(arg, paramTypes[i - start])) {
-                return false;
-            }
-        }
+    @Override
+    boolean isRecompilable() {
         return true;
     }
 
-    @SuppressWarnings("unused")
-    private static int ensureInt(final Object arg) {
-        if (arg instanceof Number) {
-            return ((Number)arg).intValue();
-        } else if (arg instanceof Undefined) {
-            return 0;
-        }
-        throw new AssertionError(arg);
-    }
-
-    /**
-     * Given the runtime callsite args, compute a method type that is equivalent to what
-     * was passed - this is typically a lot more specific that what the compiler has been
-     * able to deduce
-     * @param callSiteType callsite type for the compiled callsite target
-     * @param args runtime arguments to the compiled callsite target
-     * @return adjusted method type, narrowed as to conform to runtime callsite type instead
-     */
-    private static MethodType runtimeType(final MethodType callSiteType, final Object[] args) {
-        if (args == null) {
-            //for example bound, or otherwise runtime arguments to callsite unavailable, then
-            //do not change the type
-            return callSiteType;
-        }
-        final Class<?>[] paramTypes = new Class<?>[callSiteType.parameterCount()];
-        final int        start      = args.length - callSiteType.parameterCount();
-        for (int i = start; i < args.length; i++) {
-            paramTypes[i - start] = runtimeType(args[i]).getTypeClass();
-        }
-        return MH.type(callSiteType.returnType(), paramTypes);
-    }
-
-    private static ArrayList<Type> runtimeType(final MethodType mt) {
-        final ArrayList<Type> type = new ArrayList<>();
-        for (int i = 0; i < mt.parameterCount(); i++) {
-            type.add(Type.typeFor(mt.parameterType(i)));
-        }
-        return type;
+    @Override
+    boolean needsCallee() {
+        return needsCallee;
     }
 
     @Override
-    synchronized MethodHandle getBestInvoker(final MethodType callSiteType, final Object[] args) {
-        final MethodType runtimeType = runtimeType(callSiteType, args);
-        assert runtimeType.parameterCount() == callSiteType.parameterCount();
-
-        final MethodHandle mh = super.getBestInvoker(runtimeType, args);
-
-        /*
-         * Not all functions can be specialized, for example, if we deemed memory
-         * footprint too large to store a parse snapshot, or if it is meaningless
-         * to do so, such as e.g. for runScript
-         */
-        if (functionNode == null || !functionNode.canSpecialize()) {
-            return mh;
-        }
-
-        /*
-         * Check if best invoker is equally specific or more specific than runtime
-         * type. In that case, we don't need further specialization, but can use
-         * whatever we have already. We know that it will match callSiteType, or it
-         * would not have been returned from getBestInvoker
-         */
-        if (!code.isLessSpecificThan(runtimeType)) {
-            return mh;
-        }
-
-        int i;
-        final FunctionNode snapshot = functionNode.getSnapshot();
-        assert snapshot != null;
-
-        /*
-         * Create a list of the arg types that the compiler knows about
-         * typically, the runtime args are a lot more specific, and we should aggressively
-         * try to use those whenever possible
-         * We WILL try to make an aggressive guess as possible, and add guards if needed.
-         * For example, if the compiler can deduce that we have a number type, but the runtime
-         * passes and int, we might still want to keep it an int, and the gamble to
-         * check that whatever is passed is int representable usually pays off
-         * If the compiler only knows that a parameter is an "Object", it is still worth
-         * it to try to specialize it by looking at the runtime arg.
-         */
-        final LinkedList<Type> compileTimeArgs = new LinkedList<>();
-        for (i = callSiteType.parameterCount() - 1; i >= 0 && compileTimeArgs.size() < snapshot.getParameters().size(); i--) {
-            compileTimeArgs.addFirst(Type.typeFor(callSiteType.parameterType(i)));
+    MethodType getGenericType() {
+        // 2 is for (callee, this)
+        if (isVariableArity()) {
+            return MethodType.genericMethodType(2, true);
         }
-
-        /*
-         * The classes known at compile time are a safe to generate as primitives without parameter guards
-         * But the classes known at runtime (if more specific than compile time types) are safe to generate as primitives
-         * IFF there are parameter guards
-         */
-        MethodHandle guard = null;
-        final ArrayList<Type> runtimeParamTypes = runtimeType(runtimeType);
-        while (runtimeParamTypes.size() > functionNode.getParameters().size()) {
-            runtimeParamTypes.remove(0);
-        }
-        for (i = 0; i < compileTimeArgs.size(); i++) {
-            final Type rparam = Type.typeFor(runtimeType.parameterType(i));
-            final Type cparam = compileTimeArgs.get(i);
-
-            if (cparam.isObject() && !rparam.isObject()) {
-                //check that the runtime object is still coercible to the runtime type, because compiler can't prove it's always primitive
-                if (guard == null) {
-                    guard = MH.insertArguments(PARAM_TYPE_GUARD, 0, (Object)runtimeParamTypes.toArray(new Type[runtimeParamTypes.size()]));
-                }
-            }
-        }
-
-        Compiler.LOG.info("Callsite specialized ", name, " runtimeType=", runtimeType, " parameters=", snapshot.getParameters(), " args=", Arrays.asList(args));
-
-        assert snapshot != null;
-        assert snapshot != functionNode;
-
-        final Compiler compiler = new Compiler(installer);
-
-        final FunctionNode compiledSnapshot = compiler.compile(
-            snapshot.setHints(
-                null,
-                new Compiler.Hints(runtimeParamTypes.toArray(new Type[runtimeParamTypes.size()]))));
-
-        /*
-         * No matter how narrow your types were, they can never be narrower than Attr during recompile made them. I.e. you
-         * can put an int into the function here, if you see it as a runtime type, but if the function uses a multiplication
-         * on it, it will still need to be a double. At least until we have overflow checks. Similarly, if an int is
-         * passed but it is used as a string, it makes no sense to make the parameter narrower than Object. At least until
-         * the "different types for one symbol in difference places" work is done
-         */
-        compiler.install(compiledSnapshot);
-
-        return addCode(compiledSnapshot, runtimeType, guard, mh);
+        return MethodType.genericMethodType(2 + getArity());
     }
 
-    private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
-        return MH.findStatic(MethodHandles.lookup(), RecompilableScriptFunctionData.class, name, MH.type(rtype, types));
+    /**
+     * Return a script function data based on a function id, either this function if
+     * the id matches or a nested function based on functionId.
+     * @param functionId function id
+     * @return script function data or null if invalid id
+     */
+    public RecompilableScriptFunctionData getScriptFunctionData(final int functionId) {
+        if(functionId == functionNodeId) {
+            return this;
+        }
+        if(nestedFunctions == null) {
+            return null;
+        }
+        return nestedFunctions.get(functionId);
     }
-
 }
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/runtime/RewriteException.java	Wed Feb 26 13:17:57 2014 +0100
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.nashorn.internal.runtime;
+
+import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
+import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
+import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
+
+import java.lang.invoke.CallSite;
+import java.lang.invoke.ConstantCallSite;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.invoke.MethodType;
+import java.util.Arrays;
+import jdk.nashorn.internal.codegen.CompilerConstants.Call;
+import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.lookup.MethodHandleFactory;
+import jdk.nashorn.internal.lookup.MethodHandleFunctionality;
+
+/**
+ * Used to signal to the linker to relink the callee
+ */
+@SuppressWarnings("serial")
+public class RewriteException extends Exception {
+    private static final MethodHandleFunctionality MH = MethodHandleFactory.getFunctionality();
+
+    //contents of bytecode slots
+    private Object[] byteCodeSlots;
+    private final int[] previousContinuationEntryPoints;
+
+    /** Methodhandle for getting the contents of the bytecode slots in the exception */
+    public static final Call GET_BYTECODE_SLOTS       = virtualCallNoLookup(RewriteException.class, "getByteCodeSlots", Object[].class);
+    /** Methodhandle for getting the program point in the exception */
+    public static final Call GET_PROGRAM_POINT        = virtualCallNoLookup(RewriteException.class, "getProgramPoint", int.class);
+    /** Methodhandle for getting the return value for the exception */
+    public static final Call GET_RETURN_VALUE         = virtualCallNoLookup(RewriteException.class, "getReturnValueDestructive", Object.class);
+    /** Methodhandle for the populate array bootstrap */
+    public static final Call BOOTSTRAP                = staticCallNoLookup(RewriteException.class, "populateArrayBootstrap", CallSite.class, Lookup.class, String.class, MethodType.class, int.class);
+
+    /** Methodhandle for populating an array with local variable state */
+    private static final Call POPULATE_ARRAY           = staticCall(MethodHandles.lookup(), RewriteException.class, "populateArray", Object[].class, Object[].class, int.class, Object[].class);
+
+    /**
+     * Bootstrap method for populate array
+     * @param lookup     lookup
+     * @param name       name (ignored)
+     * @param type       method type for signature
+     * @param startIndex start index to start writing to
+     * @return callsite to array populator (constant)
+     */
+    public static CallSite populateArrayBootstrap(final MethodHandles.Lookup lookup, final String name, final MethodType type, final int startIndex) {
+        MethodHandle mh = POPULATE_ARRAY.methodHandle();
+        mh = MH.insertArguments(mh, 1, startIndex);
+        mh = MH.asCollector(mh, Object[].class, type.parameterCount() - 1);
+        mh = MH.asType(mh, type);
+        return new ConstantCallSite(mh);
+    }
+
+    /**
+     * Constructor for a rewrite exception thrown from an optimistic function.
+     * @param e the {@link UnwarrantedOptimismException} that triggered this exception.
+     * @param byteCodeSlots contents of local variable slots at the time of rewrite at the program point
+     */
+    public RewriteException(final UnwarrantedOptimismException e, final Object[] byteCodeSlots) {
+        this(e, byteCodeSlots, null);
+    }
+
+    /**
+     * Constructor for a rewrite exception thrown from a rest-of method.
+     * @param e the {@link UnwarrantedOptimismException} that triggered this exception.
+     * @param byteCodeSlots contents of local variable slots at the time of rewrite at the program point
+     * @param previousContinuationEntryPoints an array of continuation entry points that were already executed during
+     * one logical invocation of the function (a rest-of triggering a rest-of triggering a...)
+     */
+    public RewriteException(final UnwarrantedOptimismException e, final Object[] byteCodeSlots, final int[] previousContinuationEntryPoints) {
+        super("", e, false, Context.DEBUG);
+        this.byteCodeSlots = byteCodeSlots;
+        this.previousContinuationEntryPoints = previousContinuationEntryPoints;
+    }
+
+    /**
+     * Array populator used for saving the local variable state into the array contained in the
+     * RewriteException
+     * @param arrayToBePopluated array to be populated
+     * @param startIndex start index to write to
+     * @param items items with which to populate the array
+     * @return the populated array - same array object
+     */
+    public static Object[] populateArray(final Object[] arrayToBePopluated, final int startIndex, final Object[] items) {
+        System.arraycopy(items, 0, arrayToBePopluated, startIndex, items.length);
+        return arrayToBePopluated;
+    }
+
+    private UnwarrantedOptimismException getUOE() {
+        return (UnwarrantedOptimismException)getCause();
+    }
+    /**
+     * Get return value. This method is destructive, after it is invoked subsequent invocation of either
+     * {@link #getByteCodeSlots()} or this method will return null. This method is invoked from the generated
+     * continuation code as the last step before continuing the execution, and we need to make sure we don't hang on to
+     * either the entry bytecode slot values or the return value and prevent them from being garbage collected.
+     * @return return value
+     */
+    public Object getReturnValueDestructive() {
+        assert byteCodeSlots != null;
+        byteCodeSlots = null;
+        return getUOE().getReturnValueDestructive();
+    }
+
+    private Object getReturnValueNonDestructive() {
+        return getUOE().getReturnValueNonDestructive();
+    }
+    /**
+     * Get return type
+     * @return return type
+     */
+    public Type getReturnType() {
+        return getUOE().getReturnType();
+    }
+
+    /**
+     * Get the program point.
+     * @return program point.
+     */
+    public int getProgramPoint() {
+        return getUOE().getProgramPoint();
+    }
+
+    /**
+     * Get the bytecode slot contents.
+     * @return bytecode slot contents.
+     */
+    public Object[] getByteCodeSlots() {
+        return byteCodeSlots;
+    }
+
+    /**
+     * @return an array of continuation entry points that were already executed during one logical invocation of the
+     * function (a rest-of triggering a rest-of triggering a...)
+     */
+    public int[] getPreviousContinuationEntryPoints() {
+        return previousContinuationEntryPoints;
+    }
+
+    private static String stringify(final Object returnValue) {
+        if (returnValue == null) {
+            return "null";
+        }
+        final String str = returnValue.toString();
+        return returnValue instanceof Long ? (str + 'L') : str;
+    }
+
+    @Override
+    public String getMessage() {
+        return "programPoint=" + getProgramPoint() + " slots=" + Arrays.asList(byteCodeSlots) + ", returnValue=" + stringify(getReturnValueNonDestructive()) + ", returnType=" + getReturnType();
+    }
+
+    /**
+     * Short toString function for message
+     * @return short message
+     */
+    public String getMessageShort() {
+        return "[programPoint=" + getProgramPoint() + " returnType=" + getReturnType() + " (" + stringify(getReturnValueNonDestructive()) + ")]";
+    }
+
+}
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java	Wed Feb 26 13:17:57 2014 +0100
@@ -32,7 +32,6 @@
 import java.util.Set;
 import java.util.StringTokenizer;
 import java.util.TimeZone;
-
 import jdk.nashorn.internal.codegen.Namespace;
 import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
 import jdk.nashorn.internal.runtime.options.KeyValueOption;
@@ -89,6 +88,9 @@
     /** Use single Global instance per jsr223 engine instance. */
     public final boolean _global_per_engine;
 
+    /** Argument passed to compile only if optimistic compilation should take place */
+    public static final String COMPILE_ONLY_OPTIMISTIC_ARG = "optimistic";
+
     /**
      * Behavior when encountering a function declaration in a lexical context where only statements are acceptable
      * (function declarations are source elements, but not statements).
@@ -110,15 +112,22 @@
         ERROR
     }
 
+    private static final boolean GLOBAL_OPTIMISTIC = Options.getBooleanProperty("nashorn.optimistic");
+
+    /**
+     * What is the default optimistic compilation policy?
+     * @return true if optimistic, false otherwise
+     */
+    public static boolean globalOptimistic() {
+        return GLOBAL_OPTIMISTIC;
+    }
+
     /**
      * Behavior when encountering a function declaration in a lexical context where only statements are acceptable
      * (function declarations are source elements, but not statements).
      */
     public final FunctionStatementBehavior _function_statement;
 
-    /** Should lazy compilation take place */
-    public final boolean _lazy_compilation;
-
     /** Create a new class loaded for each compilation */
     public final boolean _loader_per_compile;
 
@@ -143,6 +152,12 @@
     /** Print resulting bytecode for script */
     public final boolean _print_code;
 
+    /** Directory (optional) to print files to */
+    public final String _print_code_dir;
+
+    /** List of functions to write to the print code dir, optional */
+    public final String _print_code_func;
+
     /** Print memory usage for IR after each phase */
     public final boolean _print_mem_usage;
 
@@ -203,16 +218,15 @@
         _early_lvalue_error   = options.getBoolean("early.lvalue.error");
         _empty_statements     = options.getBoolean("empty.statements");
         _fullversion          = options.getBoolean("fullversion");
-        if(options.getBoolean("function.statement.error")) {
+        if (options.getBoolean("function.statement.error")) {
             _function_statement = FunctionStatementBehavior.ERROR;
-        } else if(options.getBoolean("function.statement.warning")) {
+        } else if (options.getBoolean("function.statement.warning")) {
             _function_statement = FunctionStatementBehavior.WARNING;
         } else {
             _function_statement = FunctionStatementBehavior.ACCEPT;
         }
         _fx                   = options.getBoolean("fx");
         _global_per_engine    = options.getBoolean("global.per.engine");
-        _lazy_compilation     = options.getBoolean("lazy.compilation");
         _loader_per_compile   = options.getBoolean("loader.per.compile");
         _no_java              = options.getBoolean("no.java");
         _no_syntax_extensions = options.getBoolean("no.syntax.extensions");
@@ -220,7 +234,7 @@
         _parse_only           = options.getBoolean("parse.only");
         _print_ast            = options.getBoolean("print.ast");
         _print_lower_ast      = options.getBoolean("print.lower.ast");
-        _print_code           = options.getBoolean("print.code");
+        _print_code           = options.getString("print.code") != null;
         _print_mem_usage      = options.getBoolean("print.mem.usage");
         _print_no_newline     = options.getBoolean("print.no.newline");
         _print_parse          = options.getBoolean("print.parse");
@@ -243,6 +257,26 @@
             }
         }
 
+        String dir = null;
+        String func = null;
+        final String pc = options.getString("print.code");
+        if (pc != null) {
+            StringTokenizer st = new StringTokenizer(pc, ",");
+            while (st.hasMoreTokens()) {
+                StringTokenizer st2 = new StringTokenizer(st.nextToken(), ":");
+                while (st2.hasMoreTokens()) {
+                    String cmd = st2.nextToken();
+                    if ("dir".equals(cmd)) {
+                        dir = st2.nextToken();
+                    } else if ("function".equals(cmd)) {
+                        func = st2.nextToken();
+                    }
+                }
+            }
+        }
+        _print_code_dir = dir;
+        _print_code_func = func;
+
         int callSiteFlags = 0;
         if (options.getBoolean("profile.callsites")) {
             callSiteFlags |= NashornCallSiteDescriptor.CALLSITE_PROFILE;
@@ -335,4 +369,5 @@
     public List<String> getArguments() {
         return options.getArguments();
     }
+
 }
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java	Wed Feb 26 13:17:57 2014 +0100
@@ -29,6 +29,7 @@
 import static jdk.nashorn.internal.lookup.Lookup.MH;
 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
+import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
 
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
@@ -36,10 +37,11 @@
 import jdk.internal.dynalink.CallSiteDescriptor;
 import jdk.internal.dynalink.linker.GuardedInvocation;
 import jdk.internal.dynalink.linker.LinkRequest;
+import jdk.internal.dynalink.support.Guards;
 import jdk.nashorn.internal.codegen.CompilerConstants.Call;
-import jdk.nashorn.internal.lookup.MethodHandleFactory;
+import jdk.nashorn.internal.objects.NativeFunction;
+import jdk.nashorn.internal.runtime.linker.Bootstrap;
 import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
-import jdk.nashorn.internal.runtime.linker.NashornGuards;
 
 /**
  * Runtime representation of a JavaScript function.
@@ -47,33 +49,37 @@
 public abstract class ScriptFunction extends ScriptObject {
 
     /** Method handle for prototype getter for this ScriptFunction */
-    public static final MethodHandle G$PROTOTYPE = findOwnMH("G$prototype", Object.class, Object.class);
+    public static final MethodHandle G$PROTOTYPE = findOwnMH_S("G$prototype", Object.class, Object.class);
 
     /** Method handle for prototype setter for this ScriptFunction */
-    public static final MethodHandle S$PROTOTYPE = findOwnMH("S$prototype", void.class, Object.class, Object.class);
+    public static final MethodHandle S$PROTOTYPE = findOwnMH_S("S$prototype", void.class, Object.class, Object.class);
 
     /** Method handle for length getter for this ScriptFunction */
-    public static final MethodHandle G$LENGTH = findOwnMH("G$length", int.class, Object.class);
+    public static final MethodHandle G$LENGTH = findOwnMH_S("G$length", int.class, Object.class);
 
     /** Method handle for name getter for this ScriptFunction */
-    public static final MethodHandle G$NAME = findOwnMH("G$name", Object.class, Object.class);
+    public static final MethodHandle G$NAME = findOwnMH_S("G$name", Object.class, Object.class);
 
     /** Method handle used for implementing sync() in mozilla_compat */
-    public static final MethodHandle INVOKE_SYNC = findOwnMH("invokeSync", Object.class, ScriptFunction.class, Object.class, Object.class, Object[].class);
+    public static final MethodHandle INVOKE_SYNC = findOwnMH_S("invokeSync", Object.class, ScriptFunction.class, Object.class, Object.class, Object[].class);
 
     /** Method handle for allocate function for this ScriptFunction */
-    static final MethodHandle ALLOCATE = findOwnMH("allocate", Object.class);
+    static final MethodHandle ALLOCATE = findOwnMH_V("allocate", Object.class);
 
-    private static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", Object.class, Object.class);
+    private static final MethodHandle WRAPFILTER = findOwnMH_S("wrapFilter", Object.class, Object.class);
 
     /** method handle to scope getter for this ScriptFunction */
     public static final Call GET_SCOPE = virtualCallNoLookup(ScriptFunction.class, "getScope", ScriptObject.class);
 
-    private static final MethodHandle IS_FUNCTION_MH  = findOwnMH("isFunctionMH", boolean.class, Object.class, ScriptFunctionData.class);
+    private static final MethodHandle IS_FUNCTION_MH  = findOwnMH_S("isFunctionMH", boolean.class, Object.class, ScriptFunctionData.class);
+
+    private static final MethodHandle IS_APPLY_FUNCTION  = findOwnMH_S("isApplyFunction", boolean.class, boolean.class, Object.class, Object.class);
 
-    private static final MethodHandle IS_NONSTRICT_FUNCTION = findOwnMH("isNonStrictFunction", boolean.class, Object.class, Object.class, ScriptFunctionData.class);
+    private static final MethodHandle IS_NONSTRICT_FUNCTION = findOwnMH_S("isNonStrictFunction", boolean.class, Object.class, Object.class, ScriptFunctionData.class);
 
-    private static final MethodHandle ADD_ZEROTH_ELEMENT = findOwnMH("addZerothElement", Object[].class, Object[].class, Object.class);
+    private static final MethodHandle ADD_ZEROTH_ELEMENT = findOwnMH_S("addZerothElement", Object[].class, Object[].class, Object.class);
+
+    private static final MethodHandle WRAP_THIS = MH.findStatic(MethodHandles.lookup(), ScriptFunctionData.class, "wrapThis", MH.type(Object.class, Object.class));
 
     /** The parent scope. */
     private final ScriptObject scope;
@@ -192,6 +198,10 @@
         return data.needsWrappedThis();
     }
 
+    private static boolean needsWrappedThis(Object fn) {
+        return fn instanceof ScriptFunction ? ((ScriptFunction)fn).needsWrappedThis() : false;
+    }
+
     /**
      * Execute this script function.
      * @param self  Target object.
@@ -308,25 +318,17 @@
      * @param sync the Object to synchronize on, or undefined
      * @return synchronized function
      */
-    public abstract ScriptFunction makeSynchronizedFunction(Object sync);
+   public abstract ScriptFunction makeSynchronizedFunction(Object sync);
 
     /**
-     * Return the most appropriate invoke handle if there are specializations
-     * @param type most specific method type to look for invocation with
-     * @param args args for trampoline invocation
-     * @return invoke method handle
+     * Return the most appropriate invocation for the specified call site type. If specializations are possible, it will
+     * strive to return an efficient specialization.
+     * @param callSiteType the call site type; can be as specific as needed.
+     * @return a guarded invocation with invoke method handle and potentially a switch point guarding optimistic
+     * assumptions.
      */
-    private MethodHandle getBestInvoker(final MethodType type, final Object[] args) {
-        return data.getBestInvoker(type, args);
-    }
-
-    /**
-     * Return the most appropriate invoke handle if there are specializations
-     * @param type most specific method type to look for invocation with
-     * @return invoke method handle
-     */
-    public MethodHandle getBestInvoker(final MethodType type) {
-        return getBestInvoker(type, null);
+    private GuardedInvocation getBestInvoker(final MethodType callSiteType, final int callerProgramPoint) {
+        return data.getBestInvoker(callSiteType, callerProgramPoint);
     }
 
     /**
@@ -465,7 +467,9 @@
     @Override
     protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc) {
         final MethodType type = desc.getMethodType();
-        return new GuardedInvocation(pairArguments(data.getBestConstructor(type.changeParameterType(0, ScriptFunction.class), null), type), null, getFunctionGuard(this));
+        assert desc.getMethodType().returnType() == Object.class && !NashornCallSiteDescriptor.isOptimistic(desc);
+        final GuardedInvocation bestCtorInv = data.getBestConstructor(type);
+        return new GuardedInvocation(pairArguments(bestCtorInv.getInvocation(), type), getFunctionGuard(this), bestCtorInv.getSwitchPoint());
     }
 
     @SuppressWarnings("unused")
@@ -487,56 +491,75 @@
      * (b) method doesn't have callee parameter (builtin functions)
      *   (3) for local/scope calls, bind thiz and drop both callee and thiz.
      *   (4) for normal this-calls, drop callee.
+     *
+     * @return guarded invocation for call
      */
     @Override
     protected GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final LinkRequest request) {
         final MethodType type = desc.getMethodType();
 
-        if (request.isCallSiteUnstable()) {
-            // (this, callee, args...) => (this, callee, args[])
-            final MethodHandle collector = MH.asCollector(ScriptRuntime.APPLY.methodHandle(), Object[].class,
-                    type.parameterCount() - 2);
+        final String  name       = getName();
+        final boolean isUnstable = request.isCallSiteUnstable();
+        final boolean scopeCall  = NashornCallSiteDescriptor.isScope(desc);
+        final boolean isCall     = !scopeCall && data.isBuiltin() && "call".equals(name);
+        final boolean isApply    = !scopeCall && data.isBuiltin() && "apply".equals(name);
+
+        if (isUnstable && !(isApply || isCall)) {
+            //megamorphic - replace call with apply
+            final MethodHandle handle;
+            //ensure that the callsite is vararg so apply can consume it
+            if(type.parameterCount() == 3 && type.parameterType(2) == Object[].class) {
+                // Vararg call site
+                handle = ScriptRuntime.APPLY.methodHandle();
+            } else {
+                // (callee, this, args...) => (callee, this, args[])
+                handle = MH.asCollector(ScriptRuntime.APPLY.methodHandle(), Object[].class, type.parameterCount() - 2);
+            }
 
             // If call site is statically typed to take a ScriptFunction, we don't need a guard, otherwise we need a
             // generic "is this a ScriptFunction?" guard.
-            return new GuardedInvocation(collector, ScriptFunction.class.isAssignableFrom(desc.getMethodType().parameterType(0))
-                    ? null : NashornGuards.getScriptFunctionGuard());
+            return new GuardedInvocation(
+                    handle,
+                    null,
+                    null,
+                    ClassCastException.class);
         }
 
         MethodHandle boundHandle;
         MethodHandle guard = null;
 
-        final boolean scopeCall = NashornCallSiteDescriptor.isScope(desc);
+        // Special handling of Function.apply and Function.call. Note we must be invoking
+        if ((isApply || isCall) && !isUnstable) {
+            final Object[] args = request.getArguments();
+            if (Bootstrap.isCallable(args[1])) {
+                return createApplyOrCallCall(isApply, desc, request, args);
+            }
+        } //else just fall through and link as ordinary function or unstable apply
 
+        final int programPoint = NashornCallSiteDescriptor.isOptimistic(desc) ? NashornCallSiteDescriptor.getProgramPoint(desc) : INVALID_PROGRAM_POINT;
+        final GuardedInvocation bestInvoker = getBestInvoker(type, programPoint);
+        final MethodHandle callHandle = bestInvoker.getInvocation();
         if (data.needsCallee()) {
-            final MethodHandle callHandle = getBestInvoker(type, request.getArguments());
             if (scopeCall) {
-                // Make a handle that drops the passed "this" argument and substitutes either Global or Undefined
-                // (callee, this, args...) => (callee, args...)
-                boundHandle = MH.insertArguments(callHandle, 1, needsWrappedThis() ? Context.getGlobalTrusted() : ScriptRuntime.UNDEFINED);
-                // (callee, args...) => (callee, [this], args...)
-                boundHandle = MH.dropArguments(boundHandle, 1, Object.class);
-
+                // Make a handle that drops the passed "this" argument and binds either Global or Undefined instead
+                // Signature remains (callee, this, args...)
+                boundHandle = MH.dropArguments(bindImplicitThis(callHandle), 1, type.parameterType(1));
             } else {
                 // It's already (callee, this, args...), just what we need
                 boundHandle = callHandle;
             }
+        } else if (data.isBuiltin() && "extend".equals(data.getName())) {
+            // NOTE: the only built-in named "extend" is NativeJava.extend. As a special-case we're binding the
+            // current lookup as its "this" so it can do security-sensitive creation of adapter classes.
+            boundHandle = MH.dropArguments(MH.bindTo(callHandle, desc.getLookup()), 0, type.parameterType(0), type.parameterType(1));
+        } else if (scopeCall) {
+            // Make a handle that drops the passed "this" argument and substitutes either Global or Undefined
+            // (this, args...) => (args...)
+            // (args...) => ([callee], [this], args...)
+            boundHandle = MH.dropArguments(MH.bindTo(callHandle, getImplicitThis()), 0, type.parameterType(0), type.parameterType(1));
         } else {
-            final MethodHandle callHandle = getBestInvoker(type.dropParameterTypes(0, 1), request.getArguments());
-            if (data.isBuiltin() && "extend".equals(data.getName())) {
-                // NOTE: the only built-in named "extend" is NativeJava.extend. As a special-case we're binding the
-                // current lookup as its "this" so it can do security-sensitive creation of adapter classes.
-                boundHandle = MH.dropArguments(MH.bindTo(callHandle, desc.getLookup()), 0, Object.class, Object.class);
-            } else if (scopeCall) {
-                // Make a handle that drops the passed "this" argument and substitutes either Global or Undefined
-                // (this, args...) => (args...)
-                boundHandle = MH.bindTo(callHandle, needsWrappedThis() ? Context.getGlobalTrusted() : ScriptRuntime.UNDEFINED);
-                // (args...) => ([callee], [this], args...)
-                boundHandle = MH.dropArguments(boundHandle, 0, Object.class, Object.class);
-            } else {
-                // (this, args...) => ([callee], this, args...)
-                boundHandle = MH.dropArguments(callHandle, 0, Object.class);
-            }
+            // (this, args...) => ([callee], this, args...)
+            boundHandle = MH.dropArguments(callHandle, 0, type.parameterType(0));
         }
 
         // For non-strict functions, check whether this-object is primitive type.
@@ -552,8 +575,112 @@
 
         boundHandle = pairArguments(boundHandle, type);
 
-        return new GuardedInvocation(boundHandle, guard == null ? getFunctionGuard(this) : guard);
-   }
+        return new GuardedInvocation(boundHandle, guard == null ? getFunctionGuard(this) : guard, bestInvoker.getSwitchPoint());
+    }
+
+    private GuardedInvocation createApplyOrCallCall(final boolean isApply, final CallSiteDescriptor desc, final LinkRequest request, final Object[] args) {
+        final MethodType descType = desc.getMethodType();
+        final int paramCount = descType.parameterCount();
+        final boolean passesThis = paramCount > 2;
+        final boolean passesArgs = paramCount > 3;
+
+        final Object appliedFn = args[1];
+        final boolean appliedFnNeedsWrappedThis = needsWrappedThis(appliedFn);
+
+        // R(apply|call, ...) => R(...)
+        MethodType appliedType = descType.dropParameterTypes(0, 1);
+        if(!passesThis) {
+            // R() => R(this)
+            appliedType = appliedType.insertParameterTypes(1, Object.class);
+        } else if(appliedFnNeedsWrappedThis) {
+            appliedType = appliedType.changeParameterType(1, Object.class);
+        }
+        if(isApply) {
+            if(passesArgs) {
+                // R(this, args) => R(this, Object[])
+                appliedType = appliedType.changeParameterType(2, Object[].class);
+            } else {
+                // R(this) => R(this, Object[])
+                appliedType = appliedType.insertParameterTypes(2, Object[].class);
+            }
+        }
+        final CallSiteDescriptor appliedDesc = desc.changeMethodType(appliedType);
+
+        // Create the same arguments for the delegate linking request that would be passed in an actual apply'd invocation
+        final Object[] appliedArgs = new Object[isApply ? 3 : appliedType.parameterCount()];
+        appliedArgs[0] = appliedFn;
+        appliedArgs[1] = passesThis ? (appliedFnNeedsWrappedThis ? ScriptFunctionData.wrapThis(args[2]) : args[2]) : ScriptRuntime.UNDEFINED;
+        if(isApply) {
+            appliedArgs[2] = passesArgs ? NativeFunction.toApplyArgs(args[3]) : ScriptRuntime.EMPTY_ARRAY;
+        } else {
+            if(passesArgs) {
+                System.arraycopy(args, 3, appliedArgs, 2, args.length - 3);
+            }
+        }
+
+        // Ask the linker machinery for an invocation of the target function
+        final LinkRequest appliedRequest = request.replaceArguments(appliedDesc, appliedArgs);
+        final GuardedInvocation appliedInvocation;
+        try {
+            appliedInvocation = Bootstrap.getLinkerServices().getGuardedInvocation(appliedRequest);
+        } catch(final RuntimeException | Error e) {
+            throw e;
+        } catch(final Exception e) {
+            throw new RuntimeException(e);
+        }
+        assert appliedRequest != null; // Bootstrap.isCallable() returned true for args[1], so it must produce a linkage.
+
+        final Class<?> applyFnType = descType.parameterType(0);
+        MethodHandle inv = appliedInvocation.getInvocation(); //method handle from apply invocation. the applied function invocation
+        if(isApply) {
+            if(passesArgs) {
+                // Make sure that the passed argArray is converted to Object[] the same way NativeFunction.apply() would do it.
+                inv = MH.filterArguments(inv, 2, NativeFunction.TO_APPLY_ARGS);
+            } else {
+                // If the original call site doesn't pass argArray, pass in an empty array
+                inv = MH.insertArguments(inv, 2, (Object)ScriptRuntime.EMPTY_ARRAY);
+            }
+        }
+        if(!passesThis) {
+            // If the original call site doesn't pass in a thisArg, pass in Global/undefined as needed
+            inv = bindImplicitThis(appliedFn, inv);
+        } else if(appliedFnNeedsWrappedThis) {
+            // target function needs a wrapped this, so make sure we filter for that
+            inv = MH.filterArguments(inv, 1, WRAP_THIS);
+        }
+        inv = MH.dropArguments(inv, 0, applyFnType);
+        MethodHandle guard = appliedInvocation.getGuard();
+        // If the guard checks the value of "this" but we aren't passing thisArg, insert the default one
+        if(!passesThis && guard.type().parameterCount() > 1) {
+            guard = bindImplicitThis(appliedFn, guard);
+        }
+        final MethodType guardType = guard.type();
+
+        // Original function guard will expect the invoked function in parameter position 0, but we're passing it in
+        // position 1.
+        guard = MH.dropArguments(guard, 0, descType.parameterType(0));
+        // Take the "isApplyFunction" guard, and bind it to this function.
+        MethodHandle applyFnGuard = MH.insertArguments(IS_APPLY_FUNCTION, 2, this);
+        // Adapt the guard to receive all the arguments that the original guard does.
+        applyFnGuard = MH.dropArguments(applyFnGuard, 2, guardType.parameterArray());
+        // Fold the original function guard into our apply guard.
+        guard = MH.foldArguments(applyFnGuard, guard);
+
+        return appliedInvocation.replaceMethods(inv, guard);
+    }
+
+     private static MethodHandle bindImplicitThis(final Object fn, final MethodHandle mh) {
+         return MH.insertArguments(mh, 1, fn instanceof ScriptFunction ? ((ScriptFunction)fn).getImplicitThis() : ScriptRuntime.UNDEFINED);
+     }
+
+     private MethodHandle bindImplicitThis(final MethodHandle mh) {
+         return MH.insertArguments(mh, 1, getImplicitThis());
+     }
+
+    private Object getImplicitThis() {
+        return needsWrappedThis() ? Context.getGlobalTrusted() : ScriptRuntime.UNDEFINED;
+    }
+
 
     /**
      * Used for noSuchMethod/noSuchProperty and JSAdapter hooks.
@@ -561,7 +688,7 @@
      * These don't want a callee parameter, so bind that. Name binding is optional.
      */
     MethodHandle getCallMethodHandle(final MethodType type, final String bindName) {
-        return pairArguments(bindToNameIfNeeded(bindToCalleeIfNeeded(getBestInvoker(type, null)), bindName), type);
+        return pairArguments(bindToNameIfNeeded(bindToCalleeIfNeeded(data.getGenericInvoker()), bindName), type);
     }
 
     private static MethodHandle bindToNameIfNeeded(final MethodHandle methodHandle, final String bindName) {
@@ -591,7 +718,9 @@
      */
     private static MethodHandle getFunctionGuard(final ScriptFunction function) {
         assert function.data != null;
-        return MH.insertArguments(IS_FUNCTION_MH, 1, function.data);
+        // Built-in functions have a 1-1 correspondence to their ScriptFunctionData, so we can use a cheaper identity
+        // comparison for them.
+        return function.data.isBuiltin() ? Guards.getIdentityGuard(function) : MH.insertArguments(IS_FUNCTION_MH, 1, function.data);
     }
 
     /**
@@ -619,6 +748,12 @@
     }
 
     @SuppressWarnings("unused")
+    private static boolean isApplyFunction(final boolean appliedFnCondition, final Object self, final Object expectedSelf) {
+        // NOTE: we're using self == expectedSelf as we're only using this with built-in functions apply() and call()
+        return appliedFnCondition && self == expectedSelf;
+    }
+
+    @SuppressWarnings("unused")
     private static Object[] addZerothElement(final Object[] args, final Object value) {
         // extends input array with by adding new zeroth element
         final Object[] src = (args == null)? ScriptRuntime.EMPTY_ARRAY : args;
@@ -637,14 +772,12 @@
         }
     }
 
-    private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
-        final Class<?>   own = ScriptFunction.class;
-        final MethodType mt  = MH.type(rtype, types);
-        try {
-            return MH.findStatic(MethodHandles.lookup(), own, name, mt);
-        } catch (final MethodHandleFactory.LookupException e) {
-            return MH.findVirtual(MethodHandles.lookup(), own, name, mt);
-        }
+    private static MethodHandle findOwnMH_S(final String name, final Class<?> rtype, final Class<?>... types) {
+        return MH.findStatic(MethodHandles.lookup(), ScriptFunction.class, name, MH.type(rtype, types));
+    }
+
+    private static MethodHandle findOwnMH_V(final String name, final Class<?> rtype, final Class<?>... types) {
+        return MH.findVirtual(MethodHandles.lookup(), ScriptFunction.class, name, MH.type(rtype, types));
     }
 }
 
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java	Wed Feb 26 13:17:57 2014 +0100
@@ -32,7 +32,14 @@
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.util.Collections;
+import java.util.Map;
+import java.util.WeakHashMap;
+import jdk.internal.dynalink.linker.GuardedInvocation;
 import jdk.nashorn.internal.runtime.linker.JavaAdapterFactory;
+import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
 
 /**
  * A container for data needed to instantiate a specific {@link ScriptFunction} at runtime.
@@ -40,22 +47,33 @@
  * constants array to reduce function instantiation overhead during runtime.
  */
 public abstract class ScriptFunctionData {
+    static final int MAX_ARITY = LinkerCallSite.ARGLIMIT;
+    static {
+        // Assert it fits in a byte, as that's what we store it in. It's just a size optimization though, so if needed
+        // "byte arity" field can be widened.
+        assert MAX_ARITY < 256;
+    }
 
-    /** Name of the function or "" for anonynous functions */
+    private static final Map<ScriptFunctionData, Reference<MethodHandle>> GENERIC_INVOKERS     = Collections.synchronizedMap(new WeakHashMap<ScriptFunctionData, Reference<MethodHandle>>());
+    private static final Map<ScriptFunctionData, Reference<MethodHandle>> GENERIC_CONSTRUCTORS = Collections.synchronizedMap(new WeakHashMap<ScriptFunctionData, Reference<MethodHandle>>());
+
+    /** Name of the function or "" for anonymous functions */
     protected final String name;
 
     /** All versions of this function that have been generated to code */
+    // TODO: integrate it into ScriptFunctionData; there's not much reason for this to be in its own class.
     protected final CompiledFunctions code;
 
-    private int arity;
+    // Parameter arity of the function, corresponding to "f.length". E.g. "function f(a, b, c) { ... }" arity is 3, and
+    // some built-in ECMAScript functions have their arity declared by the specification. Note that regardless of this
+    // value, the function might still be capable of receiving variable number of arguments, see isVariableArity.
+    private byte arity;
 
     private final boolean isStrict;
-
     private final boolean isBuiltin;
+    private final boolean isConstructor;
+    private final boolean isVariableArity;
 
-    private final boolean isConstructor;
-
-    private static final MethodHandle NEWFILTER     = findOwnMH("newFilter", Object.class, Object.class, Object.class);
     private static final MethodHandle BIND_VAR_ARGS = findOwnMH("bindVarArgs", Object[].class, Object[].class, Object[].class);
 
     /**
@@ -67,17 +85,25 @@
      * @param isBuiltin     is the function built in
      * @param isConstructor is the function a constructor
      */
-    ScriptFunctionData(final String name, final int arity, final boolean isStrict, final boolean isBuiltin, final boolean isConstructor) {
-        this.name          = name;
-        this.arity         = arity;
-        this.code          = new CompiledFunctions();
-        this.isStrict      = isStrict;
-        this.isBuiltin     = isBuiltin;
-        this.isConstructor = isConstructor;
+    ScriptFunctionData(final String name, final int arity, final boolean isStrict, final boolean isBuiltin,
+            final boolean isConstructor, final boolean isVariableArity) {
+        this.name            = name;
+        this.code            = new CompiledFunctions(name);
+        this.isStrict        = isStrict;
+        this.isBuiltin       = isBuiltin;
+        this.isConstructor   = isConstructor;
+        this.isVariableArity = isVariableArity;
+
+        setArity(arity);
     }
 
     final int getArity() {
-        return arity;
+        // arity byte should be interpreted as u8.
+        return arity & 0xFF;
+    }
+
+    final boolean isVariableArity() {
+        return isVariableArity;
     }
 
     /**
@@ -85,19 +111,20 @@
      * @param arity new arity
      */
     void setArity(final int arity) {
-        this.arity = arity;
+        if(arity < 0 || arity > MAX_ARITY) {
+            throw new IllegalArgumentException(String.valueOf(arity));
+        }
+        this.arity = (byte)arity;
     }
 
     CompiledFunction bind(final CompiledFunction originalInv, final ScriptFunction fn, final Object self, final Object[] args) {
-        final MethodHandle boundInvoker = bindInvokeHandle(originalInv.getInvoker(), fn, self, args);
+        final MethodHandle boundInvoker = bindInvokeHandle(originalInv.createComposableInvoker(), fn, self, args);
 
-        //TODO the boundinvoker.type() could actually be more specific here
         if (isConstructor()) {
-            ensureConstructor(originalInv);
-            return new CompiledFunction(boundInvoker.type(), boundInvoker, bindConstructHandle(originalInv.getConstructor(), fn, args));
+            return new CompiledFunction(boundInvoker, bindConstructHandle(originalInv.createComposableConstructor(), fn, args));
         }
 
-        return new CompiledFunction(boundInvoker.type(), boundInvoker);
+        return new CompiledFunction(boundInvoker);
     }
 
     /**
@@ -116,11 +143,7 @@
         return isConstructor;
     }
 
-    boolean needsCallee() {
-        // we don't know if we need a callee or not unless we are generated
-        ensureCodeGenerated();
-        return code.needsCallee();
-    }
+    abstract boolean needsCallee();
 
     /**
      * Returns true if this is a non-strict, non-built-in function that requires non-primitive this argument
@@ -167,38 +190,22 @@
      * and not suddenly a "real" object
      *
      * @param callSiteType callsite type
-     * @param args         arguments at callsite on first trampoline invocation
-     * @return method handle to best invoker
+     * @return guarded invocation with method handle to best invoker and potentially a switch point guarding optimistic
+     * assumptions.
      */
-    MethodHandle getBestInvoker(final MethodType callSiteType, final Object[] args) {
-        return getBest(callSiteType).getInvoker();
+     final GuardedInvocation getBestInvoker(final MethodType callSiteType, final int callerProgramPoint) {
+        final CompiledFunction cf = getBest(callSiteType);
+        assert cf != null;
+        return new GuardedInvocation(cf.createInvoker(callSiteType.returnType(), callerProgramPoint), cf.getOptimisticAssumptionsSwitchPoint());
     }
 
-    MethodHandle getBestInvoker(final MethodType callSiteType) {
-        return getBestInvoker(callSiteType, null);
-    }
-
-    MethodHandle getBestConstructor(final MethodType callSiteType, final Object[] args) {
+    final GuardedInvocation getBestConstructor(final MethodType callSiteType) {
         if (!isConstructor()) {
             throw typeError("not.a.constructor", toSource());
         }
-        ensureCodeGenerated();
-
-        final CompiledFunction best = getBest(callSiteType);
-        ensureConstructor(best);
-        return best.getConstructor();
-    }
-
-    MethodHandle getBestConstructor(final MethodType callSiteType) {
-        return getBestConstructor(callSiteType, null);
-    }
-
-    /**
-     * Subclass responsibility. If we can have lazy code generation, this is a hook to ensure that
-     * code exists before performing an operation.
-     */
-    protected void ensureCodeGenerated() {
-        //empty
+        // Constructor call sites don't have a "this", but getBest is meant to operate on "callee, this, ..." style
+        final CompiledFunction cf = getBest(callSiteType.insertParameterTypes(1, Object.class));
+        return new GuardedInvocation(cf.getConstructor(), cf.getOptimisticAssumptionsSwitchPoint());
     }
 
     /**
@@ -212,21 +219,61 @@
      * @return generic invoker of this script function
      */
     public final MethodHandle getGenericInvoker() {
-        ensureCodeGenerated();
-        return code.generic().getInvoker();
+        MethodHandle invoker;
+        final Reference<MethodHandle> ref = GENERIC_INVOKERS.get(this);
+        if(ref != null) {
+            invoker = ref.get();
+            if(invoker != null) {
+                return invoker;
+            }
+        }
+        invoker = createGenericInvoker();
+        GENERIC_INVOKERS.put(this, new WeakReference<>(invoker));
+        return invoker;
+    }
+
+    private MethodHandle createGenericInvoker() {
+        return makeGenericMethod(getGeneric().createComposableInvoker());
+// TODO hannes        return code.generic().getInvoker();
     }
 
     final MethodHandle getGenericConstructor() {
-        ensureCodeGenerated();
-        ensureConstructor(code.generic());
-        return code.generic().getConstructor();
+        MethodHandle constructor;
+        final Reference<MethodHandle> ref = GENERIC_CONSTRUCTORS.get(this);
+        if(ref != null) {
+            constructor = ref.get();
+            if(constructor != null) {
+                return constructor;
+            }
+        }
+        constructor = createGenericConstructor();
+        GENERIC_CONSTRUCTORS.put(this, new WeakReference<>(constructor));
+        return constructor;
+    }
+
+    private MethodHandle createGenericConstructor() {
+        return makeGenericMethod(getGeneric().createComposableConstructor());
     }
 
-    private CompiledFunction getBest(final MethodType callSiteType) {
-        ensureCodeGenerated();
-        return code.best(callSiteType);
+    /**
+     * Returns the best function for the specified call site type.
+     * @param callSiteType The call site type. Call site types are expected to have the form
+     * {@code (callee, this[, args...])}.
+     * @return the best function for the specified call site type.
+     */
+    CompiledFunction getBest(final MethodType callSiteType) {
+        return code.best(callSiteType, isRecompilable());
     }
 
+    abstract boolean isRecompilable();
+
+    CompiledFunction getGeneric() {
+        return getBest(getGenericType());
+    }
+
+
+    abstract MethodType getGenericType();
+
     /**
      * Allocates an object using this function's allocator.
      * @return the object allocated using this function's allocator, or null if the function doesn't have an allocator.
@@ -244,90 +291,15 @@
      * @param args additional arguments to bind. Can be null.
      */
     ScriptFunctionData makeBoundFunctionData(final ScriptFunction fn, final Object self, final Object[] args) {
-        ensureCodeGenerated();
-
         final Object[] allArgs = args == null ? ScriptRuntime.EMPTY_ARRAY : args;
         final int length = args == null ? 0 : args.length;
 
-        CompiledFunctions boundList = new CompiledFunctions();
-        if (code.size() == 1) {
-            // only one variant - bind that
-            boundList.add(bind(code.first(), fn, self, allArgs));
-        } else {
-            // There are specialized versions. Get the most generic one.
-            // This is to avoid ambiguous overloaded versions of bound and
-            // specialized variants and choosing wrong overload.
-            final MethodHandle genInvoker = getGenericInvoker();
-            final CompiledFunction inv = new CompiledFunction(genInvoker.type(), genInvoker, getGenericConstructor());
-            boundList.add(bind(inv, fn, self, allArgs));
-        }
-
-        ScriptFunctionData boundData = new FinalScriptFunctionData(name, arity == -1 ? -1 : Math.max(0, arity - length), boundList, isStrict(), isBuiltin(), isConstructor());
-        return boundData;
-    }
-
-    /**
-     * Compose a constructor given a primordial constructor handle.
-     *
-     * @param ctor primordial constructor handle
-     * @return the composed constructor
-     */
-    protected MethodHandle composeConstructor(final MethodHandle ctor) {
-        // If it was (callee, this, args...), permute it to (this, callee, args...). We're doing this because having
-        // "this" in the first argument position is what allows the elegant folded composition of
-        // (newFilter x constructor x allocator) further down below in the code. Also, ensure the composite constructor
-        // always returns Object.
-        final boolean needsCallee = needsCallee(ctor);
-        MethodHandle composedCtor = needsCallee ? swapCalleeAndThis(ctor) : ctor;
-
-        composedCtor = changeReturnTypeToObject(composedCtor);
-
-        final MethodType ctorType = composedCtor.type();
-
-        // Construct a dropping type list for NEWFILTER, but don't include constructor "this" into it, so it's actually
-        // captured as "allocation" parameter of NEWFILTER after we fold the constructor into it.
-        // (this, [callee, ]args...) => ([callee, ]args...)
-        final Class<?>[] ctorArgs = ctorType.dropParameterTypes(0, 1).parameterArray();
+        final CompiledFunctions boundList = new CompiledFunctions(fn.getName());
+        final CompiledFunction bindTarget = new CompiledFunction(getGenericInvoker(), getGenericConstructor());
+        boundList.add(bind(bindTarget, fn, self, allArgs));
 
-        // Fold constructor into newFilter that replaces the return value from the constructor with the originally
-        // allocated value when the originally allocated value is a primitive.
-        // (result, this, [callee, ]args...) x (this, [callee, ]args...) => (this, [callee, ]args...)
-        composedCtor = MH.foldArguments(MH.dropArguments(NEWFILTER, 2, ctorArgs), composedCtor);
-
-        // allocate() takes a ScriptFunction and returns a newly allocated ScriptObject...
-        if (needsCallee) {
-            // ...we either fold it into the previous composition, if we need both the ScriptFunction callee object and
-            // the newly allocated object in the arguments, so (this, callee, args...) x (callee) => (callee, args...),
-            // or...
-            return MH.foldArguments(composedCtor, ScriptFunction.ALLOCATE);
-        }
-
-        // ...replace the ScriptFunction argument with the newly allocated object, if it doesn't need the callee
-        // (this, args...) filter (callee) => (callee, args...)
-        return MH.filterArguments(composedCtor, 0, ScriptFunction.ALLOCATE);
-    }
-
-    /**
-     * If this function's method handles need a callee parameter, swap the order of first two arguments for the passed
-     * method handle. If this function's method handles don't need a callee parameter, returns the original method
-     * handle unchanged.
-     *
-     * @param mh a method handle with order of arguments {@code (callee, this, args...)}
-     *
-     * @return a method handle with order of arguments {@code (this, callee, args...)}
-     */
-    private static MethodHandle swapCalleeAndThis(final MethodHandle mh) {
-        final MethodType type = mh.type();
-        assert type.parameterType(0) == ScriptFunction.class : type;
-        assert type.parameterType(1) == Object.class : type;
-        final MethodType newType = type.changeParameterType(0, Object.class).changeParameterType(1, ScriptFunction.class);
-        final int[] reorder = new int[type.parameterCount()];
-        reorder[0] = 1;
-        assert reorder[1] == 0;
-        for (int i = 2; i < reorder.length; ++i) {
-            reorder[i] = i;
-        }
-        return MethodHandles.permuteArguments(mh, newType, reorder);
+        ScriptFunctionData boundData = new FinalScriptFunctionData(name, Math.max(0, getArity() - length), boundList, isStrict(), isBuiltin(), isConstructor(), isVariableArity());
+        return boundData;
     }
 
     /**
@@ -338,7 +310,11 @@
      * @return the converted this object
      */
     private Object convertThisObject(final Object thiz) {
-        if (!(thiz instanceof ScriptObject) && needsWrappedThis()) {
+        return needsWrappedThis() ? wrapThis(thiz) : thiz;
+    }
+
+    static Object wrapThis(final Object thiz) {
+        if (!(thiz instanceof ScriptObject)) {
             if (JSType.nullOrUndefined(thiz)) {
                 return Context.getGlobalTrusted();
             }
@@ -471,6 +447,38 @@
     }
 
     /**
+     * Takes a method handle, and returns a potentially different method handle that can be used in
+     * {@code ScriptFunction#invoke(Object, Object...)} or {code ScriptFunction#construct(Object, Object...)}.
+     * The returned method handle will be sure to return {@code Object}, and will have all its parameters turned into
+     * {@code Object} as well, except for the following ones:
+     * <ul>
+     *   <li>a last parameter of type {@code Object[]} which is used for vararg functions,</li>
+     *   <li>the first argument, which is forced to be {@link ScriptFunction}, in case the function receives itself
+     *   (callee) as an argument.</li>
+     * </ul>
+     *
+     * @param mh the original method handle
+     *
+     * @return the new handle, conforming to the rules above.
+     */
+    private static MethodHandle makeGenericMethod(final MethodHandle mh) {
+        final MethodType type = mh.type();
+        MethodType newType = makeGenericType(type);
+        return type.equals(newType) ? mh : mh.asType(newType);
+    }
+
+    private static MethodType makeGenericType(final MethodType type) {
+        MethodType newType = type.generic();
+        if (isVarArg(type)) {
+            newType = newType.changeParameterType(type.parameterCount() - 1, Object[].class);
+        }
+        if (needsCallee(type)) {
+            newType = newType.changeParameterType(0, ScriptFunction.class);
+        }
+        return newType;
+    }
+
+    /**
      * Execute this script function.
      *
      * @param self  Target object.
@@ -480,9 +488,9 @@
      * @throws Throwable if there is an exception/error with the invocation or thrown from it
      */
     Object invoke(final ScriptFunction fn, final Object self, final Object... arguments) throws Throwable {
-        final MethodHandle mh  = getGenericInvoker();
-        final Object   selfObj = convertThisObject(self);
-        final Object[] args    = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments;
+        final MethodHandle mh      = getGenericInvoker();
+        final Object       selfObj = convertThisObject(self);
+        final Object[]     args    = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments;
 
         if (isVarArg(mh)) {
             if (needsCallee(mh)) {
@@ -651,24 +659,6 @@
     }
 
     /**
-     * Adapts the method handle so its return type is {@code Object}. If the handle's return type is already
-     * {@code Object}, the handle is returned unchanged.
-     *
-     * @param mh the handle to adapt
-     * @return the adapted handle
-     */
-    private static MethodHandle changeReturnTypeToObject(final MethodHandle mh) {
-        final MethodType type = mh.type();
-        return (type.returnType() == Object.class) ? mh : MH.asType(mh, type.changeReturnType(Object.class));
-    }
-
-    private void ensureConstructor(final CompiledFunction inv) {
-        if (!inv.hasConstructor()) {
-            inv.setConstructor(composeConstructor(inv.getInvoker()));
-        }
-    }
-
-    /**
      * Heuristic to figure out if the method handle has a callee argument. If it's type is
      * {@code (ScriptFunction, ...)}, then we'll assume it has a callee argument. We need this as
      * the constructor above is not passed this information, and can't just blindly assume it's false
@@ -680,8 +670,18 @@
      * @return true if the method handle expects a callee, false otherwise
      */
     protected static boolean needsCallee(final MethodHandle mh) {
-        final MethodType type = mh.type();
-        return (type.parameterCount() > 0 && type.parameterType(0) == ScriptFunction.class);
+        return needsCallee(mh.type());
+    }
+
+    static boolean needsCallee(final MethodType type) {
+        final int length = type.parameterCount();
+
+        if (length == 0) {
+            return false;
+        }
+
+        final Class<?> param0 = type.parameterType(0);
+        return param0 == ScriptFunction.class || (param0 == boolean.class && length > 1 && type.parameterType(1) == ScriptFunction.class);
     }
 
     /**
@@ -692,7 +692,10 @@
      * @return true if vararg
      */
     protected static boolean isVarArg(final MethodHandle mh) {
-        final MethodType type = mh.type();
+        return isVarArg(mh.type());
+    }
+
+    static boolean isVarArg(final MethodType type) {
         return type.parameterType(type.parameterCount() - 1).isArray();
     }
 
@@ -716,11 +719,6 @@
         return concat;
     }
 
-    @SuppressWarnings("unused")
-    private static Object newFilter(final Object result, final Object allocation) {
-        return (result instanceof ScriptObject || !JSType.isPrimitive(result))? result : allocation;
-    }
-
     private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
         return MH.findStatic(MethodHandles.lookup(), ScriptFunctionData.class, name, MH.type(rtype, types));
     }
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java	Wed Feb 26 13:17:57 2014 +0100
@@ -27,9 +27,13 @@
 
 import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCall;
 import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
+import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY;
 import static jdk.nashorn.internal.lookup.Lookup.MH;
 import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError;
 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
+import static jdk.nashorn.internal.runtime.JSType.UNDEFINED_DOUBLE;
+import static jdk.nashorn.internal.runtime.JSType.UNDEFINED_INT;
+import static jdk.nashorn.internal.runtime.JSType.UNDEFINED_LONG;
 import static jdk.nashorn.internal.runtime.PropertyDescriptor.CONFIGURABLE;
 import static jdk.nashorn.internal.runtime.PropertyDescriptor.ENUMERABLE;
 import static jdk.nashorn.internal.runtime.PropertyDescriptor.GET;
@@ -37,6 +41,8 @@
 import static jdk.nashorn.internal.runtime.PropertyDescriptor.VALUE;
 import static jdk.nashorn.internal.runtime.PropertyDescriptor.WRITABLE;
 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
+import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
+import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
 import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.getArrayIndex;
 import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex;
 
@@ -54,17 +60,17 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-
 import jdk.internal.dynalink.CallSiteDescriptor;
 import jdk.internal.dynalink.linker.GuardedInvocation;
 import jdk.internal.dynalink.linker.LinkRequest;
 import jdk.internal.dynalink.support.CallSiteDescriptorFactory;
 import jdk.nashorn.internal.codegen.CompilerConstants.Call;
 import jdk.nashorn.internal.codegen.ObjectClassGenerator;
+import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.lookup.Lookup;
-import jdk.nashorn.internal.lookup.MethodHandleFactory;
 import jdk.nashorn.internal.objects.AccessorPropertyDescriptor;
 import jdk.nashorn.internal.objects.DataPropertyDescriptor;
+import jdk.nashorn.internal.objects.NativeArray;
 import jdk.nashorn.internal.runtime.arrays.ArrayData;
 import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
 import jdk.nashorn.internal.runtime.linker.Bootstrap;
@@ -113,7 +119,10 @@
     /** Is length property not-writable? */
     public static final int IS_LENGTH_NOT_WRITABLE = 0b0001_0000;
 
-    /** Spill growth rate - by how many elements does {@link ScriptObject#spill} when full */
+    /**
+     * Spill growth rate - by how many elements does {@link ScriptObject#primitiveSpill} and
+     * {@link ScriptObject#objectSpill} when full
+     */
     public static final int SPILL_RATE = 8;
 
     /** Map to property information and accessor functions. Ordered by insertion. */
@@ -125,25 +134,35 @@
     /** Object flags. */
     private int flags;
 
-    /** Area for properties added to object after instantiation, see {@link AccessorProperty} */
-    public Object[] spill;
+    /** Area for primitive properties added to object after instantiation, see {@link AccessorProperty} */
+    protected long[]   primitiveSpill;
+
+    /** Area for reference properties added to object after instantiation, see {@link AccessorProperty} */
+    protected Object[] objectSpill;
+
+    /**
+     * Number of elements in the spill. This may be less than the spill array lengths, if not all of
+     * the allocated memory is in use
+     */
+    private int spillLength;
 
     /** Indexed array data. */
     private ArrayData arrayData;
 
-    static final MethodHandle GETPROTO           = findOwnMH("getProto", ScriptObject.class);
-    static final MethodHandle SETPROTOCHECK      = findOwnMH("setProtoCheck", void.class, Object.class);
-    static final MethodHandle MEGAMORPHIC_GET    = findOwnMH("megamorphicGet", Object.class, String.class, boolean.class);
-
-    static final MethodHandle SETFIELD           = findOwnMH("setField",         void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, MethodHandle.class, Object.class, Object.class);
-    static final MethodHandle SETSPILL           = findOwnMH("setSpill",         void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, int.class, Object.class, Object.class);
-    static final MethodHandle SETSPILLWITHNEW    = findOwnMH("setSpillWithNew",  void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, int.class, Object.class, Object.class);
-    static final MethodHandle SETSPILLWITHGROW   = findOwnMH("setSpillWithGrow", void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, int.class, int.class, Object.class, Object.class);
-
-    private static final MethodHandle TRUNCATINGFILTER   = findOwnMH("truncatingFilter", Object[].class, int.class, Object[].class);
-    private static final MethodHandle KNOWNFUNCPROPGUARD = findOwnMH("knownFunctionPropertyGuard", boolean.class, Object.class, PropertyMap.class, MethodHandle.class, Object.class, ScriptFunction.class);
-
-    private static final ArrayList<MethodHandle> protoFilters = new ArrayList<>();
+    static final MethodHandle GETPROTO           = findOwnMH_V("getProto", ScriptObject.class);
+    static final MethodHandle SETPROTOCHECK      = findOwnMH_V("setProtoCheck", void.class, Object.class);
+    static final MethodHandle MEGAMORPHIC_GET    = findOwnMH_V("megamorphicGet", Object.class, String.class, boolean.class);
+
+    private static final MethodHandle TRUNCATINGFILTER   = findOwnMH_S("truncatingFilter", Object[].class, int.class, Object[].class);
+    private static final MethodHandle KNOWNFUNCPROPGUARD = findOwnMH_S("knownFunctionPropertyGuard", boolean.class, Object.class, PropertyMap.class, MethodHandle.class, Object.class, ScriptFunction.class);
+
+    private static final ArrayList<MethodHandle> PROTO_FILTERS = new ArrayList<>();
+
+    /** Method handle for getting the array data */
+    public static final Call GET_ARRAY          = virtualCall(MethodHandles.lookup(), ScriptObject.class, "getArray", ArrayData.class);
+
+    /** Method handle for setting the array data */
+    public static final Call SET_ARRAY          = virtualCall(MethodHandles.lookup(), ScriptObject.class, "setArray", void.class, ArrayData.class);
 
     /** Method handle for getting a function argument at a given index. Used from MapCreator */
     public static final Call GET_ARGUMENT       = virtualCall(MethodHandles.lookup(), ScriptObject.class, "getArgument", Object.class, int.class);
@@ -161,8 +180,23 @@
     public static final Call SET_PROTO_CHECK    = virtualCallNoLookup(ScriptObject.class, "setProtoCheck", void.class, Object.class);
 
     /** Method handle for setting the user accessors of a ScriptObject */
+    //TODO fastpath this
     public static final Call SET_USER_ACCESSORS = virtualCall(MethodHandles.lookup(), ScriptObject.class, "setUserAccessors", void.class, String.class, ScriptFunction.class, ScriptFunction.class);
 
+    static final MethodHandle[] SET_SLOW = new MethodHandle[] {
+        findOwnMH_V("set", void.class, Object.class, int.class, boolean.class),
+        findOwnMH_V("set", void.class, Object.class, long.class, boolean.class),
+        findOwnMH_V("set", void.class, Object.class, double.class, boolean.class),
+        findOwnMH_V("set", void.class, Object.class, Object.class, boolean.class)
+    };
+
+    /** Method handle to reset the map of this ScriptObject */
+    public static final Call SET_MAP = virtualCallNoLookup(ScriptObject.class, "setMap", void.class, PropertyMap.class);
+
+    static final MethodHandle CAS_MAP           = findOwnMH_V("compareAndSetMap", boolean.class, PropertyMap.class, PropertyMap.class);
+    static final MethodHandle EXTENSION_CHECK   = findOwnMH_V("extensionCheck", boolean.class, boolean.class, String.class);
+    static final MethodHandle ENSURE_SPILL_SIZE = findOwnMH_V("ensureSpillSize", Object.class, int.class);
+
     /**
      * Constructor
      */
@@ -194,20 +228,45 @@
      * @param map intial {@link PropertyMap}
      */
     protected ScriptObject(final ScriptObject proto, final PropertyMap map) {
-        if (Context.DEBUG) {
-            ScriptObject.count++;
-        }
-
-        this.arrayData = ArrayData.EMPTY_ARRAY;
-        this.setMap(map == null ? PropertyMap.newMap() : map);
+        this(map);
         this.proto = proto;
-
         if (proto != null) {
             proto.setIsPrototype();
         }
     }
 
     /**
+     * Constructor used to instantiate spill properties directly. Used from
+     * SpillObjectCreator.
+     *
+     * @param map            property maps
+     * @param primitiveSpill primitive spills
+     * @param objectSpill    reference spills
+     */
+    public ScriptObject(final PropertyMap map, final long[] primitiveSpill, final Object[] objectSpill) {
+        this(map);
+        this.primitiveSpill = primitiveSpill;
+        this.objectSpill    = objectSpill;
+        assert primitiveSpill.length == objectSpill.length : " primitive spill pool size is not the same length as object spill pool size";
+        this.spillLength = spillAllocationLength(primitiveSpill.length);
+    }
+
+    private static int alignUp(final int size, final int alignment) {
+        return (((size) + ((alignment) - 1)) & ~((alignment) - 1));
+    }
+
+    /**
+     * Given a number of properties, return the aligned to SPILL_RATE
+     * buffer size required for the smallest spill pool needed to
+     * house them
+     * @param nProperties number of properties
+     * @return property buffer length, a multiple of SPILL_RATE
+     */
+    public static int spillAllocationLength(final int nProperties) {
+        return alignUp(nProperties, SPILL_RATE);
+    }
+
+    /**
      * Copy all properties from the source object with their receiver bound to the source.
      * This function was known as mergeMap
      *
@@ -459,12 +518,12 @@
             }
             return false;
         }
+
         // modifying an existing property
-        final PropertyDescriptor currentDesc = (PropertyDescriptor) current;
+        final PropertyDescriptor currentDesc = (PropertyDescriptor)current;
         final PropertyDescriptor newDesc     = desc;
 
-        if (newDesc.type() == PropertyDescriptor.GENERIC &&
-            ! newDesc.has(CONFIGURABLE) && ! newDesc.has(ENUMERABLE)) {
+        if (newDesc.type() == PropertyDescriptor.GENERIC && !newDesc.has(CONFIGURABLE) && !newDesc.has(ENUMERABLE)) {
             // every descriptor field is absent
             return true;
         }
@@ -474,7 +533,7 @@
             return true;
         }
 
-        if (! currentDesc.isConfigurable()) {
+        if (!currentDesc.isConfigurable()) {
             if (newDesc.has(CONFIGURABLE) && newDesc.isConfigurable()) {
                 // not configurable can not be made configurable
                 if (reject) {
@@ -497,10 +556,11 @@
         Property property = getMap().findProperty(key);
 
         if (currentDesc.type() == PropertyDescriptor.DATA &&
-            (newDesc.type() == PropertyDescriptor.DATA || newDesc.type() == PropertyDescriptor.GENERIC)) {
-            if (! currentDesc.isConfigurable() && ! currentDesc.isWritable()) {
+                (newDesc.type() == PropertyDescriptor.DATA ||
+                 newDesc.type() == PropertyDescriptor.GENERIC)) {
+            if (!currentDesc.isConfigurable() && !currentDesc.isWritable()) {
                 if (newDesc.has(WRITABLE) && newDesc.isWritable() ||
-                    newDesc.has(VALUE) && ! ScriptRuntime.sameValue(currentDesc.getValue(), newDesc.getValue())) {
+                    newDesc.has(VALUE) && !ScriptRuntime.sameValue(currentDesc.getValue(), newDesc.getValue())) {
                     if (reject) {
                         throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
                     }
@@ -509,11 +569,20 @@
             }
 
             final boolean newValue = newDesc.has(VALUE);
-            final Object value     = newValue? newDesc.getValue() : currentDesc.getValue();
+            final Object value     = newValue ? newDesc.getValue() : currentDesc.getValue();
+
             if (newValue && property != null) {
                 // Temporarily clear flags.
                 property = modifyOwnProperty(property, 0);
                 set(key, value, false);
+                //this might change the map if we change types of the property
+                //hence we need to read it again. note that we should probably
+                //have the setter return the new property throughout and in
+                //general respect Property return values from modify and add
+                //functions - which we don't seem to do at all here :-(
+                //There is already a bug filed to generify PropertyAccess so we
+                //can have the setter return e.g. a Property
+                property = getMap().findProperty(key);
             }
 
             if (property == null) {
@@ -527,23 +596,22 @@
         } else if (currentDesc.type() == PropertyDescriptor.ACCESSOR &&
                    (newDesc.type() == PropertyDescriptor.ACCESSOR ||
                     newDesc.type() == PropertyDescriptor.GENERIC)) {
-            if (! currentDesc.isConfigurable()) {
-                if (newDesc.has(PropertyDescriptor.GET) && ! ScriptRuntime.sameValue(currentDesc.getGetter(), newDesc.getGetter()) ||
-                    newDesc.has(PropertyDescriptor.SET) && ! ScriptRuntime.sameValue(currentDesc.getSetter(), newDesc.getSetter())) {
+            if (!currentDesc.isConfigurable()) {
+                if (newDesc.has(PropertyDescriptor.GET) && !ScriptRuntime.sameValue(currentDesc.getGetter(), newDesc.getGetter()) ||
+                    newDesc.has(PropertyDescriptor.SET) && !ScriptRuntime.sameValue(currentDesc.getSetter(), newDesc.getSetter())) {
                     if (reject) {
                         throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
                     }
                     return false;
                 }
             }
-
             // New set the new features.
             modifyOwnProperty(property, propFlags,
                                       newDesc.has(GET) ? newDesc.getGetter() : currentDesc.getGetter(),
                                       newDesc.has(SET) ? newDesc.getSetter() : currentDesc.getSetter());
         } else {
             // changing descriptor type
-            if (! currentDesc.isConfigurable()) {
+            if (!currentDesc.isConfigurable()) {
                 // not configurable can not be made configurable
                 if (reject) {
                     throw typeError(global, "cant.redefine.property", name, ScriptRuntime.safeToString(this));
@@ -555,7 +623,7 @@
 
             // Preserve only configurable and enumerable from current desc
             // if those are not overridden in the new property descriptor.
-            boolean value = newDesc.has(CONFIGURABLE)? newDesc.isConfigurable() : currentDesc.isConfigurable();
+            boolean value = newDesc.has(CONFIGURABLE) ? newDesc.isConfigurable() : currentDesc.isConfigurable();
             if (!value) {
                 propFlags |= Property.NOT_CONFIGURABLE;
             }
@@ -568,7 +636,7 @@
             if (type == PropertyDescriptor.DATA) {
                 // get writable from the new descriptor
                 value = newDesc.has(WRITABLE) && newDesc.isWritable();
-                if (! value) {
+                if (!value) {
                     propFlags |= Property.NOT_WRITABLE;
                 }
 
@@ -771,9 +839,7 @@
      * @return New property.
      */
     public final Property addOwnProperty(final String key, final int propertyFlags, final Object value) {
-        final Property property = addSpillProperty(key, propertyFlags);
-        property.setObjectValue(this, this, value, false);
-        return property;
+        return addSpillProperty(key, propertyFlags, value, true);
     }
 
     /**
@@ -787,10 +853,8 @@
      */
     public final Property addOwnProperty(final Property newProperty) {
         PropertyMap oldMap = getMap();
-
         while (true) {
             final PropertyMap newMap = oldMap.addProperty(newProperty);
-
             if (!compareAndSetMap(oldMap, newMap)) {
                 oldMap = getMap();
                 final Property oldProperty = oldMap.findProperty(newProperty.getKey());
@@ -808,7 +872,8 @@
         // Erase the property field value with undefined. If the property is defined
         // by user-defined accessors, we don't want to call the setter!!
         if (!(property instanceof UserAccessorProperty)) {
-            property.setObjectValue(this, this, UNDEFINED, false);
+            assert property != null;
+            property.setValue(this, this, UNDEFINED, false);
         }
     }
 
@@ -835,9 +900,7 @@
             } else {
                 // delete getter and setter function references so that we don't leak
                 if (property instanceof UserAccessorProperty) {
-                    final UserAccessorProperty uc = (UserAccessorProperty) property;
-                    setSpill(uc.getGetterSlot(), null);
-                    setSpill(uc.getSetterSlot(), null);
+                    ((UserAccessorProperty)property).setAccessors(this, getMap(), null);
                 }
                 return true;
             }
@@ -845,6 +908,29 @@
     }
 
     /**
+     * Fast initialization functions for ScriptFunctions that are strict, to avoid
+     * creating setters that probably aren't used. Inject directly into the spill pool
+     * the defaults for "arguments" and "caller"
+     *
+     * @param key
+     * @param propertyFlags
+     * @param getter
+     * @param setter
+     */
+    protected final void initUserAccessors(final String key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
+        final int slot = spillLength;
+        ensureSpillSize(spillLength); //arguments=slot0, caller=slot0
+        objectSpill[slot] = new UserAccessorProperty.Accessors(getter, setter);
+        PropertyMap oldMap = getMap();
+        Property    newProperty;
+        PropertyMap newMap;
+        do {
+            newProperty = new UserAccessorProperty(key, propertyFlags, slot);
+            newMap = oldMap.addProperty(newProperty);
+        } while (!compareAndSetMap(oldMap, newMap));
+    }
+
+    /**
      * Modify a property in the object
      *
      * @param oldProperty    property to modify
@@ -856,19 +942,27 @@
      */
     public final Property modifyOwnProperty(final Property oldProperty, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
         Property newProperty;
+
         if (oldProperty instanceof UserAccessorProperty) {
-            final UserAccessorProperty uc = (UserAccessorProperty) oldProperty;
-            final int getterSlot = uc.getGetterSlot();
-            final int setterSlot = uc.getSetterSlot();
-            setSpill(getterSlot, getter);
-            setSpill(setterSlot, setter);
-
-            // if just flipping getter and setter with new functions, no need to change property or map
-            if (uc.flags == propertyFlags) {
-                return oldProperty;
+            final UserAccessorProperty uc = (UserAccessorProperty)oldProperty;
+            final int slot = uc.getSlot();
+
+            assert uc.getCurrentType() == Object.class;
+            if (slot >= spillLength) {
+                uc.setAccessors(this, getMap(), new UserAccessorProperty.Accessors(getter, setter));
+            } else {
+                final UserAccessorProperty.Accessors gs = uc.getAccessors(this); //this crashes
+                if (gs == null) {
+                    uc.setAccessors(this, getMap(), new UserAccessorProperty.Accessors(getter, setter));
+                } else {
+                    //reuse existing getter setter for speed
+                    gs.set(getter, setter);
+                    if (uc.getFlags() == propertyFlags) {
+                        return oldProperty;
+                    }
+                }
             }
-
-            newProperty = new UserAccessorProperty(oldProperty.getKey(), propertyFlags, getterSlot, setterSlot);
+            newProperty = new UserAccessorProperty(uc.getKey(), propertyFlags, slot);
         } else {
             // erase old property value and create new user accessor property
             erasePropertyValue(oldProperty);
@@ -901,6 +995,10 @@
      * @return new property
      */
     private Property modifyOwnProperty(final Property oldProperty, final Property newProperty) {
+        if (oldProperty == newProperty) {
+            return newProperty; //nop
+        }
+
         assert newProperty.getKey().equals(oldProperty.getKey()) : "replacing property with different key";
 
         PropertyMap oldMap = getMap();
@@ -937,8 +1035,8 @@
         }
     }
 
-    private static int getIntValue(final FindProperty find) {
-        final MethodHandle getter = find.getGetter(int.class);
+    private static int getIntValue(final FindProperty find, final int programPoint) {
+        final MethodHandle getter = find.getGetter(int.class, programPoint);
         if (getter != null) {
             try {
                 return (int)getter.invokeExact((Object)find.getGetterReceiver());
@@ -949,11 +1047,11 @@
             }
         }
 
-        return ObjectClassGenerator.UNDEFINED_INT;
+        return UNDEFINED_INT;
     }
 
-    private static long getLongValue(final FindProperty find) {
-        final MethodHandle getter = find.getGetter(long.class);
+    private static long getLongValue(final FindProperty find, final int programPoint) {
+        final MethodHandle getter = find.getGetter(long.class, programPoint);
         if (getter != null) {
             try {
                 return (long)getter.invokeExact((Object)find.getGetterReceiver());
@@ -964,11 +1062,11 @@
             }
         }
 
-        return ObjectClassGenerator.UNDEFINED_LONG;
+        return UNDEFINED_LONG;
     }
 
-    private static double getDoubleValue(final FindProperty find) {
-        final MethodHandle getter = find.getGetter(double.class);
+    private static double getDoubleValue(final FindProperty find, final int programPoint) {
+        final MethodHandle getter = find.getGetter(double.class, programPoint);
         if (getter != null) {
             try {
                 return (double)getter.invokeExact((Object)find.getGetterReceiver());
@@ -979,7 +1077,7 @@
             }
         }
 
-        return ObjectClassGenerator.UNDEFINED_DOUBLE;
+        return UNDEFINED_DOUBLE;
     }
 
     /**
@@ -1096,14 +1194,12 @@
      * @param newMap Replacement map.
      * @return true if the operation succeeded.
      */
-    protected synchronized final boolean compareAndSetMap(final PropertyMap oldMap, final PropertyMap newMap) {
-        final boolean update = oldMap == this.map;
-
-        if (update) {
+    protected final boolean compareAndSetMap(final PropertyMap oldMap, final PropertyMap newMap) {
+        if (oldMap == this.map) {
             this.map = newMap;
+            return true;
         }
-
-        return update;
+        return false;
      }
 
     /**
@@ -1118,7 +1214,7 @@
      * Set the __proto__ of an object.
      * @param newProto new __proto__ to set.
      */
-    public synchronized final void setProto(final ScriptObject newProto) {
+    public final void setProto(final ScriptObject newProto) {
         final ScriptObject oldProto = proto;
         map = map.changeProto(oldProto, newProto);
 
@@ -1647,7 +1743,7 @@
             return c > 2 ? findGetMethod(desc, request, operator) : findGetIndexMethod(desc, request);
         case "setProp":
         case "setElem":
-            return c > 2 ? findSetMethod(desc, request) : findSetIndexMethod(desc);
+            return c > 2 ? findSetMethod(desc, request) : findSetIndexMethod(desc, request);
         case "call":
             return findCallMethod(desc, request);
         case "new":
@@ -1740,12 +1836,12 @@
             return methodHandle;
         }
         final int listIndex = depth - 1; // We don't need 0-deep walker
-        MethodHandle filter = listIndex < protoFilters.size() ? protoFilters.get(listIndex) : null;
+        MethodHandle filter = listIndex < PROTO_FILTERS.size() ? PROTO_FILTERS.get(listIndex) : null;
 
         if(filter == null) {
             filter = addProtoFilter(GETPROTO, depth - 1);
-            protoFilters.add(null);
-            protoFilters.set(listIndex, filter);
+            PROTO_FILTERS.add(null);
+            PROTO_FILTERS.set(listIndex, filter);
         }
 
         return MH.filterArguments(methodHandle, 0, filter.asType(filter.type().changeReturnType(methodHandle.type().parameterType(0))));
@@ -1771,31 +1867,37 @@
 
         if (find == null) {
             if (PROTO_PROPERTY_NAME.equals(name)) {
-                return new GuardedInvocation(GETPROTO, NashornGuards.getScriptObjectGuard());
+                return new GuardedInvocation(GETPROTO, null, null, ClassCastException.class);
             }
 
-            if ("getProp".equals(operator)) {
+            switch (operator) {
+            case "getProp":
                 return noSuchProperty(desc, request);
-            } else if ("getMethod".equals(operator)) {
+            case "getMethod":
                 return noSuchMethod(desc, request);
-            } else if ("getElem".equals(operator)) {
+            case "getElem":
                 return createEmptyGetter(desc, name);
+            default:
+                throw new AssertionError(); // never invoked with any other operation
             }
-            throw new AssertionError(); // never invoked with any other operation
-        }
+       }
 
         final Class<?> returnType = desc.getMethodType().returnType();
         final Property property = find.getProperty();
-        methodHandle = find.getGetter(returnType);
-
-        final boolean noGuard = ObjectClassGenerator.OBJECT_FIELDS_ONLY && NashornCallSiteDescriptor.isFastScope(desc) && !property.canChangeType();
+
+        final int programPoint = NashornCallSiteDescriptor.isOptimistic(desc) ?
+                NashornCallSiteDescriptor.getProgramPoint(desc) :
+                UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
+        methodHandle = find.getGetter(returnType, programPoint);
+
+        final boolean noGuard = OBJECT_FIELDS_ONLY && NashornCallSiteDescriptor.isFastScope(desc) && !property.canChangeType();
         // getMap() is fine as we have the prototype switchpoint depending on where the property was found
         final MethodHandle guard = noGuard ? null : NashornGuards.getMapGuard(getMap());
 
         if (methodHandle != null) {
-            assert methodHandle.type().returnType().equals(returnType);
+            assert methodHandle.type().returnType().equals(returnType) : "returntype mismatch for getter " + methodHandle.type().returnType() + " != " + returnType;
             if (find.isSelf()) {
-                return new GuardedInvocation(methodHandle, guard);
+                return new GuardedInvocation(methodHandle, guard, null, noGuard ? null : ClassCastException.class);
             }
 
             if (!property.hasGetterFunction(find.getOwner())) {
@@ -1805,17 +1907,16 @@
                         addProtoFilter(methodHandle, find.getProtoChainLength()) :
                         bindTo(methodHandle, find.getOwner());
             }
-            return new GuardedInvocation(methodHandle, noGuard ? null : getMap().getProtoGetSwitchPoint(proto, name), guard);
+            return new GuardedInvocation(methodHandle, guard, noGuard ? null : getMap().getProtoGetSwitchPoint(proto, name), noGuard ? null : ClassCastException.class);
         }
 
         assert !NashornCallSiteDescriptor.isFastScope(desc);
-        return new GuardedInvocation(Lookup.emptyGetter(returnType), getMap().getProtoGetSwitchPoint(proto, name), guard);
+        return new GuardedInvocation(Lookup.emptyGetter(returnType), guard, getMap().getProtoGetSwitchPoint(proto, name), ClassCastException.class);
     }
 
     private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name, final boolean isMethod) {
-        final MethodHandle invoker = MH.insertArguments(MEGAMORPHIC_GET, 1, name, isMethod);
-        final MethodHandle guard = getScriptObjectGuard(desc.getMethodType());
-        return new GuardedInvocation(invoker, guard);
+        ObjectClassGenerator.LOG.warning("Megamorphic getter: " + desc + " " + name + " " +isMethod);
+        return new GuardedInvocation(MH.insertArguments(MEGAMORPHIC_GET, 1, name, isMethod), null, null, ClassCastException.class);
     }
 
     @SuppressWarnings("unused")
@@ -1826,7 +1927,7 @@
             return getObjectValue(find);
         }
 
-        return isMethod ? getNoSuchMethod(key) : invokeNoSuchProperty(key);
+        return isMethod ? getNoSuchMethod(key, INVALID_PROGRAM_POINT) : invokeNoSuchProperty(key, INVALID_PROGRAM_POINT);
     }
 
     /**
@@ -1838,31 +1939,43 @@
      * @return GuardedInvocation to be invoked at call site.
      */
     protected GuardedInvocation findGetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) {
-        return findGetIndexMethod(desc.getMethodType());
+        final MethodType callType    = desc.getMethodType();
+        final Class<?>   returnType  = callType.returnType();
+        final Class<?>   returnClass = returnType.isPrimitive() ? returnType : Object.class;
+        final Class<?>   keyClass    = callType.parameterType(1);
+
+        final String name;
+        if (returnClass.isPrimitive()) {
+            //turn e.g. get with a double into getDouble
+            final String returnTypeName = returnClass.getName();
+            name = "get" + Character.toUpperCase(returnTypeName.charAt(0)) + returnTypeName.substring(1, returnTypeName.length());
+        } else {
+            name = "get";
+        }
+
+        final MethodHandle mh = findGetIndexMethodHandle(returnClass, name, keyClass, desc);
+        return new GuardedInvocation(mh, null, null, ClassCastException.class);
     }
 
     /**
-     * Find the appropriate GETINDEX method for an invoke dynamic call.
-     *
-     * @param callType the call site method type
-     * @return GuardedInvocation to be invoked at call site.
+     * Find a handle for a getIndex method
+     * @param returnType     return type for getter
+     * @param name           name
+     * @param elementType    index type for getter
+     * @param desc           call site descriptor
+     * @return method handle for getter
      */
-    private static GuardedInvocation findGetIndexMethod(final MethodType callType) {
-        final Class<?> returnClass = callType.returnType();
-        final Class<?> keyClass    = callType.parameterType(1);
-
-        String name = "get";
-        if (returnClass.isPrimitive()) {
-            //turn e.g. get with a double into getDouble
-            final String returnTypeName = returnClass.getName();
-            name += Character.toUpperCase(returnTypeName.charAt(0)) + returnTypeName.substring(1, returnTypeName.length());
+    protected MethodHandle findGetIndexMethodHandle(final Class<?> returnType, String name, final Class<?> elementType, final CallSiteDescriptor desc) {
+        if (!returnType.isPrimitive()) {
+            return findOwnMH_V(getClass(), name, returnType, elementType);
         }
 
-        return new GuardedInvocation(findOwnMH(name, returnClass, keyClass), getScriptObjectGuard(callType));
-    }
-
-    private static MethodHandle getScriptObjectGuard(final MethodType type) {
-        return ScriptObject.class.isAssignableFrom(type.parameterType(0)) ? null : NashornGuards.getScriptObjectGuard();
+        return MH.insertArguments(
+                findOwnMH_V(getClass(), name, returnType, elementType, int.class),
+                2,
+                NashornCallSiteDescriptor.isOptimistic(desc) ?
+                        NashornCallSiteDescriptor.getProgramPoint(desc) :
+                        INVALID_PROGRAM_POINT);
     }
 
     /**
@@ -1900,13 +2013,13 @@
         }
 
         if (find != null) {
-            if(!find.getProperty().isWritable()) {
+            if (!find.getProperty().isWritable()) {
                 // Existing, non-writable property
                 return createEmptySetMethod(desc, "property.not.writable", true);
             }
         } else {
             if (PROTO_PROPERTY_NAME.equals(name)) {
-                return new GuardedInvocation(SETPROTOCHECK, NashornGuards.getScriptObjectGuard());
+                return new GuardedInvocation(SETPROTOCHECK, null, null, ClassCastException.class);
             } else if (! isExtensible()) {
                 return createEmptySetMethod(desc, "object.non.extensible", false);
             }
@@ -1922,89 +2035,39 @@
            }
            assert canBeFastScope || !NashornCallSiteDescriptor.isFastScope(desc);
            final PropertyMap myMap = getMap();
-           return new GuardedInvocation(Lookup.EMPTY_SETTER, myMap.getProtoGetSwitchPoint(proto, name), NashornGuards.getMapGuard(myMap));
-    }
-
-    @SuppressWarnings("unused")
-    private static void setField(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final MethodHandle setter, final Object self, final Object value) throws Throwable {
-        final ScriptObject obj = (ScriptObject)self;
-        final boolean isStrict = NashornCallSiteDescriptor.isStrict(desc);
-        if (!obj.isExtensible()) {
-            if (isStrict) {
-                throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(obj));
-            }
-        } else if (obj.compareAndSetMap(oldMap, newMap)) {
-            setter.invokeExact(self, value);
-        } else {
-            obj.set(desc.getNameToken(CallSiteDescriptor.NAME_OPERAND), value, isStrict);
-        }
-    }
-
-    @SuppressWarnings("unused")
-    private static void setSpill(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final int index, final Object self, final Object value) {
-        final ScriptObject obj = (ScriptObject)self;
-        if (obj.trySetSpill(desc, oldMap, newMap, value)) {
-            obj.spill[index] = value;
-        }
-    }
-
-    private boolean trySetSpill(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final Object value) {
-        final boolean isStrict = NashornCallSiteDescriptor.isStrict(desc);
-        if (!isExtensible() && isStrict) {
-            throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(this));
-        } else if (compareAndSetMap(oldMap, newMap)) {
-            return true;
-        } else {
-            set(desc.getNameToken(CallSiteDescriptor.NAME_OPERAND), value, isStrict);
-            return false;
-        }
+           return new GuardedInvocation(Lookup.EMPTY_SETTER, NashornGuards.getMapGuard(myMap), myMap.getProtoGetSwitchPoint(proto, name), ClassCastException.class);
     }
 
     @SuppressWarnings("unused")
-    private static void setSpillWithNew(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final int index, final Object self, final Object value) {
-        final ScriptObject obj      = (ScriptObject)self;
-        final boolean      isStrict = NashornCallSiteDescriptor.isStrict(desc);
-
-        if (!obj.isExtensible()) {
-            if (isStrict) {
-                throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(obj));
-            }
-        } else if (obj.compareAndSetMap(oldMap, newMap)) {
-            obj.spill = new Object[SPILL_RATE];
-            obj.spill[index] = value;
+    private boolean extensionCheck(final boolean isStrict, final String name) {
+        if (isExtensible()) {
+            return true; //go on and do the set. this is our guard
+        } else if (isStrict) {
+            //throw an error for attempting to do the set in strict mode
+            throw typeError("object.non.extensible", name, ScriptRuntime.safeToString(this));
         } else {
-            obj.set(desc.getNameToken(2), value, isStrict);
+            //not extensible, non strict - this is a nop
+            return false;
         }
     }
 
-    @SuppressWarnings("unused")
-    private static void setSpillWithGrow(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final int index, final int newLength, final Object self, final Object value) {
-        final ScriptObject obj      = (ScriptObject)self;
-        final boolean      isStrict = NashornCallSiteDescriptor.isStrict(desc);
-
-        if (!obj.isExtensible()) {
-            if (isStrict) {
-                throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(obj));
-            }
-        } else if (obj.compareAndSetMap(oldMap, newMap)) {
-            final int oldLength = obj.spill.length;
-            final Object[] newSpill = new Object[newLength];
-            System.arraycopy(obj.spill, 0, newSpill, 0, oldLength);
-            obj.spill = newSpill;
-            obj.spill[index] = value;
-        } else {
-            obj.set(desc.getNameToken(2), value, isStrict);
-        }
-    }
-
-    private static GuardedInvocation findMegaMorphicSetMethod(final CallSiteDescriptor desc, final String name) {
+    private GuardedInvocation findMegaMorphicSetMethod(final CallSiteDescriptor desc, final String name) {
         final MethodType type = desc.getMethodType().insertParameterTypes(1, Object.class);
-        final GuardedInvocation inv = findSetIndexMethod(type, NashornCallSiteDescriptor.isStrict(desc));
+        final GuardedInvocation inv = findSetIndexMethod(getClass(), type, NashornCallSiteDescriptor.isStrict(desc));
         return inv.replaceMethods(MH.insertArguments(inv.getInvocation(), 1, name), inv.getGuard());
     }
 
-    private static GuardedInvocation findSetIndexMethod(final CallSiteDescriptor desc) { // array, index, value
-        return findSetIndexMethod(desc.getMethodType(), NashornCallSiteDescriptor.isStrict(desc));
+    /**
+     * Lookup function for the set index method, available for subclasses as well, e.g. {@link NativeArray}
+     * provides special quick accessor linkage for continuous arrays that are represented as Java arrays
+     *
+     * @param desc    call site descriptor
+     * @param request link request
+     *
+     * @return GuardedInvocation to be invoked at call site.
+     */
+    protected GuardedInvocation findSetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) { // array, index, value
+        return findSetIndexMethod(getClass(), desc.getMethodType(), NashornCallSiteDescriptor.isStrict(desc));
     }
 
     /**
@@ -2015,16 +2078,15 @@
      *
      * @return GuardedInvocation to be invoked at call site.
      */
-    private static GuardedInvocation findSetIndexMethod(final MethodType callType, final boolean isStrict) {
+    private static GuardedInvocation findSetIndexMethod(final Class<? extends ScriptObject> clazz, final MethodType callType, final boolean isStrict) {
         assert callType.parameterCount() == 3;
-
         final Class<?>   keyClass   = callType.parameterType(1);
         final Class<?>   valueClass = callType.parameterType(2);
 
-        MethodHandle methodHandle = findOwnMH("set", void.class, keyClass, valueClass, boolean.class);
+        MethodHandle methodHandle = findOwnMH_V(clazz, "set", void.class, keyClass, valueClass, boolean.class);
         methodHandle = MH.insertArguments(methodHandle, 3, isStrict);
 
-        return new GuardedInvocation(methodHandle, getScriptObjectGuard(callType));
+        return new GuardedInvocation(methodHandle, null, null, ClassCastException.class);
     }
 
     /**
@@ -2052,7 +2114,7 @@
         // TODO: It'd be awesome if we could bind "name" without binding "this".
         return new GuardedInvocation(MH.dropArguments(MH.constant(ScriptFunction.class,
                 func.makeBoundFunction(thiz, new Object[] { name })), 0, Object.class),
-                null, NashornGuards.getMapGuard(getMap()));
+                NashornGuards.getMapGuard(getMap()), null, ClassCastException.class);
     }
 
     /**
@@ -2081,9 +2143,11 @@
                 if (scopeAccess && func.isStrict()) {
                     methodHandle = bindTo(methodHandle, UNDEFINED);
                 }
-                return new GuardedInvocation(methodHandle,
-                        find.isInherited()? getMap().getProtoGetSwitchPoint(proto, NO_SUCH_PROPERTY_NAME) : null,
-                        getKnownFunctionPropertyGuard(getMap(), find.getGetter(Object.class), find.getOwner(), func));
+                return new GuardedInvocation(
+                        methodHandle,
+                        getKnownFunctionPropertyGuard(getMap(), find.getGetter(Object.class, INVALID_PROGRAM_POINT), find.getOwner(), func),
+                        find.isInherited() ? getMap().getProtoGetSwitchPoint(proto, NO_SUCH_PROPERTY_NAME) : null,
+                        ClassCastException.class);
             }
         }
 
@@ -2099,34 +2163,41 @@
      * @param name Name of property.
      * @return Result from call.
      */
-    protected Object invokeNoSuchProperty(final String name) {
+    protected Object invokeNoSuchProperty(final String name, final int programPoint) {
         final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true);
 
+        Object ret = UNDEFINED;
+
         if (find != null) {
             final Object func = getObjectValue(find);
 
             if (func instanceof ScriptFunction) {
-                return ScriptRuntime.apply((ScriptFunction)func, this, name);
+                ret = ScriptRuntime.apply((ScriptFunction)func, this, name);
             }
         }
 
-        return UNDEFINED;
+        if (isValid(programPoint)) {
+            throw new UnwarrantedOptimismException(ret, programPoint);
+        }
+
+        return ret;
     }
 
+
     /**
      * Get __noSuchMethod__ as a function bound to this object and {@code name} if it is defined.
      * @param name the method name
      * @return the bound function, or undefined
      */
-    private Object getNoSuchMethod(final String name) {
+    private Object getNoSuchMethod(final String name, final int programPoint) {
         final FindProperty find = findProperty(NO_SUCH_METHOD_NAME, true);
 
         if (find == null) {
-            return invokeNoSuchProperty(name);
+            return invokeNoSuchProperty(name, programPoint);
         }
 
         final Object value = getObjectValue(find);
-        if (! (value instanceof ScriptFunction)) {
+        if (!(value instanceof ScriptFunction)) {
             return UNDEFINED;
         }
 
@@ -2134,7 +2205,10 @@
     }
 
     private GuardedInvocation createEmptyGetter(final CallSiteDescriptor desc, final String name) {
-        return new GuardedInvocation(Lookup.emptyGetter(desc.getMethodType().returnType()), getMap().getProtoGetSwitchPoint(proto, name), NashornGuards.getMapGuard(getMap()));
+        if(NashornCallSiteDescriptor.isOptimistic(desc)) {
+            throw new UnwarrantedOptimismException(UNDEFINED, NashornCallSiteDescriptor.getProgramPoint(desc), Type.OBJECT);
+        }
+        return new GuardedInvocation(Lookup.emptyGetter(desc.getMethodType().returnType()), NashornGuards.getMapGuard(getMap()), getMap().getProtoGetSwitchPoint(proto, name), ClassCastException.class);
     }
 
     private abstract static class ScriptObjectIterator <T extends Object> implements Iterator<T> {
@@ -2208,48 +2282,37 @@
      * @param propertyFlags Property flags.
      * @return Added property.
      */
-    private Property addSpillProperty(final String key, final int propertyFlags) {
-        int fieldCount   = getMap().getFieldCount();
-        int fieldMaximum = getMap().getFieldMaximum();
+    private Property addSpillProperty(final String key, final int propertyFlags, final Object value, final boolean hasInitialValue) {
+        final PropertyMap propertyMap = getMap();
+        final int fieldCount = propertyMap.getFieldCount();
+        final int fieldMax = propertyMap.getFieldMaximum();
+
         Property property;
-
-        if (fieldCount < fieldMaximum) {
-            property = new AccessorProperty(key, propertyFlags & ~Property.IS_SPILL, getClass(), fieldCount);
+        if (fieldCount < fieldMax) {
+            property = hasInitialValue ?
+                new AccessorProperty(key, propertyFlags, fieldCount, this, value) :
+                new AccessorProperty(key, propertyFlags, getClass(), fieldCount);
             notifyPropertyAdded(this, property);
             property = addOwnProperty(property);
         } else {
-            int i = getMap().getSpillLength();
-            property = new AccessorProperty(key, propertyFlags | Property.IS_SPILL, i);
+            final int spillCount = propertyMap.getSpillLength();
+            property = hasInitialValue ?
+                new SpillProperty(key, propertyFlags, spillCount, this, value) :
+                new SpillProperty(key, propertyFlags, spillCount);
             notifyPropertyAdded(this, property);
             property = addOwnProperty(property);
-            i = property.getSlot();
-
-            final int newLength = (i + SPILL_RATE) / SPILL_RATE * SPILL_RATE;
-
-            if (spill == null || newLength > spill.length) {
-                final Object[] newSpill = new Object[newLength];
-
-                if (spill != null) {
-                    System.arraycopy(spill, 0, newSpill, 0, spill.length);
-                }
-
-                spill = newSpill;
-            }
+            ensureSpillSize(property.getSlot());
         }
-
         return property;
     }
 
-
     /**
      * Add a spill entry for the given key.
      * @param key Property key.
      * @return Setter method handle.
      */
-    MethodHandle addSpill(final String key) {
-        final Property spillProperty = addSpillProperty(key, 0);
-        final Class<?> type = Object.class;
-        return spillProperty.getSetter(type, getMap()); //TODO specfields
+    MethodHandle addSpill(final Class<?> type, final String key) {
+        return addSpillProperty(key, 0, null, false).getSetter(OBJECT_FIELDS_ONLY ? Object.class : type, getMap());
     }
 
     /**
@@ -2282,9 +2345,8 @@
      * @return method handle with adjusted arguments
      */
     public static MethodHandle pairArguments(final MethodHandle methodHandle, final MethodType callType, final Boolean callerVarArg) {
-
         final MethodType methodType = methodHandle.type();
-        if (methodType.equals(callType)) {
+        if (methodType.equals(callType.changeReturnType(methodType.returnType()))) {
             return methodHandle;
         }
 
@@ -2295,22 +2357,6 @@
         final boolean isCallerVarArg = callerVarArg != null ? callerVarArg.booleanValue() : (callCount > 0 &&
                 callType.parameterType(callCount - 1).isArray());
 
-        if (callCount < parameterCount) {
-            final int      missingArgs = parameterCount - callCount;
-            final Object[] fillers     = new Object[missingArgs];
-
-            Arrays.fill(fillers, UNDEFINED);
-
-            if (isCalleeVarArg) {
-                fillers[missingArgs - 1] = new Object[0];
-            }
-
-            return MH.insertArguments(
-                methodHandle,
-                parameterCount - missingArgs,
-                fillers);
-        }
-
         if (isCalleeVarArg) {
             return isCallerVarArg ?
                 methodHandle :
@@ -2332,6 +2378,22 @@
                 );
         }
 
+        if (callCount < parameterCount) {
+            final int      missingArgs = parameterCount - callCount;
+            final Object[] fillers     = new Object[missingArgs];
+
+            Arrays.fill(fillers, UNDEFINED);
+
+            if (isCalleeVarArg) {
+                fillers[missingArgs - 1] = ScriptRuntime.EMPTY_ARRAY;
+            }
+
+            return MH.insertArguments(
+                methodHandle,
+                parameterCount - missingArgs,
+                fillers);
+        }
+
         if (callCount > parameterCount) {
             final int discardedArgs = callCount - parameterCount;
 
@@ -2348,15 +2410,13 @@
     private static Object[] truncatingFilter(final int n, final Object[] array) {
         final int length = array == null ? 0 : array.length;
         if (n == length) {
-            return array == null ? new Object[0] : array;
+            return array == null ? ScriptRuntime.EMPTY_ARRAY : array;
         }
 
         final Object[] newArray = new Object[n];
 
         if (array != null) {
-            for (int i = 0; i < n && i < length; i++) {
-                newArray[i] = array[i];
-            }
+            System.arraycopy(array, 0, newArray, 0, Math.min(n, length));
         }
 
         if (length < n) {
@@ -2416,14 +2476,14 @@
        }
     }
 
-    private int getInt(final int index, final String key) {
+    private int getInt(final int index, final String key, final int programPoint) {
         if (isValidArrayIndex(index)) {
             for (ScriptObject object = this; ; ) {
                 if (object.getMap().containsArrayKeys()) {
                     final FindProperty find = object.findProperty(key, false, false, this);
 
                     if (find != null) {
-                        return getIntValue(find);
+                        return getIntValue(find, programPoint);
                     }
                 }
 
@@ -2434,77 +2494,78 @@
                 final ArrayData array = object.getArray();
 
                 if (array.has(index)) {
-                    return array.getInt(index);
+                    return isValid(programPoint) ?
+                        array.getIntOptimistic(index, programPoint) :
+                        array.getInt(index);
                 }
             }
         } else {
             final FindProperty find = findProperty(key, true);
 
             if (find != null) {
-                return getIntValue(find);
+                return getIntValue(find, programPoint);
             }
         }
 
-        return JSType.toInt32(invokeNoSuchProperty(key));
+        return JSType.toInt32(invokeNoSuchProperty(key, programPoint));
     }
 
     @Override
-    public int getInt(final Object key) {
-        final Object primitiveKey = JSType.toPrimitive(key, String.class);
-        final int index = getArrayIndex(primitiveKey);
-        final ArrayData array = getArray();
+    public int getInt(final Object key, final int programPoint) {
+        final Object    primitiveKey = JSType.toPrimitive(key, String.class);
+        final int       index        = getArrayIndex(primitiveKey);
+        final ArrayData array        = getArray();
 
         if (array.has(index)) {
-            return array.getInt(index);
+            return isValid(programPoint) ? array.getIntOptimistic(index, programPoint) : array.getInt(index);
         }
 
-        return getInt(index, JSType.toString(primitiveKey));
+        return getInt(index, JSType.toString(primitiveKey), programPoint);
     }
 
     @Override
-    public int getInt(final double key) {
-        final int index = getArrayIndex(key);
+    public int getInt(final double key, final int programPoint) {
+        final int       index = getArrayIndex(key);
         final ArrayData array = getArray();
 
         if (array.has(index)) {
-            return array.getInt(index);
+            return isValid(programPoint) ? array.getIntOptimistic(index, programPoint) : array.getInt(index);
         }
 
-        return getInt(index, JSType.toString(key));
+        return getInt(index, JSType.toString(key), programPoint);
     }
 
     @Override
-    public int getInt(final long key) {
-        final int index = getArrayIndex(key);
+    public int getInt(final long key, final int programPoint) {
+        final int       index = getArrayIndex(key);
         final ArrayData array = getArray();
 
         if (array.has(index)) {
-            return array.getInt(index);
+            return isValid(programPoint) ? array.getIntOptimistic(index, programPoint) : array.getInt(index);
         }
 
-        return getInt(index, JSType.toString(key));
+        return getInt(index, JSType.toString(key), programPoint);
     }
 
     @Override
-    public int getInt(final int key) {
-        final int index = getArrayIndex(key);
+    public int getInt(final int key, final int programPoint) {
+        final int       index = getArrayIndex(key);
         final ArrayData array = getArray();
 
         if (array.has(index)) {
-            return array.getInt(index);
+            return isValid(programPoint) ? array.getIntOptimistic(key, programPoint) : array.getInt(key);
         }
 
-        return getInt(index, JSType.toString(key));
+        return getInt(index, JSType.toString(key), programPoint);
     }
 
-    private long getLong(final int index, final String key) {
+    private long getLong(final int index, final String key, final int programPoint) {
         if (isValidArrayIndex(index)) {
             for (ScriptObject object = this; ; ) {
                 if (object.getMap().containsArrayKeys()) {
                     final FindProperty find = object.findProperty(key, false, false, this);
-
                     if (find != null) {
-                        return getLongValue(find);
+                        return getLongValue(find, programPoint);
                     }
                 }
 
@@ -2515,77 +2576,78 @@
                 final ArrayData array = object.getArray();
 
                 if (array.has(index)) {
-                    return array.getLong(index);
+                    return isValid(programPoint) ?
+                        array.getLongOptimistic(index, programPoint) :
+                        array.getLong(index);
                 }
             }
         } else {
             final FindProperty find = findProperty(key, true);
 
             if (find != null) {
-                return getLongValue(find);
+                return getLongValue(find, programPoint);
             }
         }
 
-        return JSType.toLong(invokeNoSuchProperty(key));
+        return JSType.toLong(invokeNoSuchProperty(key, programPoint));
     }
 
     @Override
-    public long getLong(final Object key) {
-        final Object primitiveKey = JSType.toPrimitive(key, String.class);
-        final int index = getArrayIndex(primitiveKey);
-        final ArrayData array = getArray();
+    public long getLong(final Object key, final int programPoint) {
+        final Object    primitiveKey = JSType.toPrimitive(key, String.class);
+        final int       index        = getArrayIndex(primitiveKey);
+        final ArrayData array        = getArray();
 
         if (array.has(index)) {
-            return array.getLong(index);
+            return isValid(programPoint) ? array.getLongOptimistic(index, programPoint) : array.getLong(index);
         }
 
-        return getLong(index, JSType.toString(primitiveKey));
+        return getLong(index, JSType.toString(primitiveKey), programPoint);
     }
 
     @Override
-    public long getLong(final double key) {
-        final int index = getArrayIndex(key);
+    public long getLong(final double key, final int programPoint) {
+        final int       index = getArrayIndex(key);
         final ArrayData array = getArray();
 
         if (array.has(index)) {
-            return array.getLong(index);
+            return isValid(programPoint) ? array.getLongOptimistic(index, programPoint) : array.getLong(index);
         }
 
-        return getLong(index, JSType.toString(key));
+        return getLong(index, JSType.toString(key), programPoint);
     }
 
     @Override
-    public long getLong(final long key) {
-        final int index = getArrayIndex(key);
+    public long getLong(final long key, final int programPoint) {
+        final int       index = getArrayIndex(key);
         final ArrayData array = getArray();
 
         if (array.has(index)) {
-            return array.getLong(index);
+            return isValid(programPoint) ? array.getLongOptimistic(index, programPoint) : array.getLong(index);
         }
 
-        return getLong(index, JSType.toString(key));
+        return getLong(index, JSType.toString(key), programPoint);
     }
 
     @Override
-    public long getLong(final int key) {
-        final int index = getArrayIndex(key);
+    public long getLong(final int key, final int programPoint) {
+        final int       index = getArrayIndex(key);
         final ArrayData array = getArray();
 
         if (array.has(index)) {
-            return array.getLong(index);
+            return isValid(programPoint) ? array.getLongOptimistic(key, programPoint) : array.getLong(key);
         }
 
-        return getLong(index, JSType.toString(key));
+        return getLong(index, JSType.toString(key), programPoint);
     }
 
-    private double getDouble(final int index, final String key) {
+    private double getDouble(final int index, final String key, final int programPoint) {
         if (isValidArrayIndex(index)) {
             for (ScriptObject object = this; ; ) {
                 if (object.getMap().containsArrayKeys()) {
                     final FindProperty find = object.findProperty(key, false, false, this);
-
                     if (find != null) {
-                        return getDoubleValue(find);
+                        return getDoubleValue(find, programPoint);
                     }
                 }
 
@@ -2596,67 +2658,69 @@
                 final ArrayData array = object.getArray();
 
                 if (array.has(index)) {
-                    return array.getDouble(index);
+                    return isValid(programPoint) ?
+                        array.getDoubleOptimistic(index, programPoint) :
+                        array.getDouble(index);
                 }
             }
         } else {
             final FindProperty find = findProperty(key, true);
 
             if (find != null) {
-                return getDoubleValue(find);
+                return getDoubleValue(find, programPoint);
             }
         }
 
-        return JSType.toNumber(invokeNoSuchProperty(key));
+        return JSType.toNumber(invokeNoSuchProperty(key, INVALID_PROGRAM_POINT));
     }
 
     @Override
-    public double getDouble(final Object key) {
-        final Object primitiveKey = JSType.toPrimitive(key, String.class);
-        final int index = getArrayIndex(primitiveKey);
-        final ArrayData array = getArray();
+    public double getDouble(final Object key, final int programPoint) {
+        final Object    primitiveKey = JSType.toPrimitive(key, String.class);
+        final int       index        = getArrayIndex(primitiveKey);
+        final ArrayData array        = getArray();
 
         if (array.has(index)) {
-            return array.getDouble(index);
+            return isValid(programPoint) ? array.getDoubleOptimistic(index, programPoint) : array.getDouble(index);
         }
 
-        return getDouble(index, JSType.toString(primitiveKey));
+        return getDouble(index, JSType.toString(primitiveKey), programPoint);
     }
 
     @Override
-    public double getDouble(final double key) {
-        final int index = getArrayIndex(key);
+    public double getDouble(final double key, final int programPoint) {
+        final int       index = getArrayIndex(key);
         final ArrayData array = getArray();
 
         if (array.has(index)) {
-            return array.getDouble(index);
+            return isValid(programPoint) ? array.getDoubleOptimistic(index, programPoint) : array.getDouble(index);
         }
 
-        return getDouble(index, JSType.toString(key));
+        return getDouble(index, JSType.toString(key), programPoint);
     }
 
     @Override
-    public double getDouble(final long key) {
-        final int index = getArrayIndex(key);
+    public double getDouble(final long key, final int programPoint) {
+        final int       index = getArrayIndex(key);
         final ArrayData array = getArray();
 
         if (array.has(index)) {
-            return array.getDouble(index);
+            return isValid(programPoint) ? array.getDoubleOptimistic(index, programPoint) : array.getDouble(index);
         }
 
-        return getDouble(index, JSType.toString(key));
+        return getDouble(index, JSType.toString(key), programPoint);
     }
 
     @Override
-    public double getDouble(final int key) {
-        final int index = getArrayIndex(key);
+    public double getDouble(final int key, final int programPoint) {
+        final int       index = getArrayIndex(key);
         final ArrayData array = getArray();
 
         if (array.has(index)) {
-            return array.getDouble(index);
+            return isValid(programPoint) ? array.getDoubleOptimistic(key, programPoint) : array.getDouble(key);
         }
 
-        return getDouble(index, JSType.toString(key));
+        return getDouble(index, JSType.toString(key), programPoint);
     }
 
     private Object get(final int index, final String key) {
@@ -2688,14 +2752,14 @@
             }
         }
 
-        return invokeNoSuchProperty(key);
+        return invokeNoSuchProperty(key, INVALID_PROGRAM_POINT);
     }
 
     @Override
     public Object get(final Object key) {
-        final Object primitiveKey = JSType.toPrimitive(key, String.class);
-        final int index = getArrayIndex(primitiveKey);
-        final ArrayData array = getArray();
+        final Object    primitiveKey = JSType.toPrimitive(key, String.class);
+        final int       index        = getArrayIndex(primitiveKey);
+        final ArrayData array        = getArray();
 
         if (array.has(index)) {
             return array.getObject(index);
@@ -2740,58 +2804,114 @@
         return get(index, JSType.toString(key));
     }
 
-    /**
-     * Handle when an array doesn't have a slot - possibly grow and/or convert array.
-     *
-     * @param index  key as index
-     * @param value  element value
-     * @param strict are we in strict mode
-     */
-    private void doesNotHave(final int index, final Object value, final boolean strict) {
-        final long oldLength = getArray().length();
-        final long longIndex = ArrayIndex.toLongIndex(index);
-
+    private boolean doesNotHaveCheckArrayKeys(final long longIndex, final int value, final boolean strict) {
         if (getMap().containsArrayKeys()) {
-            final String key = JSType.toString(longIndex);
+            final String       key  = JSType.toString(longIndex);
             final FindProperty find = findProperty(key, true);
-
+            if (find != null) {
+                setObject(find, strict, key, value);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean doesNotHaveCheckArrayKeys(final long longIndex, final long value, final boolean strict) {
+        if (getMap().containsArrayKeys()) {
+            final String       key  = JSType.toString(longIndex);
+            final FindProperty find = findProperty(key, true);
             if (find != null) {
                 setObject(find, strict, key, value);
-                return;
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean doesNotHaveCheckArrayKeys(final long longIndex, final double value, final boolean strict) {
+         if (getMap().containsArrayKeys()) {
+            final String       key  = JSType.toString(longIndex);
+            final FindProperty find = findProperty(key, true);
+            if (find != null) {
+                setObject(find, strict, key, value);
+                return true;
             }
         }
-
+        return false;
+    }
+
+    private boolean doesNotHaveCheckArrayKeys(final long longIndex, final Object value, final boolean strict) {
+        if (getMap().containsArrayKeys()) {
+            final String       key  = JSType.toString(longIndex);
+            final FindProperty find = findProperty(key, true);
+            if (find != null) {
+                setObject(find, strict, key, value);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    //value agnostic
+    private boolean doesNotHaveEnsureLength(final long longIndex, final long oldLength, final boolean strict) {
         if (longIndex >= oldLength) {
             if (!isExtensible()) {
                 if (strict) {
-                    throw typeError("object.non.extensible", JSType.toString(index), ScriptRuntime.safeToString(this));
+                    throw typeError("object.non.extensible", JSType.toString(longIndex), ScriptRuntime.safeToString(this));
                 }
-                return;
+                return true;
             }
             setArray(getArray().ensure(longIndex));
         }
-
-        if (value instanceof Integer) {
-            setArray(getArray().set(index, (int)value, strict));
-        } else if (value instanceof Long) {
-            setArray(getArray().set(index, (long)value, strict));
-        } else if (value instanceof Double) {
-            setArray(getArray().set(index, (double)value, strict));
-        } else {
-            setArray(getArray().set(index, value, strict));
-        }
-
+        return false;
+    }
+
+    private void doesNotHaveEnsureDelete(final long longIndex, final long oldLength, final boolean strict) {
         if (longIndex > oldLength) {
             ArrayData array = getArray();
-
             if (array.canDelete(oldLength, (longIndex - 1), strict)) {
                 array = array.delete(oldLength, (longIndex - 1));
             }
-
             setArray(array);
         }
     }
 
+    private void doesNotHave(final int index, final int value, final boolean strict) {
+        final long oldLength = getArray().length();
+        final long longIndex = ArrayIndex.toLongIndex(index);
+        if (!doesNotHaveCheckArrayKeys(longIndex, value, strict) && !doesNotHaveEnsureLength(longIndex, oldLength, strict)) {
+            setArray(getArray().set(index, value, strict));
+            doesNotHaveEnsureDelete(longIndex, oldLength, strict);
+        }
+    }
+
+    private void doesNotHave(final int index, final long value, final boolean strict) {
+        final long oldLength = getArray().length();
+        final long longIndex = ArrayIndex.toLongIndex(index);
+        if (!doesNotHaveCheckArrayKeys(longIndex, value, strict) && !doesNotHaveEnsureLength(longIndex, oldLength, strict)) {
+            setArray(getArray().set(index, value, strict));
+            doesNotHaveEnsureDelete(longIndex, oldLength, strict);
+        }
+    }
+
+    private void doesNotHave(final int index, final double value, final boolean strict) {
+        final long oldLength = getArray().length();
+        final long longIndex = ArrayIndex.toLongIndex(index);
+        if (!doesNotHaveCheckArrayKeys(longIndex, value, strict) && !doesNotHaveEnsureLength(longIndex, oldLength, strict)) {
+            setArray(getArray().set(index, value, strict));
+            doesNotHaveEnsureDelete(longIndex, oldLength, strict);
+        }
+    }
+
+    private void doesNotHave(final int index, final Object value, final boolean strict) {
+        final long oldLength = getArray().length();
+        final long longIndex = ArrayIndex.toLongIndex(index);
+        if (!doesNotHaveCheckArrayKeys(longIndex, value, strict) && !doesNotHaveEnsureLength(longIndex, oldLength, strict)) {
+            setArray(getArray().set(index, value, strict));
+            doesNotHaveEnsureDelete(longIndex, oldLength, strict);
+        }
+    }
+
     /**
      * This is the most generic of all Object setters. Most of the others use this in some form.
      * TODO: should be further specialized
@@ -2818,26 +2938,24 @@
                 return;
             }
 
-            f.setObjectValue(value, strict);
+            f.setValue(value, strict);
 
         } else if (!isExtensible()) {
             if (strict) {
                 throw typeError("object.non.extensible", key, ScriptRuntime.safeToString(this));
             }
         } else {
-            spill(key, value);
+            //this will unbox any Number object to its primitive type in case the
+            //property supporst primitive types, so it doesn't matter that it comes
+            //in as an Object.
+            addSpillProperty(key, 0, value, true);
         }
     }
 
-    private void spill(final String key, final Object value) {
-        addSpillProperty(key, 0).setObjectValue(this, this, value, false);
-    }
-
-
     @Override
     public void set(final Object key, final int value, final boolean strict) {
         final Object primitiveKey = JSType.toPrimitive(key, String.class);
-        final int index = getArrayIndex(primitiveKey);
+        final int    index        = getArrayIndex(primitiveKey);
 
         if (isValidArrayIndex(index)) {
             if (getArray().has(index)) {
@@ -2856,7 +2974,7 @@
     @Override
     public void set(final Object key, final long value, final boolean strict) {
         final Object primitiveKey = JSType.toPrimitive(key, String.class);
-        final int index = getArrayIndex(primitiveKey);
+        final int    index        = getArrayIndex(primitiveKey);
 
         if (isValidArrayIndex(index)) {
             if (getArray().has(index)) {
@@ -2875,7 +2993,7 @@
     @Override
     public void set(final Object key, final double value, final boolean strict) {
         final Object primitiveKey = JSType.toPrimitive(key, String.class);
-        final int index = getArrayIndex(primitiveKey);
+        final int    index        = getArrayIndex(primitiveKey);
 
         if (isValidArrayIndex(index)) {
             if (getArray().has(index)) {
@@ -2894,7 +3012,7 @@
     @Override
     public void set(final Object key, final Object value, final boolean strict) {
         final Object primitiveKey = JSType.toPrimitive(key, String.class);
-        final int index = getArrayIndex(primitiveKey);
+        final int    index        = getArrayIndex(primitiveKey);
 
         if (isValidArrayIndex(index)) {
             if (getArray().has(index)) {
@@ -3057,14 +3175,12 @@
     @Override
     public void set(final int key, final int value, final boolean strict) {
         final int index = getArrayIndex(key);
-
         if (isValidArrayIndex(index)) {
             if (getArray().has(index)) {
                 setArray(getArray().set(index, value, strict));
             } else {
                 doesNotHave(index, value, strict);
             }
-
             return;
         }
 
@@ -3129,7 +3245,7 @@
     @Override
     public boolean has(final Object key) {
         final Object primitiveKey = JSType.toPrimitive(key);
-        final int index = getArrayIndex(primitiveKey);
+        final int    index        = getArrayIndex(primitiveKey);
         return isValidArrayIndex(index) ? hasArrayProperty(index) : hasProperty(JSType.toString(primitiveKey), true);
     }
 
@@ -3167,7 +3283,7 @@
     @Override
     public boolean hasOwnProperty(final Object key) {
         final Object primitiveKey = JSType.toPrimitive(key, String.class);
-        final int index = getArrayIndex(primitiveKey);
+        final int    index        = getArrayIndex(primitiveKey);
         return isValidArrayIndex(index) ? hasOwnArrayProperty(index) : hasProperty(JSType.toString(primitiveKey), false);
     }
 
@@ -3243,9 +3359,9 @@
 
     @Override
     public boolean delete(final Object key, final boolean strict) {
-        final Object primitiveKey = JSType.toPrimitive(key, String.class);
-        final int index = getArrayIndex(primitiveKey);
-        final ArrayData array = getArray();
+        final Object    primitiveKey = JSType.toPrimitive(key, String.class);
+        final int       index        = getArrayIndex(primitiveKey);
+        final ArrayData array        = getArray();
 
         if (array.has(index)) {
             if (array.canDelete(index, strict)) {
@@ -3291,49 +3407,44 @@
      * @return the newly created UserAccessorProperty
      */
     protected final UserAccessorProperty newUserAccessors(final String key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
-        final UserAccessorProperty property = getMap().newUserAccessors(key, propertyFlags);
-        setSpill(property.getGetterSlot(), getter);
-        setSpill(property.getSetterSlot(), setter);
-
-        return property;
+        final UserAccessorProperty uc = getMap().newUserAccessors(key, propertyFlags);
+        //property.getSetter(Object.class, getMap());
+        uc.setAccessors(this, getMap(), new UserAccessorProperty.Accessors(getter, setter));
+        return uc;
     }
 
-    /**
-     * Write a value to a spill slot
-     * @param slot  the slot index
-     * @param value the value
-     */
-    protected final void setSpill(final int slot, final Object value) {
-        if (spill == null) {
-            // create new spill.
-            spill = new Object[Math.max(slot + 1, SPILL_RATE)];
-        } else if (slot >= spill.length) {
-            // grow spill as needed
-            final Object[] newSpill = new Object[slot + 1];
-            System.arraycopy(spill, 0, newSpill, 0, spill.length);
-            spill = newSpill;
+    Object ensureSpillSize(final int slot) {
+        if (slot < spillLength) {
+            return this;
         }
-
-        spill[slot] = value;
+        final int newLength = alignUp(slot + 1, SPILL_RATE);
+        final Object[] newObjectSpill    = new Object[newLength];
+        final long[]   newPrimitiveSpill = OBJECT_FIELDS_ONLY ? null : new long[newLength];
+
+        if (objectSpill != null) {
+            System.arraycopy(objectSpill, 0, newObjectSpill, 0, spillLength);
+            if (!OBJECT_FIELDS_ONLY) {
+                System.arraycopy(primitiveSpill, 0, newPrimitiveSpill, 0, spillLength);
+            }
+        }
+
+        this.primitiveSpill = newPrimitiveSpill;
+        this.objectSpill    = newObjectSpill;
+        this.spillLength = newLength;
+
+        return this;
     }
 
-    /**
-     * Get a value from a spill slot
-     * @param slot the slot index
-     * @return the value in the spill slot with the given index
-     */
-    protected Object getSpill(final int slot) {
-        return spill != null && slot < spill.length ? spill[slot] : null;
+    private static MethodHandle findOwnMH_V(final Class<? extends ScriptObject> clazz, final String name, final Class<?> rtype, final Class<?>... types) {
+        return MH.findVirtual(MethodHandles.lookup(), ScriptObject.class, name, MH.type(rtype, types));
     }
 
-    private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
-        final Class<?>   own = ScriptObject.class;
-        final MethodType mt  = MH.type(rtype, types);
-        try {
-            return MH.findStatic(MethodHandles.lookup(), own, name, mt);
-        } catch (final MethodHandleFactory.LookupException e) {
-            return MH.findVirtual(MethodHandles.lookup(), own, name, mt);
-        }
+    private static MethodHandle findOwnMH_V(final String name, final Class<?> rtype, final Class<?>... types) {
+        return MH.findVirtual(MethodHandles.lookup(), ScriptObject.class, name, MH.type(rtype, types));
+    }
+
+    private static MethodHandle findOwnMH_S(final String name, final Class<?> rtype, final Class<?>... types) {
+        return MH.findStatic(MethodHandles.lookup(), ScriptObject.class, name, MH.type(rtype, types));
     }
 
     private static MethodHandle getKnownFunctionPropertyGuard(final PropertyMap map, final MethodHandle getter, final Object where, final ScriptFunction func) {
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptRuntime.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptRuntime.java	Wed Feb 26 13:17:57 2014 +0100
@@ -45,6 +45,7 @@
 import jdk.internal.dynalink.beans.StaticClass;
 import jdk.nashorn.api.scripting.JSObject;
 import jdk.nashorn.api.scripting.ScriptObjectMirror;
+import jdk.nashorn.internal.codegen.CompilerConstants;
 import jdk.nashorn.internal.codegen.CompilerConstants.Call;
 import jdk.nashorn.internal.ir.debug.JSONWriter;
 import jdk.nashorn.internal.objects.Global;
@@ -81,9 +82,6 @@
     /** Method handle used to enter a {@code with} scope at runtime. */
     public static final Call OPEN_WITH = staticCallNoLookup(ScriptRuntime.class, "openWith", ScriptObject.class, ScriptObject.class, Object.class);
 
-    /** Method handle used to exit a {@code with} scope at runtime. */
-    public static final Call CLOSE_WITH = staticCallNoLookup(ScriptRuntime.class, "closeWith", ScriptObject.class, ScriptObject.class);
-
     /**
      * Method used to place a scope's variable into the Global scope, which has to be done for the
      * properties declared at outermost script level.
@@ -466,7 +464,8 @@
     }
 
     /**
-     * Entering a {@code with} node requires new scope. This is the implementation
+     * Entering a {@code with} node requires new scope. This is the implementation. When exiting the with statement,
+     * use {@link ScriptObject#getProto()} on the scope.
      *
      * @param scope      existing scope
      * @param expression expression in with
@@ -490,20 +489,6 @@
     }
 
     /**
-     * Exiting a {@code with} node requires restoring scope. This is the implementation
-     *
-     * @param scope existing scope
-     *
-     * @return restored scope
-     */
-    public static ScriptObject closeWith(final ScriptObject scope) {
-        if (scope instanceof WithObject) {
-            return ((WithObject)scope).getParentScope();
-        }
-        return scope;
-    }
-
-    /**
      * ECMA 11.6.1 - The addition operator (+) - generic implementation
      * Compiler specializes using {@link jdk.nashorn.internal.codegen.RuntimeCallSite}
      * if any type information is available for any of the operands
@@ -577,6 +562,13 @@
         if (property != null) {
             if (obj instanceof ScriptObject) {
                 obj = ((ScriptObject)obj).get(property);
+                if(Global.isLocationPropertyPlaceholder(obj)) {
+                    if(CompilerConstants.__LINE__.name().equals(property)) {
+                        obj = Integer.valueOf(0);
+                    } else {
+                        obj = "";
+                    }
+                }
             } else if (object instanceof Undefined) {
                 obj = ((Undefined)obj).get(property);
             } else if (object == null) {
--- a/nashorn/src/jdk/nashorn/internal/runtime/SetMethodCreator.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/SetMethodCreator.java	Wed Feb 26 13:17:57 2014 +0100
@@ -25,14 +25,14 @@
 
 package jdk.nashorn.internal.runtime;
 
+import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY;
+import static jdk.nashorn.internal.lookup.Lookup.MH;
 import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError;
-import static jdk.nashorn.internal.lookup.Lookup.MH;
+import static jdk.nashorn.internal.runtime.JSType.getAccessorTypeIndex;
 
 import java.lang.invoke.MethodHandle;
 import jdk.internal.dynalink.CallSiteDescriptor;
 import jdk.internal.dynalink.linker.GuardedInvocation;
-import jdk.nashorn.internal.codegen.ObjectClassGenerator;
-import jdk.nashorn.internal.lookup.Lookup;
 import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
 import jdk.nashorn.internal.runtime.linker.NashornGuards;
 
@@ -44,10 +44,11 @@
  */
 final class SetMethodCreator {
     // See constructor parameters for description of fields
-    private final ScriptObject sobj;
-    private final PropertyMap map;
-    private final FindProperty find;
+    private final ScriptObject       sobj;
+    private final PropertyMap        map;
+    private final FindProperty       find;
     private final CallSiteDescriptor desc;
+    private final Class<?>           type;
 
     /**
      * Creates a new property setter method creator.
@@ -58,9 +59,11 @@
      */
     SetMethodCreator(final ScriptObject sobj, final FindProperty find, final CallSiteDescriptor desc) {
         this.sobj = sobj;
-        this.map = sobj.getMap();
+        this.map  = sobj.getMap();
         this.find = find;
         this.desc = desc;
+        this.type = desc.getMethodType().parameterType(1);
+
     }
 
     private String getName() {
@@ -96,7 +99,7 @@
         SetMethod(final MethodHandle methodHandle, final Property property) {
             assert methodHandle != null;
             this.methodHandle = methodHandle;
-            this.property = property;
+            this.property     = property;
         }
 
         /**
@@ -104,7 +107,14 @@
          * @return the composed guarded invocation that represents the dynamic setter method for the property.
          */
         GuardedInvocation createGuardedInvocation() {
-            return new GuardedInvocation(methodHandle, getGuard());
+            // getGuard() and getException() either both return null, or neither does. The reason for that is that now
+            // getGuard returns a map guard that casts its argument to ScriptObject, and if that fails, we need to
+            // relink on ClassCastException.
+            return new GuardedInvocation(methodHandle, getGuard(), null, getException());
+        }
+
+        private Class<ClassCastException> getException() {
+            return needsNoGuard() ? null : ClassCastException.class;
         }
 
         private MethodHandle getGuard() {
@@ -112,12 +122,11 @@
         }
 
         private boolean needsNoGuard() {
-            return NashornCallSiteDescriptor.isFastScope(desc) &&
-                    (ObjectClassGenerator.OBJECT_FIELDS_ONLY || isPropertyTypeStable());
+            return NashornCallSiteDescriptor.isFastScope(desc) && isPropertyTypeStable();
         }
 
         private boolean isPropertyTypeStable() {
-            return property == null || !property.canChangeType();
+            return OBJECT_FIELDS_ONLY;
         }
     }
 
@@ -145,7 +154,6 @@
 
     private SetMethod createExistingPropertySetter() {
         final Property property = find.getProperty();
-        final Class<?> type = desc.getMethodType().parameterType(1);
         final MethodHandle methodHandle = find.getSetter(type, NashornCallSiteDescriptor.isStrict(desc));
 
         assert methodHandle != null;
@@ -165,7 +173,7 @@
 
     private SetMethod createGlobalPropertySetter() {
         final ScriptObject global = Context.getGlobalTrusted();
-        return new SetMethod(ScriptObject.bindTo(global.addSpill(getName()), global), null);
+        return new SetMethod(ScriptObject.bindTo(global.addSpill(type, getName()), global), null);
     }
 
     private SetMethod createNewPropertySetter() {
@@ -174,38 +182,51 @@
         return sm;
     }
 
+    private SetMethod createNewSetter(final Property property) {
+        final PropertyMap oldMap   = getMap();
+        final PropertyMap newMap   = getNewMap(property);
+        final boolean     isStrict = NashornCallSiteDescriptor.isStrict(desc);
+        final String      name     = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
+
+        //fast type specific setter
+        MethodHandle fastSetter = property.getSetter(type, newMap); //0 sobj, 1 value, slot folded for spill property already
+
+        //slow setter, that calls ScriptObject.set with appropraite type and key name
+        MethodHandle slowSetter = ScriptObject.SET_SLOW[getAccessorTypeIndex(type)];
+        slowSetter = MH.insertArguments(slowSetter, 3, NashornCallSiteDescriptor.isStrict(desc));
+        slowSetter = MH.insertArguments(slowSetter, 1, name);
+        slowSetter = MH.asType(slowSetter, slowSetter.type().changeParameterType(0, Object.class));
+
+        assert slowSetter.type().equals(fastSetter.type()) : "slow=" + slowSetter + " != fast=" + fastSetter;
+
+        //cas map used as guard, if true that means we can do the set fast
+        MethodHandle casMap = MH.insertArguments(ScriptObject.CAS_MAP, 1, oldMap, newMap);
+        casMap = MH.dropArguments(casMap, 1, type);
+        casMap = MH.asType(casMap, casMap.type().changeParameterType(0, Object.class));
+        MethodHandle casGuard = MH.guardWithTest(casMap, fastSetter, slowSetter);
+
+        //outermost level needs an extendable check. if object can be extended, guard is true and
+        //we can run the cas setter. The setter goes to "nop" VOID_RETURN if false or throws an
+        //exception if we are in strict mode and object is not extensible
+        MethodHandle extCheck = MH.insertArguments(ScriptObject.EXTENSION_CHECK, 1, isStrict, name);
+        extCheck = MH.asType(extCheck, extCheck.type().changeParameterType(0, Object.class));
+        extCheck = MH.dropArguments(extCheck, 1, type);
+
+        MethodHandle nop = JSType.VOID_RETURN.methodHandle();
+        nop = MH.dropArguments(nop, 0, Object.class, type);
+
+        return new SetMethod(MH.asType(MH.guardWithTest(extCheck, casGuard, nop), fastSetter.type()), property);
+    }
+
     private SetMethod createNewFieldSetter() {
-        final PropertyMap oldMap = getMap();
-        final Property property = new AccessorProperty(getName(), 0, sobj.getClass(), oldMap.getFieldCount());
-        final PropertyMap newMap = oldMap.addProperty(property);
-        MethodHandle setter = MH.insertArguments(ScriptObject.SETFIELD, 0, desc, oldMap, newMap, property.getSetter(Object.class, newMap));
-
-        return new SetMethod(MH.asType(setter, Lookup.SET_OBJECT_TYPE), property);
+        return createNewSetter(new AccessorProperty(getName(), 0, sobj.getClass(), getMap().getFieldCount(), type));
     }
 
     private SetMethod createNewSpillPropertySetter() {
-        final int nextSpill = getMap().getSpillLength();
-
-        final Property property = new AccessorProperty(getName(), Property.IS_SPILL, nextSpill);
-        return new SetMethod(createSpillMethodHandle(nextSpill, property), property);
+        return createNewSetter(new SpillProperty(getName(), 0, getMap().getSpillLength(), type));
     }
 
-    private MethodHandle createSpillMethodHandle(final int nextSpill, Property property) {
-        final PropertyMap oldMap = getMap();
-        final PropertyMap newMap = getNewMap(property);
-
-        final Object[] spill = sobj.spill;
-        if (spill == null) {
-            return MH.insertArguments(ScriptObject.SETSPILLWITHNEW,  0, desc, oldMap, newMap, nextSpill);
-        } else if (nextSpill < spill.length) {
-            return MH.insertArguments(ScriptObject.SETSPILL,         0, desc, oldMap, newMap, nextSpill);
-        } else {
-            final int newLength = (nextSpill + ScriptObject.SPILL_RATE) / ScriptObject.SPILL_RATE * ScriptObject.SPILL_RATE;
-            return MH.insertArguments(ScriptObject.SETSPILLWITHGROW, 0, desc, oldMap, newMap, nextSpill, newLength);
-        }
-    }
-
-    private PropertyMap getNewMap(Property property) {
+    private PropertyMap getNewMap(final Property property) {
         return getMap().addProperty(property);
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/runtime/SpillProperty.java	Wed Feb 26 13:17:57 2014 +0100
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2010-2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.runtime;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY;
+import static jdk.nashorn.internal.lookup.Lookup.MH;
+
+/**
+ * Spill property
+ */
+public class SpillProperty extends AccessorProperty {
+    private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
+
+    private static final MethodHandle PARRAY_GETTER = MH.asType(MH.getter(LOOKUP, ScriptObject.class, "primitiveSpill",  long[].class), MH.type(long[].class, Object.class));
+    private static final MethodHandle OARRAY_GETTER = MH.asType(MH.getter(LOOKUP, ScriptObject.class, "objectSpill",  Object[].class), MH.type(Object[].class, Object.class));
+
+    private static final MethodHandle OBJECT_GETTER    = MH.filterArguments(MH.arrayElementGetter(Object[].class), 0, OARRAY_GETTER);
+    private static final MethodHandle PRIMITIVE_GETTER = MH.filterArguments(MH.arrayElementGetter(long[].class), 0, PARRAY_GETTER);
+    private static final MethodHandle OBJECT_SETTER    = MH.filterArguments(MH.arrayElementSetter(Object[].class), 0, OARRAY_GETTER);
+    private static final MethodHandle PRIMITIVE_SETTER = MH.filterArguments(MH.arrayElementSetter(long[].class), 0, PARRAY_GETTER);
+
+    private static class Accessors {
+        private MethodHandle objectGetter;
+        private MethodHandle objectSetter;
+        private MethodHandle primitiveGetter;
+        private MethodHandle primitiveSetter;
+
+        private final int slot;
+        private final MethodHandle ensureSpillSize;
+
+        private static Accessors ACCESSOR_CACHE[] = new Accessors[512];
+
+        //private static final Map<Integer, Reference<Accessors>> ACCESSOR_CACHE = Collections.synchronizedMap(new WeakHashMap<Integer, Reference<Accessors>>());
+
+        Accessors(final int slot) {
+            assert slot >= 0;
+            this.slot = slot;
+            this.ensureSpillSize = MH.asType(MH.insertArguments(ScriptObject.ENSURE_SPILL_SIZE, 1, slot), MH.type(Object.class, Object.class));
+        }
+
+        private static void ensure(int slot) {
+            int len = ACCESSOR_CACHE.length;
+            if (slot >= len) {
+                do {
+                    len *= 2;
+                } while (slot >= len);
+                Accessors newCache[] = new Accessors[len];
+                System.arraycopy(ACCESSOR_CACHE, 0, newCache, 0, ACCESSOR_CACHE.length);
+                ACCESSOR_CACHE = newCache;
+            }
+        }
+
+        static MethodHandle getCached(final int slot, final boolean isPrimitive, final boolean isGetter) {
+            //Reference<Accessors> ref = ACCESSOR_CACHE.get(slot);
+            ensure(slot);
+            Accessors acc = ACCESSOR_CACHE[slot];
+            if (acc == null) {
+                acc = new Accessors(slot);
+                ACCESSOR_CACHE[slot] = acc;
+            }
+
+            return acc.getOrCreate(isPrimitive, isGetter);
+        }
+
+        private static MethodHandle primordial(final boolean isPrimitive, final boolean isGetter) {
+            if (isPrimitive) {
+                return isGetter ? PRIMITIVE_GETTER : PRIMITIVE_SETTER;
+            }
+            return isGetter ? OBJECT_GETTER : OBJECT_SETTER;
+        }
+
+        MethodHandle getOrCreate(final boolean isPrimitive, final boolean isGetter) {
+            MethodHandle accessor;
+
+            accessor = getInner(isPrimitive, isGetter);
+            if (accessor != null) {
+                return accessor;
+            }
+
+            accessor = primordial(isPrimitive, isGetter);
+            accessor = MH.insertArguments(accessor, 1, slot);
+            if (!isGetter) {
+                accessor = MH.filterArguments(accessor, 0, ensureSpillSize);
+            }
+            setInner(isPrimitive, isGetter, accessor);
+
+            return accessor;
+        }
+
+        void setInner(final boolean isPrimitive, final boolean isGetter, final MethodHandle mh) {
+            if (isPrimitive) {
+                if (isGetter) {
+                    primitiveGetter = mh;
+                } else {
+                    primitiveSetter = mh;
+                }
+            } else {
+                if (isGetter) {
+                    objectGetter = mh;
+                } else {
+                    objectSetter = mh;
+                }
+            }
+        }
+
+        MethodHandle getInner(final boolean isPrimitive, final boolean isGetter) {
+            if (isPrimitive) {
+                return isGetter ? primitiveGetter : primitiveSetter;
+            }
+            return isGetter ? objectGetter : objectSetter;
+        }
+    }
+
+    private static MethodHandle primitiveGetter(final int slot) {
+        return OBJECT_FIELDS_ONLY ? null : Accessors.getCached(slot, true, true);
+    }
+    private static MethodHandle primitiveSetter(final int slot) {
+        return OBJECT_FIELDS_ONLY ? null : Accessors.getCached(slot, true, false);
+    }
+    private static MethodHandle objectGetter(final int slot) {
+        return Accessors.getCached(slot, false, true);
+    }
+    private static MethodHandle objectSetter(final int slot) {
+        return Accessors.getCached(slot, false, false);
+    }
+
+    /**
+     * Constructor for spill properties. Array getters and setters will be created on demand.
+     *
+     * @param key    the property key
+     * @param flags  the property flags
+     * @param slot   spill slot
+     */
+    public SpillProperty(final String key, final int flags, final int slot) {
+        super(key, flags, slot, primitiveGetter(slot), primitiveSetter(slot), objectGetter(slot), objectSetter(slot));
+        assert !OBJECT_FIELDS_ONLY || getCurrentType() == Object.class;
+    }
+
+    SpillProperty(final String key, final int flags, final int slot, final Class<?> initialType) {
+        this(key, flags, slot);
+        setCurrentType(OBJECT_FIELDS_ONLY ? Object.class : initialType);
+    }
+
+    SpillProperty(final String key, final int flags, final int slot, final ScriptObject owner, final Object initialValue) {
+        this(key, flags, slot);
+        setInitialValue(owner, initialValue);
+    }
+
+    /**
+     * Copy constructor
+     * @param property other property
+     */
+    protected SpillProperty(final SpillProperty property) {
+        super(property);
+    }
+
+    /**
+     * Copy constructor
+     * @param newType new type
+     * @param property other property
+     */
+    protected SpillProperty(final SpillProperty property, final Class<?> newType) {
+        super(property, newType);
+    }
+
+    @Override
+    public Property copy() {
+        return new SpillProperty(this);
+    }
+
+    @Override
+    public Property copy(final Class<?> newType) {
+        return new SpillProperty(this, newType);
+    }
+
+    @Override
+    public boolean isSpill() {
+        return true;
+    }
+
+    @Override
+    public int getSpillCount() {
+        return 1;
+    }
+
+}
--- a/nashorn/src/jdk/nashorn/internal/runtime/Undefined.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/Undefined.java	Wed Feb 26 13:17:57 2014 +0100
@@ -25,12 +25,11 @@
 
 package jdk.nashorn.internal.runtime;
 
+import static jdk.nashorn.internal.lookup.Lookup.MH;
 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
-import static jdk.nashorn.internal.lookup.Lookup.MH;
 
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
-import java.lang.invoke.MethodType;
 import jdk.internal.dynalink.CallSiteDescriptor;
 import jdk.internal.dynalink.linker.GuardedInvocation;
 import jdk.internal.dynalink.support.CallSiteDescriptorFactory;
@@ -110,13 +109,13 @@
             if (desc.getNameTokenCount() < 3) {
                 return findGetIndexMethod(desc);
             }
-            throw lookupTypeError("cant.read.property.of.undefined", desc);
+            return findGetMethod(desc);
         case "setProp":
         case "setElem":
             if (desc.getNameTokenCount() < 3) {
                 return findSetIndexMethod(desc);
             }
-            throw lookupTypeError("cant.set.property.of.undefined", desc);
+            return findSetMethod(desc);
         default:
             break;
         }
@@ -128,44 +127,23 @@
         return typeError(msg, desc.getNameTokenCount() > 2 ? desc.getNameToken(2) : null);
     }
 
-    /**
-     * Find the appropriate GETINDEX method for an invoke dynamic call.
-     * @param desc The invoke dynamic callsite descriptor
-     * @param args arguments
-     * @return GuardedInvocation to be invoked at call site.
-     */
-    private static GuardedInvocation findGetIndexMethod(final CallSiteDescriptor desc, final Object... args) {
-        final MethodType callType  = desc.getMethodType();
-        final Class<?> returnClass = callType.returnType();
-        final Class<?> keyClass    = callType.parameterType(1);
+    private static final MethodHandle GET_METHOD = findOwnMH("get", Object.class, Object.class);
+    private static final MethodHandle SET_METHOD = MH.insertArguments(findOwnMH("set", void.class, Object.class, Object.class, boolean.class), 3, Boolean.TRUE);
 
-        String name = "get";
-        if (returnClass.isPrimitive()) {
-            //turn e.g. get with a double into getDouble
-            final String returnTypeName = returnClass.getName();
-            name += Character.toUpperCase(returnTypeName.charAt(0)) + returnTypeName.substring(1, returnTypeName.length());
-        }
-        MethodHandle methodHandle = findOwnMH(name, returnClass, keyClass);
-        methodHandle = MH.asType(methodHandle, methodHandle.type().changeParameterType(0, Object.class));
-
-        return new GuardedInvocation(methodHandle, UNDEFINED_GUARD);
+    private static GuardedInvocation findGetMethod(final CallSiteDescriptor desc) {
+        return new GuardedInvocation(MH.insertArguments(GET_METHOD, 1, desc.getNameToken(2)), UNDEFINED_GUARD).asType(desc);
     }
 
-    /**
-     * Find the appropriate SETINDEX method for an invoke dynamic call.
-     * @param desc The invoke dynamic callsite descriptor
-     * @return GuardedInvocation to be invoked at call site.
-     */
+    private static GuardedInvocation findGetIndexMethod(final CallSiteDescriptor desc) {
+        return new GuardedInvocation(GET_METHOD, UNDEFINED_GUARD).asType(desc);
+    }
+
+    private static GuardedInvocation findSetMethod(final CallSiteDescriptor desc) {
+        return new GuardedInvocation(MH.insertArguments(SET_METHOD, 1, desc.getNameToken(2)), UNDEFINED_GUARD).asType(desc);
+    }
+
     private static GuardedInvocation findSetIndexMethod(final CallSiteDescriptor desc) {
-        final MethodType callType   = desc.getMethodType();
-        final Class<?>   keyClass   = callType.parameterType(1);
-        final Class<?>   valueClass = callType.parameterType(2);
-
-        MethodHandle methodHandle = findOwnMH("set", void.class, keyClass, valueClass, boolean.class);
-        methodHandle = MH.asType(methodHandle, methodHandle.type().changeParameterType(0, Object.class));
-        methodHandle = MH.insertArguments(methodHandle, 3, false);
-
-        return new GuardedInvocation(methodHandle, UNDEFINED_GUARD);
+        return new GuardedInvocation(SET_METHOD, UNDEFINED_GUARD).asType(desc);
     }
 
     @Override
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/runtime/UnwarrantedOptimismException.java	Wed Feb 26 13:17:57 2014 +0100
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.nashorn.internal.runtime;
+
+import jdk.nashorn.internal.codegen.types.Type;
+
+/**
+ * This exception is thrown from an optimistic operation, e.g. an integer add,
+ * that was to optimistic for what really took place. Typically things like
+ * trying to get an array element that we want to be an int, and it was a double,
+ * and an int add that actually overflows and needs a double for the representation
+ */
+
+@SuppressWarnings("serial")
+public final class UnwarrantedOptimismException extends RuntimeException {
+    /** Denotes an invalid program point */
+    public static final int INVALID_PROGRAM_POINT = -1;
+
+    /** The value for the first ordinary program point */
+    public static final int FIRST_PROGRAM_POINT = 0;
+
+    private Object returnValue;
+    private final int    programPoint;
+    private final Type   returnType;
+
+    /**
+     * Constructor
+     * @param returnValue actual return value from the too narrow operation
+     * @param programPoint program point where unwarranted optimism was detected
+     */
+    public UnwarrantedOptimismException(final Object returnValue, final int programPoint) {
+        this(returnValue, programPoint, getReturnType(returnValue));
+    }
+
+    /**
+     * Check if a program point is valid
+     * @param programPoint the program point
+     * @return true if valid
+     */
+    public static boolean isValid(final int programPoint) {
+        assert programPoint >= INVALID_PROGRAM_POINT;
+        return programPoint != INVALID_PROGRAM_POINT;
+    }
+
+    private static Type getReturnType(final Object v) {
+        if (v instanceof Double) {
+            return Type.NUMBER;
+        } else if (v instanceof Long) {
+            return Type.LONG;
+        }
+        assert !(v instanceof Integer) : v + " is an int"; // Can't have an unwarranted optimism exception with int
+        return Type.OBJECT;
+    }
+
+    /**
+     * Constructor with explicit return value type.
+     * @param returnValue actual return value from the too narrow operation
+     * @param programPoint program point where unwarranted optimism was detected
+     * @param returnType type of the returned value. Used to disambiguate the return type. E.g. an {@code ObjectArrayData}
+     * might return a {@link Double} for a particular element getter, but still throw this exception even if the call
+     * site can accept a double, since the array's type is actually {@code Type#OBJECT}. In this case, it must
+     * explicitly use this constructor to indicate its values are to be considered {@code Type#OBJECT} and not
+     * {@code Type#NUMBER}.
+     */
+    public UnwarrantedOptimismException(final Object returnValue, final int programPoint, final Type returnType) {
+        super("", null, false, Context.DEBUG);
+        this.returnValue  = returnValue;
+        this.programPoint = programPoint;
+        this.returnType   = returnType;
+    }
+
+    /**
+     * Get the return value. This is a destructive readout, after the method is invoked the return value is null'd out.
+     * @return return value
+     */
+    public Object getReturnValueDestructive() {
+        final Object retval = returnValue;
+        returnValue = null;
+        return retval;
+    }
+
+    Object getReturnValueNonDestructive() {
+        return returnValue;
+    }
+
+    /**
+     * Get the return type
+     * @return return type
+     */
+    public Type getReturnType() {
+        return returnType;
+    }
+
+    /**
+     * Does this exception refer to an invalid program point? This might be OK if
+     * we throw it, e.g. from a parameter guard
+     * @return true if invalid program point specified
+     */
+    public boolean hasInvalidProgramPoint() {
+        return programPoint == INVALID_PROGRAM_POINT;
+    }
+
+    /**
+     * Get the program point
+     * @return the program point
+     */
+    public int getProgramPoint() {
+        return programPoint;
+    }
+
+    /**
+     * Check if we ended up with a primitive return value (even though it may be
+     * too wide for what we tried to do, e.g. double instead of int)
+     * @return true if return value is primitive
+     */
+    public boolean hasPrimitiveReturnValue() {
+        return returnValue instanceof Number || returnValue instanceof Boolean;
+    }
+
+    @Override
+    public String getMessage() {
+        return "UNWARRANTED OPTIMISM: [returnValue=" +
+            returnValue +
+            " (class=" +
+            (returnValue == null ? "null" : returnValue.getClass().getSimpleName()) +
+            (hasInvalidProgramPoint() ?
+                " <invalid program point>" :
+                (" @ program point #" + programPoint)) +
+            ")]";
+    }
+
+}
--- a/nashorn/src/jdk/nashorn/internal/runtime/UserAccessorProperty.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/UserAccessorProperty.java	Wed Feb 26 13:17:57 2014 +0100
@@ -25,6 +25,13 @@
 
 package jdk.nashorn.internal.runtime;
 
+import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
+import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
+import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
+import static jdk.nashorn.internal.lookup.Lookup.MH;
+import static jdk.nashorn.internal.runtime.JSType.CONVERT_OBJECT_OPTIMISTIC;
+import static jdk.nashorn.internal.runtime.JSType.getAccessorTypeIndex;
+
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.util.concurrent.Callable;
@@ -33,10 +40,6 @@
 import jdk.nashorn.internal.lookup.Lookup;
 import jdk.nashorn.internal.runtime.linker.Bootstrap;
 
-import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
-import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
-import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
-
 /**
  * Property with user defined getters/setters. Actual getter and setter
  * functions are stored in underlying ScriptObject. Only the 'slot' info is
@@ -52,21 +55,34 @@
  * getter and setter (say, both non-null), we'll have spill slots to store
  * those. When a slot is negative, (-slot - 1) is the embed/spill index.
  */
-public final class UserAccessorProperty extends Property {
+public final class UserAccessorProperty extends SpillProperty {
+
+    static class Accessors {
+        Object getter;
+        Object setter;
+
+        Accessors(final Object getter, final Object setter) {
+            set(getter, setter);
+        }
 
-    /** User defined getter function slot. */
-    private final int getterSlot;
+        final void set(final Object getter, final Object setter) {
+            this.getter = getter;
+            this.setter = setter;
+        }
 
-    /** User defined setter function slot. */
-    private final int setterSlot;
+        @Override
+        public String toString() {
+            return "[getter=" + getter + " setter=" + setter + ']';
+        }
+    }
 
     /** Getter method handle */
     private final static CompilerConstants.Call USER_ACCESSOR_GETTER = staticCall(MethodHandles.lookup(), UserAccessorProperty.class,
-            "userAccessorGetter", Object.class, ScriptObject.class, int.class, Object.class);
+            "userAccessorGetter", Object.class, Accessors.class, Object.class);
 
     /** Setter method handle */
     private final static CompilerConstants.Call USER_ACCESSOR_SETTER = staticCall(MethodHandles.lookup(), UserAccessorProperty.class,
-            "userAccessorSetter", void.class, ScriptObject.class, int.class, String.class, Object.class, Object.class);
+            "userAccessorSetter", void.class, Accessors.class, String.class, Object.class, Object.class);
 
     /** Dynamic invoker for getter */
     private static final Object INVOKE_UA_GETTER = new Object();
@@ -85,6 +101,7 @@
 
     /** Dynamic invoker for setter */
     private static Object INVOKE_UA_SETTER = new Object();
+
     private static MethodHandle getINVOKE_UA_SETTER() {
         return ((GlobalObject)Context.getGlobal()).getDynamicInvoker(INVOKE_UA_SETTER,
                 new Callable<MethodHandle>() {
@@ -104,91 +121,146 @@
      * @param getterSlot getter slot, starting at first embed
      * @param setterSlot setter slot, starting at first embed
      */
-    UserAccessorProperty(final String key, final int flags, final int getterSlot, final int setterSlot) {
-        super(key, flags, -1);
-        this.getterSlot = getterSlot;
-        this.setterSlot = setterSlot;
+    UserAccessorProperty(final String key, final int flags, final int slot) {
+        super(key, flags, slot);
     }
 
     private UserAccessorProperty(final UserAccessorProperty property) {
         super(property);
-        this.getterSlot = property.getterSlot;
-        this.setterSlot = property.setterSlot;
     }
 
-    /**
-     * Return getter spill slot for this UserAccessorProperty.
-     * @return getter slot
-     */
-    public int getGetterSlot() {
-        return getterSlot;
-    }
-
-    /**
-     * Return setter spill slot for this UserAccessorProperty.
-     * @return setter slot
-     */
-    public int getSetterSlot() {
-        return setterSlot;
+    private UserAccessorProperty(final UserAccessorProperty property, final Class<?> newType) {
+        super(property, newType);
     }
 
     @Override
-    protected Property copy() {
+    public Property copy() {
         return new UserAccessorProperty(this);
     }
 
     @Override
-    public boolean equals(final Object other) {
-        if (!super.equals(other)) {
-            return false;
+    public Property copy(final Class<?> newType) {
+        return new UserAccessorProperty(this, newType);
+    }
+
+    void setAccessors(final ScriptObject sobj, final PropertyMap map, final Accessors gs) {
+        try {
+            //invoke the getter and find out
+            super.getSetter(Object.class, map).invokeExact((Object)sobj, (Object)gs);
+        } catch (final Error | RuntimeException t) {
+            throw t;
+        } catch (final Throwable t) {
+            throw new RuntimeException(t);
         }
+    }
 
-        final UserAccessorProperty uc = (UserAccessorProperty) other;
-        return getterSlot == uc.getterSlot && setterSlot == uc.setterSlot;
+    //pick the getter setter out of the correct spill slot in sobj
+    Accessors getAccessors(final ScriptObject sobj) {
+        try {
+            //invoke the super getter with this spill slot
+            //get the getter setter from the correct spill slot
+            final Object gs = super.getGetter(Object.class).invokeExact((Object)sobj);
+            return (Accessors)gs;
+        } catch (final Error | RuntimeException t) {
+            throw t;
+        } catch (final Throwable t) {
+            throw new RuntimeException(t);
+        }
     }
 
     @Override
-    public int hashCode() {
-        return super.hashCode() ^ getterSlot ^ setterSlot;
+    public Class<?> getCurrentType() {
+        return Object.class;
     }
 
-    /*
-     * Accessors.
-     */
     @Override
-    public int getSpillCount() {
-        return 2;
+    public boolean hasGetterFunction(final ScriptObject sobj) {
+        return getAccessors(sobj).getter != null;
     }
 
     @Override
-    public boolean hasGetterFunction(final ScriptObject obj) {
-        return obj.getSpill(getterSlot) != null;
+    public boolean hasSetterFunction(final ScriptObject sobj) {
+        return getAccessors(sobj).setter != null;
     }
 
     @Override
-    public boolean hasSetterFunction(final ScriptObject obj) {
-        return obj.getSpill(setterSlot) != null;
+    public int getIntValue(ScriptObject self, ScriptObject owner) {
+        return (int)getObjectValue(self, owner);
+    }
+
+    @Override
+    public long getLongValue(ScriptObject self, ScriptObject owner) {
+        return (long)getObjectValue(self, owner);
+    }
+
+    @Override
+    public double getDoubleValue(ScriptObject self, ScriptObject owner) {
+        return (double)getObjectValue(self, owner);
     }
 
     @Override
     public Object getObjectValue(final ScriptObject self, final ScriptObject owner) {
-        return userAccessorGetter(owner, getGetterSlot(), self);
+        return userAccessorGetter(getAccessors((owner != null) ? owner : (ScriptObject)self), self);
+    }
+
+    @Override
+    public void setValue(ScriptObject self, ScriptObject owner, int value, boolean strict) {
+        setValue(self, owner, value, strict);
     }
 
     @Override
-    public void setObjectValue(final ScriptObject self, final ScriptObject owner, final Object value, final boolean strict) {
-        userAccessorSetter(owner, getSetterSlot(), strict ? getKey() : null, self, value);
+    public void setValue(ScriptObject self, ScriptObject owner, long value, boolean strict) {
+        setValue(self, owner, value, strict);
+    }
+
+    @Override
+    public void setValue(ScriptObject self, ScriptObject owner, double value, boolean strict) {
+        setValue(self, owner, value, strict);
+    }
+
+    @Override
+    public void setValue(final ScriptObject self, final ScriptObject owner, final Object value, final boolean strict) {
+        userAccessorSetter(getAccessors((owner != null) ? owner : (ScriptObject)self), strict ? getKey() : null, self, value);
     }
 
     @Override
     public MethodHandle getGetter(final Class<?> type) {
+        //this returns a getter on the format (Accessors, Object receiver)
         return Lookup.filterReturnType(USER_ACCESSOR_GETTER.methodHandle(), type);
     }
 
     @Override
-    public ScriptFunction getGetterFunction(final ScriptObject obj) {
-        final Object value = obj.getSpill(getterSlot);
-        return (value instanceof ScriptFunction) ? (ScriptFunction) value : null;
+    public MethodHandle getOptimisticGetter(final Class<?> type, final int programPoint) {
+        //fortype is always object, but in the optimistic world we have to throw
+        //unwarranted optimism exception for narrower types. We can improve this
+        //by checking for boxed types and unboxing them, but it is doubtful that
+        //this gives us any performance, as UserAccessorProperties are typically not
+        //primitives. Are there? TODO: investigate later. For now we just throw an
+        //exception for narrower types than object
+
+        if (type.isPrimitive()) {
+            final MethodHandle getter = getGetter(Object.class);
+            final MethodHandle mh =
+                    MH.asType(
+                            MH.filterReturnValue(
+                                    getter,
+                                    MH.insertArguments(
+                                            CONVERT_OBJECT_OPTIMISTIC[getAccessorTypeIndex(type)],
+                                            1,
+                                            programPoint)),
+                                    getter.type().changeReturnType(type));
+
+            return mh;
+        }
+
+        assert type == Object.class;
+        return getGetter(type);
+    }
+
+    @Override
+    public ScriptFunction getGetterFunction(final ScriptObject sobj) {
+        final Object value = getAccessors(sobj).getter;
+        return (value instanceof ScriptFunction) ? (ScriptFunction)value : null;
     }
 
     @Override
@@ -197,25 +269,23 @@
     }
 
     @Override
-    public ScriptFunction getSetterFunction(final ScriptObject obj) {
-        final Object value = obj.getSpill(setterSlot);
-        return (value instanceof ScriptFunction) ? (ScriptFunction) value : null;
+    public ScriptFunction getSetterFunction(final ScriptObject sobj) {
+        final Object value = getAccessors(sobj).setter;
+        return (value instanceof ScriptFunction) ? (ScriptFunction)value : null;
     }
 
     // User defined getter and setter are always called by "dyn:call". Note that the user
     // getter/setter may be inherited. If so, proto is bound during lookup. In either
     // inherited or self case, slot is also bound during lookup. Actual ScriptFunction
     // to be called is retrieved everytime and applied.
-    static Object userAccessorGetter(final ScriptObject proto, final int slot, final Object self) {
-        final ScriptObject container = (proto != null) ? proto : (ScriptObject)self;
-        final Object       func      = container.getSpill(slot);
-
+    static Object userAccessorGetter(final Accessors gs, final Object self) {
+        final Object func = gs.getter;
         if (func instanceof ScriptFunction) {
             try {
                 return getINVOKE_UA_GETTER().invokeExact(func, self);
-            } catch(final Error|RuntimeException t) {
+            } catch (final Error | RuntimeException t) {
                 throw t;
-            } catch(final Throwable t) {
+            } catch (final Throwable t) {
                 throw new RuntimeException(t);
             }
         }
@@ -223,19 +293,17 @@
         return UNDEFINED;
     }
 
-    static void userAccessorSetter(final ScriptObject proto, final int slot, final String name, final Object self, final Object value) {
-        final ScriptObject container = (proto != null) ? proto : (ScriptObject)self;
-        final Object       func      = container.getSpill(slot);
-
+    static void userAccessorSetter(final Accessors gs, final String name, final Object self, final Object value) {
+        final Object func = gs.setter;
         if (func instanceof ScriptFunction) {
             try {
                 getINVOKE_UA_SETTER().invokeExact(func, self, value);
-            } catch(final Error|RuntimeException t) {
+            } catch (final Error | RuntimeException t) {
                 throw t;
-            } catch(final Throwable t) {
+            } catch (final Throwable t) {
                 throw new RuntimeException(t);
             }
-        }  else if (name != null) {
+        } else if (name != null) {
             throw typeError("property.has.no.setter", name, ScriptRuntime.safeToString(self));
         }
     }
--- a/nashorn/src/jdk/nashorn/internal/runtime/WithObject.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/WithObject.java	Wed Feb 26 13:17:57 2014 +0100
@@ -42,11 +42,14 @@
  *
  */
 public final class WithObject extends ScriptObject implements Scope {
-    private static final MethodHandle WITHEXPRESSIONGUARD    = findOwnMH("withExpressionGuard",  boolean.class, Object.class, PropertyMap.class, SwitchPoint.class);
-    private static final MethodHandle WITHEXPRESSIONFILTER   = findOwnMH("withFilterExpression", Object.class, Object.class);
-    private static final MethodHandle WITHSCOPEFILTER        = findOwnMH("withFilterScope",      Object.class, Object.class);
-    private static final MethodHandle BIND_TO_EXPRESSION_OBJ = findOwnMH("bindToExpression",     Object.class, Object.class, Object.class);
-    private static final MethodHandle BIND_TO_EXPRESSION_FN  = findOwnMH("bindToExpression",     Object.class, ScriptFunction.class, Object.class);
+    private static final MethodHandle WITHEXPRESSIONGUARD    = findOwnMH("withExpressionGuard",  boolean.class, ScriptObject.class, PropertyMap.class, SwitchPoint.class);
+    private static final MethodHandle WITHEXPRESSIONFILTER   = findOwnMH("withFilterExpression", ScriptObject.class, ScriptObject.class);
+    private static final MethodHandle WITHSCOPEFILTER        = findOwnMH("withFilterScope",      ScriptObject.class, ScriptObject.class);
+    private static final MethodHandle BIND_TO_EXPRESSION_OBJ = findOwnMH("bindToExpression",     Object.class, Object.class, ScriptObject.class);
+    private static final MethodHandle BIND_TO_EXPRESSION_FN  = findOwnMH("bindToExpression",     Object.class, ScriptFunction.class, ScriptObject.class);
+
+    private static final MethodHandle CONSTANT_FALSE_TAKE_SCRIPT_OBJECT = MH.dropArguments(MH.constant(boolean.class, false), 0, ScriptObject.class);
+
 
     /** With expression object. */
     private final ScriptObject expression;
@@ -92,7 +95,7 @@
         final NashornCallSiteDescriptor ndesc = (NashornCallSiteDescriptor)desc;
         FindProperty find = null;
         GuardedInvocation link = null;
-        ScriptObject self = null;
+        ScriptObject self;
 
         final boolean isNamedOperation;
         final String name;
@@ -243,31 +246,36 @@
         final MethodHandle linkInvocation = link.getInvocation();
         final MethodType linkType = linkInvocation.type();
         final boolean linkReturnsFunction = ScriptFunction.class.isAssignableFrom(linkType.returnType());
+
         return link.replaceMethods(
                 // Make sure getMethod will bind the script functions it receives to WithObject.expression
                 MH.foldArguments(linkReturnsFunction ? BIND_TO_EXPRESSION_FN : BIND_TO_EXPRESSION_OBJ,
-                        filter(linkInvocation.asType(linkType.changeReturnType(
-                                linkReturnsFunction ? ScriptFunction.class : Object.class)), WITHEXPRESSIONFILTER)),
+                        filterReceiver(linkInvocation.asType(linkType.changeReturnType(
+                                linkReturnsFunction ? ScriptFunction.class : Object.class).changeParameterType(0, ScriptObject.class)), WITHEXPRESSIONFILTER)),
                 // No clever things for the guard -- it is still identically filtered.
-                filterGuard(link, WITHEXPRESSIONFILTER));
+                filterGuardReceiver(link, WITHEXPRESSIONFILTER));
     }
 
     private GuardedInvocation fixScopeCallSite(final GuardedInvocation link, final String name) {
         final GuardedInvocation newLink = fixReceiverType(link, WITHSCOPEFILTER);
-        return link.replaceMethods(filter(newLink.getInvocation(), WITHSCOPEFILTER),
-            MH.guardWithTest(
-                expressionGuard(name),
-                filterGuard(newLink, WITHSCOPEFILTER),
-                MH.dropArguments(MH.constant(boolean.class, false), 0, Object.class)));
+        final MethodHandle expressionGuard = expressionGuard(name);
+        final MethodHandle filteredGuard = filterGuardReceiver(newLink, WITHSCOPEFILTER);
+        final MethodHandle newGuard;
+        if (filteredGuard == null) {
+            newGuard = expressionGuard;
+        } else {
+            newGuard = MH.guardWithTest(expressionGuard, filteredGuard, CONSTANT_FALSE_TAKE_SCRIPT_OBJECT);
+        }
+        return link.replaceMethods(filterReceiver(newLink.getInvocation(), WITHSCOPEFILTER), newGuard);
     }
 
-    private static MethodHandle filterGuard(final GuardedInvocation link, final MethodHandle filter) {
+    private static MethodHandle filterGuardReceiver(final GuardedInvocation link, final MethodHandle receiverFilter) {
         final MethodHandle test = link.getGuard();
-        return test == null ? null : filter(test, filter);
+        return test == null ? null : filterReceiver(test, receiverFilter);
     }
 
-    private static MethodHandle filter(final MethodHandle mh, final MethodHandle filter) {
-        return MH.filterArguments(mh, 0, filter);
+    private static MethodHandle filterReceiver(final MethodHandle mh, final MethodHandle receiverFilter) {
+        return MH.filterArguments(mh, 0, receiverFilter);
     }
 
     /**
@@ -275,17 +283,17 @@
      * @param receiver WithObject wrapper.
      * @return The with expression.
      */
-    public static Object withFilterExpression(final Object receiver) {
+    public static ScriptObject withFilterExpression(final ScriptObject receiver) {
         return ((WithObject)receiver).expression;
     }
 
     @SuppressWarnings("unused")
-    private static Object bindToExpression(final Object fn, final Object receiver) {
+    private static Object bindToExpression(final Object fn, final ScriptObject receiver) {
         return fn instanceof ScriptFunction ? bindToExpression((ScriptFunction) fn, receiver) : fn;
     }
 
-    private static Object bindToExpression(final ScriptFunction fn, final Object receiver) {
-        return fn.makeBoundFunction(withFilterExpression(receiver), new Object[0]);
+    private static Object bindToExpression(final ScriptFunction fn, final ScriptObject receiver) {
+        return fn.makeBoundFunction(withFilterExpression(receiver), ScriptRuntime.EMPTY_ARRAY);
     }
 
     private MethodHandle expressionGuard(final String name) {
@@ -295,7 +303,7 @@
     }
 
     @SuppressWarnings("unused")
-    private static boolean withExpressionGuard(final Object receiver, final PropertyMap map, final SwitchPoint sp) {
+    private static boolean withExpressionGuard(final ScriptObject receiver, final PropertyMap map, final SwitchPoint sp) {
         return ((WithObject)receiver).expression.getMap() == map && (sp == null || !sp.hasBeenInvalidated());
     }
 
@@ -304,7 +312,7 @@
      * @param receiver WithObject wrapper.
      * @return The with scope.
      */
-    public static Object withFilterScope(final Object receiver) {
+    public static ScriptObject withFilterScope(final ScriptObject receiver) {
         return ((WithObject)receiver).getProto();
     }
 
--- a/nashorn/src/jdk/nashorn/internal/runtime/arrays/ArrayData.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/arrays/ArrayData.java	Wed Feb 26 13:17:57 2014 +0100
@@ -25,16 +25,45 @@
 
 package jdk.nashorn.internal.runtime.arrays;
 
+import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
+import static jdk.nashorn.internal.lookup.Lookup.MH;
+import static jdk.nashorn.internal.runtime.JSType.getAccessorTypeIndex;
+import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
+
 import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
 import java.nio.ByteBuffer;
+import jdk.nashorn.internal.codegen.CompilerConstants;
+import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.lookup.Lookup;
 import jdk.nashorn.internal.runtime.GlobalObject;
 import jdk.nashorn.internal.runtime.JSType;
 import jdk.nashorn.internal.runtime.PropertyDescriptor;
+import jdk.nashorn.internal.runtime.UnwarrantedOptimismException;
+import sun.misc.Unsafe;
 
 /**
  * ArrayData - abstraction for wrapping array elements
  */
 public abstract class ArrayData {
+    /**
+     * Unsafe instance used for unsafe array getters (de facto always safe as they are guarded
+     * by {@link ArrayData#has(int)} anyway
+     */
+    protected static final Unsafe UNSAFE = null;
+
+/*    static {
+        @SuppressWarnings("unused")
+        Unsafe unsafe = null;
+        try {
+            unsafe = Unsafe.getUnsafe();
+        } catch (final SecurityException e) {
+            System.err.println("WARNING: Disabling unsafe array getters... Unsafe not available");
+        }
+        //TODO: disabled for now as we have seen no real performance improvement on non-micros
+        //and using UNSAFE is a damn stupid thing to do
+        UNSAFE = unsafe;
+    }*/
 
     /** Minimum chunk size for underlying arrays */
     protected static final int CHUNK_SIZE = 16;
@@ -53,6 +82,37 @@
     private long length;
 
     /**
+     * Method handle to throw an {@link UnwarrantedOptimismException} when getting an element
+     * of the wrong type
+     */
+    protected static final CompilerConstants.Call THROW_UNWARRANTED = staticCall(MethodHandles.lookup(), ArrayData.class, "throwUnwarranted", void.class, ArrayData.class, int.class, int.class);
+
+    /**
+     * Unwarranted thrower
+     *
+     * @param data         array data
+     * @param programPoint program point
+     * @param index        array index
+     */
+    protected static void throwUnwarranted(final ArrayData data, final int programPoint, final int index) {
+        throw new UnwarrantedOptimismException(data.getObject(index), programPoint);
+    }
+
+    /**
+     * Version of has that throws a class cast exception if element does not exist
+     * used for relinking
+     *
+     * @param index index to check - currently only int indexes
+     * @return index
+     */
+    protected int throwHas(final int index) {
+        if (!has(index)) {
+            throw new ClassCastException();
+        }
+        return index;
+    }
+
+    /**
      * Constructor
      * @param length Virtual length of the array.
      */
@@ -69,6 +129,43 @@
     }
 
     /**
+     * Return element getter for a {@link ContinuousArray}
+     * @param getHas       has getter
+     * @param returnType   return type
+     * @param programPoint program point
+     * @return method handle for element setter
+     */
+    protected final static MethodHandle getContinuousElementGetter(final MethodHandle getHas, final Class<?> returnType, final int programPoint) {
+        final boolean isOptimistic = isValid(programPoint);
+        final int fti = getAccessorTypeIndex(getHas.type().returnType());
+        final int ti  = getAccessorTypeIndex(returnType);
+        MethodHandle mh = getHas;
+
+        if (isOptimistic) {
+            if (ti < fti) {
+                mh = MH.insertArguments(ArrayData.THROW_UNWARRANTED.methodHandle(), 1, programPoint);
+            }
+        }
+        mh = MH.asType(mh, mh.type().changeReturnType(returnType).changeParameterType(0, ContinuousArray.class));
+
+        if (!isOptimistic) {
+            //for example a & array[17];
+            return Lookup.filterReturnType(mh, returnType);
+        }
+        return mh;
+    }
+
+    /**
+     * Return element setter for a {@link ContinuousArray}
+     * @param setHas       set has guard
+     * @param elementType  element type
+     * @return method handle for element setter
+     */
+    protected final static MethodHandle getContinuousElementSetter(final MethodHandle setHas, final Class<?> elementType) {
+        return MH.asType(setHas, setHas.type().changeParameterType(2, elementType).changeParameterType(0, ContinuousArray.class));
+    }
+
+    /**
      * Factory method for unspecified array with given length - start as int array data
      *
      * @param length the initial length
@@ -151,7 +248,7 @@
      * @return the ArrayData
      */
     public static ArrayData allocate(final ByteBuffer buf) {
-        return new ByteBufferArrayData((ByteBuffer)buf);
+        return new ByteBufferArrayData(buf);
     }
 
     /**
@@ -336,6 +433,27 @@
     public abstract int getInt(int index);
 
     /**
+     * Returns the optimistic type of this array data. Basically, when an array data object needs to throw an
+     * {@link UnwarrantedOptimismException}, this type is used as the actual type of the return value.
+     * @return the optimistic type of this array data.
+     */
+    public Type getOptimisticType() {
+        return Type.OBJECT;
+    }
+
+    /**
+     * Get optimistic int - default is that it's impossible. Overridden
+     * by arrays that actually represents ints
+     *
+     * @param index        the index
+     * @param programPoint program point
+     * @return the value
+     */
+    public int getIntOptimistic(int index, int programPoint) {
+        throw new UnwarrantedOptimismException(getObject(index), programPoint, getOptimisticType());
+    }
+
+    /**
      * Get a long value from a given index
      *
      * @param index the index
@@ -344,6 +462,18 @@
     public abstract long getLong(int index);
 
     /**
+     * Get optimistic long - default is that it's impossible. Overridden
+     * by arrays that actually represents longs or narrower
+     *
+     * @param index        the index
+     * @param programPoint program point
+     * @return the value
+     */
+    public long getLongOptimistic(int index, int programPoint) {
+        throw new UnwarrantedOptimismException(getObject(index), programPoint, getOptimisticType());
+    }
+
+    /**
      * Get a double value from a given index
      *
      * @param index the index
@@ -352,6 +482,18 @@
     public abstract double getDouble(int index);
 
     /**
+     * Get optimistic double - default is that it's impossible. Overridden
+     * by arrays that actually represents doubles or narrower
+     *
+     * @param index        the index
+     * @param programPoint program point
+     * @return the value
+     */
+    public double getDoubleOptimistic(int index, int programPoint) {
+        throw new UnwarrantedOptimismException(getObject(index), programPoint, getOptimisticType());
+    }
+
+    /**
      * Get an Object value from a given index
      *
      * @param index the index
@@ -482,12 +624,12 @@
      * @param removed number of removed elements
      * @param added number of added elements
      * @throws UnsupportedOperationException if fast splice is not supported for the class or arguments.
+     * @return new arraydata, but this never happens because we always throw an exception
      */
     public ArrayData fastSplice(final int start, final int removed, final int added) throws UnsupportedOperationException {
         throw new UnsupportedOperationException();
     }
 
-
     static Class<?> widestType(final Object... items) {
         assert items.length > 0;
 
@@ -553,4 +695,5 @@
             throw new RuntimeException(t);
         }
     }
+
 }
--- a/nashorn/src/jdk/nashorn/internal/runtime/arrays/ArrayFilter.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/arrays/ArrayFilter.java	Wed Feb 26 13:17:57 2014 +0100
@@ -25,6 +25,7 @@
 
 package jdk.nashorn.internal.runtime.arrays;
 
+import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.runtime.ScriptRuntime;
 import jdk.nashorn.internal.runtime.Undefined;
 import jdk.nashorn.internal.runtime.linker.Bootstrap;
@@ -141,21 +142,41 @@
     }
 
     @Override
+    public Type getOptimisticType() {
+        return underlying.getOptimisticType();
+    }
+
+    @Override
     public int getInt(final int index) {
         return underlying.getInt(index);
     }
 
     @Override
+    public int getIntOptimistic(final int index, final int programPoint) {
+        return underlying.getIntOptimistic(index, programPoint);
+    }
+
+    @Override
     public long getLong(final int index) {
         return underlying.getLong(index);
     }
 
     @Override
+    public long getLongOptimistic(final int index, final int programPoint) {
+        return underlying.getLongOptimistic(index, programPoint);
+    }
+
+    @Override
     public double getDouble(final int index) {
         return underlying.getDouble(index);
     }
 
     @Override
+    public double getDoubleOptimistic(final int index, final int programPoint) {
+        return underlying.getDoubleOptimistic(index, programPoint);
+    }
+
+    @Override
     public Object getObject(final int index) {
         return underlying.getObject(index);
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/runtime/arrays/ContinuousArray.java	Wed Feb 26 13:17:57 2014 +0100
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.runtime.arrays;
+
+import java.lang.invoke.MethodHandle;
+
+/**
+ * Interface implemented by all arrays that are directly accessible as underlying
+ * native arrays
+ */
+public interface ContinuousArray {
+
+    /**
+     * Return the guard used for the setter, usually some kind of {@link ArrayData#has}
+     * @return guard handle for setters
+     */
+    public MethodHandle getSetGuard();
+
+    /**
+     * Return element getter for a certain type at a certain program point
+     * @param returnType   return type
+     * @param programPoint program point
+     * @return element getter or null if not supported (used to implement slow linkage instead
+     *   as fast isn't possible)
+     */
+    public MethodHandle getElementGetter(final Class<?> returnType, final int programPoint);
+
+    /**
+     * Return element getter for a certain type at a certain program point
+     * @param elementType element type
+     * @return element setter or null if not supported (used to implement slow linkage instead
+     *   as fast isn't possible)
+     */
+    public MethodHandle getElementSetter(final Class<?> elementType);
+}
--- a/nashorn/src/jdk/nashorn/internal/runtime/arrays/IntArrayData.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/arrays/IntArrayData.java	Wed Feb 26 13:17:57 2014 +0100
@@ -25,7 +25,13 @@
 
 package jdk.nashorn.internal.runtime.arrays;
 
+import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCall;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
 import java.util.Arrays;
+
+import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.runtime.JSType;
 import jdk.nashorn.internal.runtime.ScriptRuntime;
 
@@ -33,7 +39,7 @@
  * Implementation of {@link ArrayData} as soon as an int has been
  * written to the array. This is the default data for new arrays
  */
-final class IntArrayData extends ArrayData {
+final class IntArrayData extends ArrayData implements ContinuousArray {
     /**
      * The wrapped array
      */
@@ -59,6 +65,54 @@
         this.array = array;
     }
 
+    private static final MethodHandle HAS_GET_ELEM = virtualCall(MethodHandles.lookup(), IntArrayData.class, UNSAFE == null ? "getElem" : "getElemUnsafe", int.class, int.class).methodHandle();
+    private static final MethodHandle SET_ELEM     = virtualCall(MethodHandles.lookup(), IntArrayData.class, UNSAFE == null ? "setElem" : "setElemUnsafe", void.class, int.class, int.class).methodHandle();
+    private static final MethodHandle HAS          = virtualCall(MethodHandles.lookup(), IntArrayData.class, "has", boolean.class, int.class).methodHandle();
+
+    @Override
+    public final MethodHandle getSetGuard() {
+        return HAS;
+    }
+
+    private final static long UNSAFE_BASE  = UNSAFE == null ? 0L : UNSAFE.arrayBaseOffset(int[].class);
+    private final static long UNSAFE_SCALE = UNSAFE == null ? 0L : UNSAFE.arrayIndexScale(int[].class);
+
+    @SuppressWarnings("unused")
+    private int getElemUnsafe(final int index) {
+        if (has(index)) {
+            return UNSAFE.getInt(array, UNSAFE_BASE + UNSAFE_SCALE * index);
+        }
+        throw new ClassCastException();
+    }
+
+    @SuppressWarnings("unused")
+    private int getElem(final int index) {
+        if (has(index)) {
+            return array[index];
+        }
+        throw new ClassCastException();
+    }
+
+    @SuppressWarnings("unused")
+    private void setElemUnsafe(final int index, final int elem) {
+        UNSAFE.putInt(array, UNSAFE_BASE + UNSAFE_SCALE * index, elem);
+    }
+
+    @SuppressWarnings("unused")
+    private void setElem(final int index, final int elem) {
+        array[index] = elem;
+    }
+
+    @Override
+    public MethodHandle getElementGetter(final Class<?> returnType, final int programPoint) {
+        return getContinuousElementGetter(HAS_GET_ELEM, returnType, programPoint);
+    }
+
+    @Override
+    public MethodHandle getElementSetter(final Class<?> elementType) {
+        return elementType == int.class ? getContinuousElementSetter(SET_ELEM, elementType) : null;
+    }
+
     @Override
     public ArrayData copy() {
         return new IntArrayData(array.clone(), (int) length());
@@ -71,7 +125,7 @@
 
     @Override
     public Object asArrayOfType(final Class<?> componentType) {
-        if(componentType == int.class) {
+        if (componentType == int.class) {
             return array.length == length() ? array.clone() : Arrays.copyOf(array, (int) length());
         }
         return super.asArrayOfType(componentType);
@@ -172,8 +226,8 @@
 
     @Override
     public ArrayData set(final int index, final Object value, final boolean strict) {
-        if (value instanceof Integer) {
-            return set(index, ((Number)value).intValue(), strict);
+        if (JSType.isRepresentableAsInt(value)) {
+            return set(index, JSType.toInt32(value), strict);
         } else if (value == ScriptRuntime.UNDEFINED) {
             return new UndefinedArrayFilter(this).set(index, value, strict);
         }
@@ -192,9 +246,8 @@
 
     @Override
     public ArrayData set(final int index, final long value, final boolean strict) {
-        final int intValue = (int)value;
-        if (intValue == value) {
-            array[index] = intValue;
+        if (JSType.isRepresentableAsInt(value)) {
+            array[index] = JSType.toInt32(value);
             setLength(Math.max(index + 1, length()));
             return this;
         }
@@ -214,21 +267,41 @@
     }
 
     @Override
+    public Type getOptimisticType() {
+        return Type.INT;
+    }
+
+    @Override
     public int getInt(final int index) {
         return array[index];
     }
 
     @Override
+    public int getIntOptimistic(final int index, final int programPoint) {
+        return array[index];
+    }
+
+    @Override
     public long getLong(final int index) {
         return array[index];
     }
 
     @Override
+    public long getLongOptimistic(final int index, final int programPoint) {
+        return array[index];
+    }
+
+    @Override
     public double getDouble(final int index) {
         return array[index];
     }
 
     @Override
+    public double getDoubleOptimistic(final int index, final int programPoint) {
+        return array[index];
+    }
+
+    @Override
     public Object getObject(final int index) {
         return array[index];
     }
--- a/nashorn/src/jdk/nashorn/internal/runtime/arrays/LongArrayData.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/arrays/LongArrayData.java	Wed Feb 26 13:17:57 2014 +0100
@@ -25,7 +25,14 @@
 
 package jdk.nashorn.internal.runtime.arrays;
 
+import static jdk.nashorn.internal.lookup.Lookup.MH;
+import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCall;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
 import java.util.Arrays;
+
+import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.runtime.JSType;
 import jdk.nashorn.internal.runtime.ScriptRuntime;
 
@@ -33,7 +40,7 @@
  * Implementation of {@link ArrayData} as soon as a long has been
  * written to the array
  */
-final class LongArrayData extends ArrayData {
+final class LongArrayData extends ArrayData implements ContinuousArray {
     /**
      * The wrapped array
      */
@@ -52,12 +59,12 @@
 
     @Override
     public ArrayData copy() {
-        return new LongArrayData(array.clone(), (int) length());
+        return new LongArrayData(array.clone(), (int)length());
     }
 
     @Override
     public Object[] asObjectArray() {
-        return toObjectArray(array, (int) length());
+        return toObjectArray(array, (int)length());
     }
 
     private static Object[] toObjectArray(final long[] array, final int length) {
@@ -73,8 +80,8 @@
 
     @Override
     public Object asArrayOfType(final Class<?> componentType) {
-        if(componentType == long.class) {
-            return array.length == length() ? array.clone() : Arrays.copyOf(array, (int) length());
+        if (componentType == long.class) {
+            return array.length == length() ? array.clone() : Arrays.copyOf(array, (int)length());
         }
         return super.asArrayOfType(componentType);
     }
@@ -184,6 +191,62 @@
     }
 
     @Override
+    public Type getOptimisticType() {
+        return Type.LONG;
+    }
+
+    private static final MethodHandle HAS_GET_ELEM = virtualCall(MethodHandles.lookup(), LongArrayData.class, UNSAFE == null ? "getElem" : "getElemUnsafe", long.class, int.class).methodHandle();
+    private static final MethodHandle SET_ELEM     = virtualCall(MethodHandles.lookup(), LongArrayData.class, UNSAFE == null ? "setElem" : "setElemUnsafe", void.class, int.class, long.class).methodHandle();
+    private static final MethodHandle HAS          = virtualCall(MethodHandles.lookup(), LongArrayData.class, "has", boolean.class, int.class).methodHandle();
+
+    private final static long UNSAFE_BASE  = UNSAFE == null ? 0L : UNSAFE.arrayBaseOffset(long[].class);
+    private final static long UNSAFE_SCALE = UNSAFE == null ? 0L : UNSAFE.arrayIndexScale(long[].class);
+
+    @Override
+    public final MethodHandle getSetGuard() {
+        return HAS;
+    }
+
+    @SuppressWarnings("unused")
+    private long getElemUnsafe(final int index) {
+        if (has(index)) {
+            return UNSAFE.getLong(array, UNSAFE_BASE + UNSAFE_SCALE * index);
+        }
+        throw new ClassCastException();
+    }
+
+    @SuppressWarnings("unused")
+    private long getElem(final int index) {
+        if (has(index)) {
+            return array[index];
+        }
+        throw new ClassCastException();
+    }
+
+    @SuppressWarnings("unused")
+    private void setElemUnsafe(final int index, final long elem) {
+        UNSAFE.putLong(array, UNSAFE_BASE + UNSAFE_SCALE * index, elem);
+    }
+
+    @SuppressWarnings("unused")
+    private void setElem(final int index, final long elem) {
+        array[index] = elem;
+    }
+
+    @Override
+    public MethodHandle getElementGetter(final Class<?> returnType, final int programPoint) {
+        if (returnType == int.class) {
+            return null;
+        }
+        return getContinuousElementGetter(HAS_GET_ELEM, returnType, programPoint);
+    }
+
+    @Override
+    public MethodHandle getElementSetter(final Class<?> elementType) {
+        return (elementType == int.class || elementType == long.class) ? getContinuousElementSetter(MH.asType(SET_ELEM, SET_ELEM.type().changeParameterType(2, elementType)), elementType) : null;
+    }
+
+    @Override
     public int getInt(final int index) {
         return (int)array[index];
     }
@@ -194,11 +257,21 @@
     }
 
     @Override
+    public long getLongOptimistic(final int index, final int programPoint) {
+        return array[index];
+    }
+
+    @Override
     public double getDouble(final int index) {
         return array[index];
     }
 
     @Override
+    public double getDoubleOptimistic(final int index, final int programPoint) {
+        return array[index];
+    }
+
+    @Override
     public Object getObject(final int index) {
         return array[index];
     }
--- a/nashorn/src/jdk/nashorn/internal/runtime/arrays/NoTypeArrayData.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/arrays/NoTypeArrayData.java	Wed Feb 26 13:17:57 2014 +0100
@@ -43,7 +43,7 @@
 
     @Override
     public Object[] asObjectArray() {
-        return new Object[0];
+        return ScriptRuntime.EMPTY_ARRAY;
     }
 
     @Override
--- a/nashorn/src/jdk/nashorn/internal/runtime/arrays/NumberArrayData.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/arrays/NumberArrayData.java	Wed Feb 26 13:17:57 2014 +0100
@@ -26,14 +26,20 @@
 package jdk.nashorn.internal.runtime.arrays;
 
 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
+import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCall;
+import static jdk.nashorn.internal.lookup.Lookup.MH;
 
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
 import java.util.Arrays;
 
+import jdk.nashorn.internal.codegen.types.Type;
+
 /**
  * Implementation of {@link ArrayData} as soon as a double has been
  * written to the array
  */
-final class NumberArrayData extends ArrayData {
+final class NumberArrayData extends ArrayData implements ContinuousArray {
     /**
      * The wrapped array
      */
@@ -165,6 +171,62 @@
     }
 
     @Override
+    public Type getOptimisticType() {
+        return Type.NUMBER;
+    }
+
+    private static final MethodHandle HAS_GET_ELEM = virtualCall(MethodHandles.lookup(), NumberArrayData.class, UNSAFE == null ? "getElem" : "getElemUnsafe", double.class, int.class).methodHandle();
+    private static final MethodHandle SET_ELEM     = virtualCall(MethodHandles.lookup(), NumberArrayData.class, UNSAFE == null ? "setElem" : "setElemUnsafe", void.class, int.class, double.class).methodHandle();
+    private static final MethodHandle HAS          = virtualCall(MethodHandles.lookup(), NumberArrayData.class, "has", boolean.class, int.class).methodHandle();
+
+    private final static long UNSAFE_BASE  = UNSAFE == null ? 0L : UNSAFE.arrayBaseOffset(double[].class);
+    private final static long UNSAFE_SCALE = UNSAFE == null ? 0L : UNSAFE.arrayIndexScale(double[].class);
+
+    @Override
+    public final MethodHandle getSetGuard() {
+        return HAS;
+    }
+
+    @SuppressWarnings("unused")
+    private double getElemUnsafe(final int index) {
+        if (has(index)) {
+            return UNSAFE.getDouble(array, UNSAFE_BASE + UNSAFE_SCALE * index);
+        }
+        throw new ClassCastException();
+    }
+
+    @SuppressWarnings("unused")
+    private double getElem(final int index) {
+        if (has(index)) {
+            return array[index];
+        }
+        throw new ClassCastException();
+    }
+
+    @SuppressWarnings("unused")
+    private void setElemUnsafe(final int index, final double elem) {
+        UNSAFE.putDouble(array, UNSAFE_BASE + UNSAFE_SCALE * index, elem);
+    }
+
+    @SuppressWarnings("unused")
+    private void setElem(final int index, final double elem) {
+        array[index] = elem;
+    }
+
+    @Override
+    public MethodHandle getElementGetter(final Class<?> returnType, final int programPoint) {
+        if (returnType == int.class || returnType == long.class) {
+            return null;
+        }
+        return getContinuousElementGetter(HAS_GET_ELEM, returnType, programPoint);
+    }
+
+    @Override
+    public MethodHandle getElementSetter(final Class<?> elementType) {
+        return elementType.isPrimitive() ? getContinuousElementSetter(MH.asType(SET_ELEM, SET_ELEM.type().changeParameterType(2, elementType)), elementType) : null;
+    }
+
+    @Override
     public int getInt(final int index) {
         return (int)array[index];
     }
@@ -180,6 +242,11 @@
     }
 
     @Override
+    public double getDoubleOptimistic(final int index, final int programPoint) {
+        return array[index];
+    }
+
+    @Override
     public Object getObject(final int index) {
         return array[index];
     }
--- a/nashorn/src/jdk/nashorn/internal/runtime/arrays/ObjectArrayData.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/arrays/ObjectArrayData.java	Wed Feb 26 13:17:57 2014 +0100
@@ -25,7 +25,13 @@
 
 package jdk.nashorn.internal.runtime.arrays;
 
+import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCall;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
 import java.util.Arrays;
+
+import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.runtime.JSType;
 import jdk.nashorn.internal.runtime.ScriptRuntime;
 
@@ -33,7 +39,7 @@
  * Implementation of {@link ArrayData} as soon as an Object has been
  * written to the array
  */
-final class ObjectArrayData extends ArrayData {
+final class ObjectArrayData extends ArrayData implements ContinuousArray {
 
     /**
      * The wrapped array
@@ -58,7 +64,15 @@
 
     @Override
     public Object[] asObjectArray() {
-        return Arrays.copyOf(array, (int) length());
+        return array.length == length() ? array.clone() : asObjectArrayCopy();
+    }
+
+    private Object[] asObjectArrayCopy() {
+        final long l = length();
+        assert l <= Integer.MAX_VALUE;
+        final Object[] copy = new Object[(int)l];
+        System.arraycopy(array, 0, copy, 0, (int)l);
+        return copy;
     }
 
     @Override
@@ -151,6 +165,63 @@
     }
 
     @Override
+    public Type getOptimisticType() {
+        return Type.OBJECT;
+    }
+
+    private static final MethodHandle HAS_GET_ELEM = virtualCall(MethodHandles.lookup(), ObjectArrayData.class, UNSAFE == null ? "getElem" : "getElemUnsafe", Object.class, int.class).methodHandle();
+    private static final MethodHandle SET_ELEM     = virtualCall(MethodHandles.lookup(), ObjectArrayData.class, UNSAFE == null ? "setElem" : "setElemUnsafe", void.class, int.class, Object.class).methodHandle();
+    private static final MethodHandle HAS          = virtualCall(MethodHandles.lookup(), ObjectArrayData.class, "has", boolean.class, int.class).methodHandle();
+
+    private final static long UNSAFE_BASE  = UNSAFE == null ? 0L : UNSAFE.arrayBaseOffset(Object[].class);
+    private final static long UNSAFE_SCALE = UNSAFE == null ? 0L : UNSAFE.arrayIndexScale(Object[].class);
+
+    @Override
+    public final MethodHandle getSetGuard() {
+        return HAS;
+    }
+
+    @SuppressWarnings("unused")
+    private Object getElemUnsafe(final int index) {
+        if (has(index)) {
+            return UNSAFE.getObject(array, UNSAFE_BASE + UNSAFE_SCALE * index);
+        }
+        throw new ClassCastException();
+    }
+
+    @SuppressWarnings("unused")
+    private Object getElem(final int index) {
+        if (has(index)) {
+            return array[index];
+        }
+        throw new ClassCastException();
+    }
+
+    @SuppressWarnings("unused")
+    private void setElemUnsafe(final int index, final Object elem) {
+        UNSAFE.putObject(array, UNSAFE_BASE + UNSAFE_SCALE * index, elem);
+    }
+
+    @SuppressWarnings("unused")
+    private void setElem(final int index, final Object elem) {
+        array[index] = elem;
+    }
+
+    @Override
+    public MethodHandle getElementGetter(final Class<?> returnType, final int programPoint) {
+        if (returnType.isPrimitive()) {
+            return null;
+        }
+        return getContinuousElementGetter(HAS_GET_ELEM, returnType, programPoint);
+    }
+
+    @Override
+    public MethodHandle getElementSetter(final Class<?> elementType) {
+        return getContinuousElementSetter(SET_ELEM, Object.class);
+    }
+
+
+    @Override
     public int getInt(final int index) {
         return JSType.toInt32(array[index]);
     }
--- a/nashorn/src/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java	Wed Feb 26 13:17:57 2014 +0100
@@ -28,6 +28,7 @@
 import java.util.Arrays;
 import java.util.Map;
 import java.util.TreeMap;
+import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.runtime.JSType;
 import jdk.nashorn.internal.runtime.ScriptRuntime;
 
@@ -35,7 +36,7 @@
  * Handle arrays where the index is very large.
  */
 class SparseArrayData extends ArrayData {
-    static final long MAX_DENSE_LENGTH = 512 * 1024;
+    static final long MAX_DENSE_LENGTH = 16 * 512 * 1024;
 
     /** Underlying array. */
     private ArrayData underlying;
@@ -220,6 +221,11 @@
     }
 
     @Override
+    public Type getOptimisticType() {
+        return underlying.getOptimisticType();
+    }
+
+    @Override
     public int getInt(final int index) {
         if (index >= 0 && index < maxDenseLength) {
             return underlying.getInt(index);
@@ -228,6 +234,14 @@
     }
 
     @Override
+    public int getIntOptimistic(final int index, final int programPoint) {
+        if (index >= 0 && index < maxDenseLength) {
+            return underlying.getIntOptimistic(index, programPoint);
+        }
+        return JSType.toInt32Optimistic(sparseMap.get(indexToKey(index)), programPoint);
+    }
+
+    @Override
     public long getLong(final int index) {
         if (index >= 0 && index < maxDenseLength) {
             return underlying.getLong(index);
@@ -236,6 +250,14 @@
     }
 
     @Override
+    public long getLongOptimistic(final int index, final int programPoint) {
+        if (index >= 0 && index < maxDenseLength) {
+            return underlying.getLongOptimistic(index, programPoint);
+        }
+        return JSType.toLongOptimistic(sparseMap.get(indexToKey(index)), programPoint);
+    }
+
+    @Override
     public double getDouble(final int index) {
         if (index >= 0 && index < maxDenseLength) {
             return underlying.getDouble(index);
@@ -244,6 +266,14 @@
     }
 
     @Override
+    public double getDoubleOptimistic(final int index, final int programPoint) {
+        if (index >= 0 && index < maxDenseLength) {
+            return underlying.getDouble(index);
+        }
+        return JSType.toNumberOptimistic(sparseMap.get(indexToKey(index)), programPoint);
+    }
+
+    @Override
     public Object getObject(final int index) {
         if (index >= 0 && index < maxDenseLength) {
             return underlying.getObject(index);
--- a/nashorn/src/jdk/nashorn/internal/runtime/arrays/UndefinedArrayFilter.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/arrays/UndefinedArrayFilter.java	Wed Feb 26 13:17:57 2014 +0100
@@ -29,6 +29,7 @@
 
 import java.lang.reflect.Array;
 import jdk.nashorn.internal.runtime.BitVector;
+import jdk.nashorn.internal.runtime.UnwarrantedOptimismException;
 
 /**
  * This filter handles the presence of undefined array elements.
@@ -154,6 +155,15 @@
     }
 
     @Override
+    public int getIntOptimistic(int index, int programPoint) {
+        if (undefined.isSet(index)) {
+            throw new UnwarrantedOptimismException(UNDEFINED, programPoint);
+        }
+
+        return super.getIntOptimistic(index, programPoint);
+    }
+
+    @Override
     public long getLong(final int index) {
         if (undefined.isSet(index)) {
             return 0L;
@@ -163,6 +173,15 @@
     }
 
     @Override
+    public long getLongOptimistic(int index, int programPoint) {
+        if (undefined.isSet(index)) {
+            throw new UnwarrantedOptimismException(UNDEFINED, programPoint);
+        }
+
+        return super.getLongOptimistic(index, programPoint);
+    }
+
+    @Override
     public double getDouble(final int index) {
         if (undefined.isSet(index)) {
             return Double.NaN;
@@ -172,6 +191,15 @@
     }
 
     @Override
+    public double getDoubleOptimistic(int index, int programPoint) {
+        if (undefined.isSet(index)) {
+            throw new UnwarrantedOptimismException(UNDEFINED, programPoint);
+        }
+
+        return super.getDoubleOptimistic(index, programPoint);
+    }
+
+    @Override
     public Object getObject(final int index) {
         if (undefined.isSet(index)) {
             return UNDEFINED;
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java	Wed Feb 26 13:17:57 2014 +0100
@@ -28,21 +28,29 @@
 import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
 
 import java.lang.invoke.CallSite;
+import java.lang.invoke.ConstantCallSite;
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodHandles.Lookup;
 import java.lang.invoke.MethodType;
+
 import jdk.internal.dynalink.CallSiteDescriptor;
 import jdk.internal.dynalink.DynamicLinker;
 import jdk.internal.dynalink.DynamicLinkerFactory;
+import jdk.internal.dynalink.GuardedInvocationFilter;
 import jdk.internal.dynalink.beans.BeansLinker;
 import jdk.internal.dynalink.beans.StaticClass;
 import jdk.internal.dynalink.linker.GuardedInvocation;
+import jdk.internal.dynalink.linker.LinkRequest;
 import jdk.internal.dynalink.linker.LinkerServices;
 import jdk.nashorn.api.scripting.JSObject;
 import jdk.nashorn.internal.codegen.CompilerConstants.Call;
+import jdk.nashorn.internal.codegen.ObjectClassGenerator;
 import jdk.nashorn.internal.codegen.RuntimeCallSite;
+import jdk.nashorn.internal.lookup.MethodHandleFactory;
+import jdk.nashorn.internal.lookup.MethodHandleFunctionality;
 import jdk.nashorn.internal.runtime.JSType;
+import jdk.nashorn.internal.runtime.OptimisticReturnFilters;
 import jdk.nashorn.internal.runtime.ScriptFunction;
 import jdk.nashorn.internal.runtime.ScriptRuntime;
 import jdk.nashorn.internal.runtime.options.Options;
@@ -54,6 +62,25 @@
     /** Reference to the seed boostrap function */
     public static final Call BOOTSTRAP = staticCallNoLookup(Bootstrap.class, "bootstrap", CallSite.class, Lookup.class, String.class, MethodType.class, int.class);
 
+    private static final MethodHandleFunctionality MH = MethodHandleFactory.getFunctionality();
+
+    /**
+     * The default dynalink relink threshold for megamorphisism is 8. In the case
+     * of object fields only, it is fine. However, with dual fields, in order to get
+     * performance on benchmarks with a lot of object instantiation and then field
+     * reassignment, it can take slightly more relinks to become stable with type
+     * changes swapping out an entire proprety map and making a map guard fail.
+     * Therefore the relink threshold is set to 16 for dual fields (now the default).
+     * This doesn't seem to have any other negative performance implication.
+     *
+     * See for example octane.gbemu, run with --log=fields:warning to study
+     * megamorphic behavior
+     */
+    private static final int NASHORN_DEFAULT_UNSTABLE_RELINK_THRESHOLD =
+            ObjectClassGenerator.OBJECT_FIELDS_ONLY ?
+                     8 :
+                    16;
+
     // do not create me!!
     private Bootstrap() {
     }
@@ -65,7 +92,14 @@
                 new BoundDynamicMethodLinker(), new JavaSuperAdapterLinker(), new JSObjectLinker(), new ReflectionCheckLinker());
         factory.setFallbackLinkers(new NashornBeansLinker(), new NashornBottomLinker());
         factory.setSyncOnRelink(true);
-        final int relinkThreshold = Options.getIntProperty("nashorn.unstable.relink.threshold", -1);
+        factory.setPrelinkFilter(new GuardedInvocationFilter() {
+            @Override
+            public GuardedInvocation filter(GuardedInvocation inv, LinkRequest request, LinkerServices linkerServices) {
+                final CallSiteDescriptor desc = request.getCallSiteDescriptor();
+                return OptimisticReturnFilters.filterOptimisticReturnValue(inv, desc).asType(linkerServices, desc.getMethodType());
+            }
+        });
+        final int relinkThreshold = Options.getIntProperty("nashorn.unstable.relink.threshold", NASHORN_DEFAULT_UNSTABLE_RELINK_THRESHOLD);
         if (relinkThreshold > -1) {
             factory.setUnstableRelinkThreshold(relinkThreshold);
         }
@@ -140,6 +174,54 @@
     }
 
     /**
+     * Boostrapper for math calls that may overflow
+     * @param lookup         lookup
+     * @param name           name of operation
+     * @param type           method type
+     * @param programPoint   program point to bind to callsite
+     *
+     * @return callsite for a math instrinic node
+     */
+    public static CallSite mathBootstrap(final MethodHandles.Lookup lookup, final String name, final MethodType type, final int programPoint) {
+        final MethodHandle mh;
+        switch (name) {
+        case "iadd":
+            mh = JSType.ADD_EXACT.methodHandle();
+            break;
+        case "isub":
+            mh = JSType.SUB_EXACT.methodHandle();
+            break;
+        case "imul":
+            mh = JSType.MUL_EXACT.methodHandle();
+            break;
+        case "idiv":
+            mh = JSType.DIV_EXACT.methodHandle();
+            break;
+        case "ineg":
+            mh = JSType.NEGATE_EXACT.methodHandle();
+            break;
+        case "ladd":
+            mh = JSType.ADD_EXACT_LONG.methodHandle();
+            break;
+        case "lsub":
+            mh = JSType.SUB_EXACT_LONG.methodHandle();
+            break;
+        case "lmul":
+            mh = JSType.MUL_EXACT_LONG.methodHandle();
+            break;
+        case "ldiv":
+            mh = JSType.DIV_EXACT_LONG.methodHandle();
+            break;
+        case "lneg":
+            mh = JSType.NEGATE_EXACT_LONG.methodHandle();
+            break;
+        default:
+            throw new AssertionError("unsupported math intrinsic");
+        }
+        return new ConstantCallSite(MH.insertArguments(mh, mh.type().parameterCount() - 1, programPoint));
+    }
+
+    /**
      * Returns a dynamic invoker for a specified dynamic operation using the public lookup. You can use this method to
      * create a method handle that when invoked acts completely as if it were a Nashorn-linked call site. An overview of
      * available dynamic operations can be found in the
@@ -299,16 +381,16 @@
     /**
      * Takes a guarded invocation, and ensures its method and guard conform to the type of the call descriptor, using
      * all type conversions allowed by the linker's services. This method is used by Nashorn's linkers as a last step
-     * before returning guarded invocations to the callers. Most of the code used to produce the guarded invocations
-     * does not make an effort to coordinate types of the methods, and so a final type adjustment before a guarded
-     * invocation is returned is the responsibility of the linkers themselves.
+     * before returning guarded invocations. Most of the code used to produce the guarded invocations does not make an
+     * effort to coordinate types of the methods, and so a final type adjustment before a guarded invocation is returned
+     * to the aggregating linker is the responsibility of the linkers themselves.
      * @param inv the guarded invocation that needs to be type-converted. Can be null.
      * @param linkerServices the linker services object providing the type conversions.
      * @param desc the call site descriptor to whose method type the invocation needs to conform.
      * @return the type-converted guarded invocation. If input is null, null is returned. If the input invocation
      * already conforms to the requested type, it is returned unchanged.
      */
-    static GuardedInvocation asType(final GuardedInvocation inv, final LinkerServices linkerServices, final CallSiteDescriptor desc) {
-        return inv == null ? null : inv.asType(linkerServices, desc.getMethodType());
+    static GuardedInvocation asTypeSafeReturn(final GuardedInvocation inv, final LinkerServices linkerServices, final CallSiteDescriptor desc) {
+        return inv == null ? null : inv.asTypeSafeReturn(linkerServices, desc.getMethodType());
     }
 }
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java	Wed Feb 26 13:17:57 2014 +0100
@@ -76,7 +76,7 @@
             throw new AssertionError(); // Should never reach here.
         }
 
-        return Bootstrap.asType(inv, linkerServices, desc);
+        return Bootstrap.asTypeSafeReturn(inv, linkerServices, desc);
     }
 
     @Override
@@ -117,30 +117,31 @@
 
     private static GuardedInvocation findGetMethod(final CallSiteDescriptor desc) {
         final MethodHandle getter = MH.insertArguments(JSOBJECT_GETMEMBER, 1, desc.getNameToken(2));
-        return new GuardedInvocation(getter, null, IS_JSOBJECT_GUARD);
+        return new GuardedInvocation(getter, IS_JSOBJECT_GUARD);
     }
 
     private static GuardedInvocation findGetIndexMethod() {
-        return new GuardedInvocation(JSOBJECTLINKER_GET, null, IS_JSOBJECT_GUARD);
+        return new GuardedInvocation(JSOBJECTLINKER_GET, IS_JSOBJECT_GUARD);
     }
 
     private static GuardedInvocation findSetMethod(final CallSiteDescriptor desc) {
         final MethodHandle getter = MH.insertArguments(JSOBJECT_SETMEMBER, 1, desc.getNameToken(2));
-        return new GuardedInvocation(getter, null, IS_JSOBJECT_GUARD);
+        return new GuardedInvocation(getter, IS_JSOBJECT_GUARD);
     }
 
     private static GuardedInvocation findSetIndexMethod() {
-        return new GuardedInvocation(JSOBJECTLINKER_PUT, null, IS_JSOBJECT_GUARD);
+        return new GuardedInvocation(JSOBJECTLINKER_PUT, IS_JSOBJECT_GUARD);
     }
 
     private static GuardedInvocation findCallMethod(final CallSiteDescriptor desc) {
+        // TODO: if call site is already a vararg, don't do asCollector
         final MethodHandle func = MH.asCollector(JSOBJECT_CALL, Object[].class, desc.getMethodType().parameterCount() - 2);
-        return new GuardedInvocation(func, null, IS_JSOBJECT_GUARD);
+        return new GuardedInvocation(func, IS_JSOBJECT_GUARD);
     }
 
     private static GuardedInvocation findNewMethod(final CallSiteDescriptor desc) {
         final MethodHandle func = MH.asCollector(JSOBJECT_NEW, Object[].class, desc.getMethodType().parameterCount() - 1);
-        return new GuardedInvocation(func, null, IS_JSOBJECT_GUARD);
+        return new GuardedInvocation(func, IS_JSOBJECT_GUARD);
     }
 
     @SuppressWarnings("unused")
@@ -180,8 +181,8 @@
     }
 
     @SuppressWarnings("unused")
-    private static long toInt64(final JSObject obj) {
-        return JSType.toInt64(toNumber(obj));
+    private static long toLong(final JSObject obj) {
+        return JSType.toLong(toNumber(obj));
     }
 
     private static double toNumber(final JSObject obj) {
@@ -201,38 +202,29 @@
     private static final MethodHandleFunctionality MH = MethodHandleFactory.getFunctionality();
 
     // method handles of the current class
-    private static final MethodHandle IS_JSOBJECT_GUARD  = findOwnMH("isJSObject", boolean.class, Object.class);
-    private static final MethodHandle JSOBJECTLINKER_GET = findOwnMH("get", Object.class, Object.class, Object.class);
-    private static final MethodHandle JSOBJECTLINKER_PUT = findOwnMH("put", Void.TYPE, Object.class, Object.class, Object.class);
+    private static final MethodHandle IS_JSOBJECT_GUARD  = findOwnMH_S("isJSObject", boolean.class, Object.class);
+    private static final MethodHandle JSOBJECTLINKER_GET = findOwnMH_S("get", Object.class, Object.class, Object.class);
+    private static final MethodHandle JSOBJECTLINKER_PUT = findOwnMH_S("put", Void.TYPE, Object.class, Object.class, Object.class);
 
     // method handles of JSObject class
-    private static final MethodHandle JSOBJECT_GETMEMBER  = findJSObjectMH("getMember", Object.class, String.class);
-    private static final MethodHandle JSOBJECT_SETMEMBER  = findJSObjectMH("setMember", Void.TYPE, String.class, Object.class);
-    private static final MethodHandle JSOBJECT_CALL       = findJSObjectMH("call", Object.class, Object.class, Object[].class);
-    private static final MethodHandle JSOBJECT_NEW        = findJSObjectMH("newObject", Object.class, Object[].class);
+    private static final MethodHandle JSOBJECT_GETMEMBER  = findJSObjectMH_V("getMember", Object.class, String.class);
+    private static final MethodHandle JSOBJECT_SETMEMBER  = findJSObjectMH_V("setMember", Void.TYPE, String.class, Object.class);
+    private static final MethodHandle JSOBJECT_CALL       = findJSObjectMH_V("call", Object.class, Object.class, Object[].class);
+    private static final MethodHandle JSOBJECT_NEW        = findJSObjectMH_V("newObject", Object.class, Object[].class);
 
     private static final Map<Class<?>, MethodHandle> CONVERTERS = new HashMap<>();
     static {
-        CONVERTERS.put(boolean.class, findOwnMH("toBoolean", boolean.class, JSObject.class));
-        CONVERTERS.put(int.class, findOwnMH("toInt32", int.class, JSObject.class));
-        CONVERTERS.put(long.class, findOwnMH("toInt64", long.class, JSObject.class));
-        CONVERTERS.put(double.class, findOwnMH("toNumber", double.class, JSObject.class));
-    }
-
-    private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
-        return findMH(name, JSObjectLinker.class, rtype, types);
+        CONVERTERS.put(boolean.class, findOwnMH_S("toBoolean", boolean.class, JSObject.class));
+        CONVERTERS.put(int.class,     findOwnMH_S("toInt32", int.class, JSObject.class));
+        CONVERTERS.put(long.class,    findOwnMH_S("toLong", long.class, JSObject.class));
+        CONVERTERS.put(double.class,  findOwnMH_S("toNumber", double.class, JSObject.class));
     }
 
-    private static MethodHandle findJSObjectMH(final String name, final Class<?> rtype, final Class<?>... types) {
-        return findMH(name, JSObject.class, rtype, types);
+    private static MethodHandle findJSObjectMH_V(final String name, final Class<?> rtype, final Class<?>... types) {
+        return MH.findVirtual(MethodHandles.lookup(), JSObject.class, name, MH.type(rtype, types));
     }
 
-    private static MethodHandle findMH(final String name, final Class<?> target, final Class<?> rtype, final Class<?>... types) {
-        final MethodType mt  = MH.type(rtype, types);
-        try {
-            return MH.findStatic(MethodHandles.lookup(), target, name, mt);
-        } catch (final MethodHandleFactory.LookupException e) {
-            return MH.findVirtual(MethodHandles.lookup(), target, name, mt);
-        }
+    private static MethodHandle findOwnMH_S(final String name, final Class<?> rtype, final Class<?>... types) {
+        return MH.findStatic(MethodHandles.lookup(), JSObjectLinker.class, name, MH.type(rtype, types));
     }
 }
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java	Wed Feb 26 13:17:57 2014 +0100
@@ -164,7 +164,7 @@
         final StaticClass adapterClass = getAdapterClassFor(new Class<?>[] { targetType }, null, lookup);
         return MH.bindTo(Bootstrap.getLinkerServices().getGuardedInvocation(new LinkRequestImpl(
                 NashornCallSiteDescriptor.get(lookup, "dyn:new",
-                        MethodType.methodType(targetType, StaticClass.class, sourceType), 0), false,
+                        MethodType.methodType(targetType, StaticClass.class, sourceType), 0), null, false,
                         adapterClass, null)).getInvocation(), adapterClass);
     }
 
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaSuperAdapter.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaSuperAdapter.java	Wed Feb 26 13:17:57 2014 +0100
@@ -27,7 +27,7 @@
 
 /**
  * Represents a an adapter for invoking superclass methods on an adapter instance generated by
- * {@link JavaAdapterBytecodeGenerator}. Note that objects of this class are just wrappers around the adapter instances,
+ * {@code JavaAdapterBytecodeGenerator}. Note that objects of this class are just wrappers around the adapter instances,
  * without any behavior. All the behavior is defined in the {@code JavaSuperAdapterLinker}.
  */
 class JavaSuperAdapter {
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaSuperAdapterLinker.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaSuperAdapterLinker.java	Wed Feb 26 13:17:57 2014 +0100
@@ -42,7 +42,7 @@
 import jdk.nashorn.internal.runtime.ScriptRuntime;
 
 /**
- * A linker for instances of {@link JavaSuperAdapter}. Only links {@code getMethod} calls, by forwarding them to the
+ * A linker for instances of {@code JavaSuperAdapter}. Only links {@code getMethod} calls, by forwarding them to the
  * bean linker for the adapter class and prepending {@code super$} to method names.
  *
  */
@@ -115,15 +115,15 @@
 
         final MethodHandle invocation = guardedInv.getInvocation();
         final MethodType invType = invocation.type();
-        // For invocation typed R(T0, ...) create a dynamic method binder of type R(R, T0)
-        final MethodHandle typedBinder = BIND_DYNAMIC_METHOD.asType(MethodType.methodType(invType.returnType(),
+        // For invocation typed R(T0, ...) create a dynamic method binder of type Object(R, T0)
+        final MethodHandle typedBinder = BIND_DYNAMIC_METHOD.asType(MethodType.methodType(Object.class,
                 invType.returnType(), invType.parameterType(0)));
-        // For invocation typed R(T0, T1, ...) create a dynamic method binder of type R(R, T0, T1, ...)
+        // For invocation typed R(T0, T1, ...) create a dynamic method binder of type Object(R, T0, T1, ...)
         final MethodHandle droppingBinder = MethodHandles.dropArguments(typedBinder, 2,
                 invType.parameterList().subList(1, invType.parameterCount()));
         // Finally, fold the invocation into the binder to produce a method handle that will bind every returned
         // DynamicMethod object from dyn:getMethod calls to the actual receiver
-        // R(R(T0, T1, ...), T0, T1, ...)
+        // Object(R(T0, T1, ...), T0, T1, ...)
         final MethodHandle bindingInvocation = MethodHandles.foldArguments(droppingBinder, invocation);
 
         final MethodHandle typedGetAdapter = asFilterType(GET_ADAPTER, 0, invType, type);
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/LinkerCallSite.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/LinkerCallSite.java	Wed Feb 26 13:17:57 2014 +0100
@@ -46,7 +46,6 @@
 import jdk.internal.dynalink.ChainedCallSite;
 import jdk.internal.dynalink.DynamicLinker;
 import jdk.internal.dynalink.linker.GuardedInvocation;
-import jdk.nashorn.internal.lookup.MethodHandleFactory;
 import jdk.nashorn.internal.runtime.Context;
 import jdk.nashorn.internal.runtime.Debug;
 import jdk.nashorn.internal.runtime.ScriptObject;
@@ -63,7 +62,7 @@
 
     private static final String PROFILEFILE = Options.getStringProperty("nashorn.profilefile", "NashornProfile.txt");
 
-    private static final MethodHandle INCREASE_MISS_COUNTER = findOwnMH("increaseMissCount", Object.class, String.class, Object.class);
+    private static final MethodHandle INCREASE_MISS_COUNTER = MH.findStatic(MethodHandles.lookup(), LinkerCallSite.class, "increaseMissCount", MH.type(Object.class, String.class, Object.class));
 
     LinkerCallSite(final NashornCallSiteDescriptor descriptor) {
         super(descriptor);
@@ -145,11 +144,10 @@
      */
     public static Object increaseMissCount(final String desc, final Object self) {
         ++missCount;
-        if(r.nextInt(100) < missSamplingPercentage) {
-            AtomicInteger i = missCounts.get(desc);
-            if(i == null) {
-                i = new AtomicInteger(1);
-                missCounts.put(desc, i);
+        if (r.nextInt(100) < missSamplingPercentage) {
+            final AtomicInteger i = missCounts.get(desc);
+            if (i == null) {
+                missCounts.put(desc, new AtomicInteger(1));
             } else {
                 i.incrementAndGet();
             }
@@ -157,14 +155,6 @@
         return self;
     }
 
-    private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
-        try {
-            return MH.findStatic(MethodHandles.lookup(), LinkerCallSite.class, name, MH.type(rtype, types));
-        } catch (final MethodHandleFactory.LookupException e) {
-            return MH.findVirtual(MethodHandles.lookup(), LinkerCallSite.class, name, MH.type(rtype, types));
-        }
-    }
-
     /*
      * Debugging call sites.
      */
@@ -314,10 +304,11 @@
      * Debug subclass for LinkerCallSite that allows tracing
      */
     private static class TracingLinkerCallSite extends LinkerCallSite {
+        private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
 
-        private static final MethodHandle TRACEOBJECT = findOwnMH("traceObject", Object.class, MethodHandle.class, Object[].class);
-        private static final MethodHandle TRACEVOID   = findOwnMH("traceVoid", void.class, MethodHandle.class, Object[].class);
-        private static final MethodHandle TRACEMISS   = findOwnMH("traceMiss", void.class, String.class, Object[].class);
+        private static final MethodHandle TRACEOBJECT = MH.findVirtual(LOOKUP, TracingLinkerCallSite.class, "traceObject", MH.type(Object.class, MethodHandle.class, Object[].class));
+        private static final MethodHandle TRACEVOID   = MH.findVirtual(LOOKUP, TracingLinkerCallSite.class, "traceVoid", MH.type(void.class, MethodHandle.class, Object[].class));
+        private static final MethodHandle TRACEMISS   = MH.findVirtual(LOOKUP, TracingLinkerCallSite.class, "traceMiss", MH.type(void.class, String.class, Object[].class));
 
         TracingLinkerCallSite(final NashornCallSiteDescriptor desc) {
            super(desc);
@@ -486,14 +477,6 @@
         public void traceMiss(final String desc, final Object... args) throws Throwable {
             tracePrint(Context.getCurrentErr(), desc, args, null);
         }
-
-        private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
-            try {
-                return MH.findStatic(MethodHandles.lookup(), TracingLinkerCallSite.class, name, MH.type(rtype, types));
-            } catch (final MethodHandleFactory.LookupException e) {
-                return MH.findVirtual(MethodHandles.lookup(), TracingLinkerCallSite.class, name, MH.type(rtype, types));
-            }
-        }
     }
 
     // counters updated in debug mode
@@ -503,6 +486,11 @@
     private static final Random r = new Random();
     private static final int missSamplingPercentage = Options.getIntProperty("nashorn.tcs.miss.samplePercent", 1);
 
+    @Override
+    protected int getMaxChainLength() {
+        return 16;
+    }
+
     /**
      * Get the callsite count
      * @return the count
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java	Wed Feb 26 13:17:57 2014 +0100
@@ -100,6 +100,11 @@
             return filters != null ? MethodHandles.filterArguments(typed, 0, filters) : typed;
         }
 
+        @Override
+        public MethodHandle asTypeLosslessReturn(MethodHandle handle, MethodType fromType) {
+            return Implementation.asTypeLosslessReturn(this, handle, fromType);
+        }
+
         private static boolean shouldConvert(final Class<?> handleType, final Class<?> fromType) {
             return handleType == Object.class && fromType == Object.class;
         }
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java	Wed Feb 26 13:17:57 2014 +0100
@@ -44,9 +44,11 @@
 import jdk.internal.dynalink.linker.LinkRequest;
 import jdk.internal.dynalink.linker.LinkerServices;
 import jdk.internal.dynalink.support.Guards;
+import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.runtime.Context;
 import jdk.nashorn.internal.runtime.JSType;
 import jdk.nashorn.internal.runtime.ScriptRuntime;
+import jdk.nashorn.internal.runtime.UnwarrantedOptimismException;
 
 /**
  * Nashorn bottom linker; used as a last-resort catch-all linker for all linking requests that fall through all other
@@ -104,10 +106,10 @@
                 if (callType.parameterCount() != m.getParameterCount() + 2) {
                     throw typeError("no.method.matches.args", ScriptRuntime.safeToString(self));
                 }
-                return new GuardedInvocation(
+                return Bootstrap.asTypeSafeReturn(new GuardedInvocation(
                         // drop 'thiz' passed from the script.
                         MH.dropArguments(desc.getLookup().unreflect(m), 1, callType.parameterType(1)),
-                        Guards.getInstanceOfGuard(m.getDeclaringClass())).asType(callType);
+                        Guards.getInstanceOfGuard(m.getDeclaringClass())), linkerServices, desc);
             }
             if(BeansLinker.isDynamicMethod(self)) {
                 throw typeError("no.method.matches.args", ScriptRuntime.safeToString(self));
@@ -118,6 +120,9 @@
             throw typeError("no.such.function", getArgument(linkRequest), ScriptRuntime.safeToString(self));
         case "getProp":
         case "getElem":
+            if(NashornCallSiteDescriptor.isOptimistic(desc)) {
+                throw new UnwarrantedOptimismException(UNDEFINED, NashornCallSiteDescriptor.getProgramPoint(desc), Type.OBJECT);
+            }
             if (desc.getOperand() != null) {
                 return getInvocation(EMPTY_PROP_GETTER, self, linkerServices, desc);
             }
@@ -151,14 +156,14 @@
     private static GuardedInvocation convertToTypeNoCast(final Class<?> sourceType, final Class<?> targetType) throws Exception {
         final MethodHandle mh = CONVERTERS.get(targetType);
         if (mh != null) {
-            return new GuardedInvocation(mh, null);
+            return new GuardedInvocation(mh);
         }
 
         return null;
     }
 
     private static GuardedInvocation getInvocation(final MethodHandle handle, final Object self, final LinkerServices linkerServices, final CallSiteDescriptor desc) {
-        return Bootstrap.asType(new GuardedInvocation(handle, Guards.getClassGuard(self.getClass())), linkerServices, desc);
+        return Bootstrap.asTypeSafeReturn(new GuardedInvocation(handle, Guards.getClassGuard(self.getClass())), linkerServices, desc);
     }
 
     // Used solely in an assertion to figure out if the object we get here is something we in fact expect. Objects
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/NashornCallSiteDescriptor.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/NashornCallSiteDescriptor.java	Wed Feb 26 13:17:57 2014 +0100
@@ -42,35 +42,56 @@
 public final class NashornCallSiteDescriptor extends AbstractCallSiteDescriptor {
     /** Flags that the call site references a scope variable (it's an identifier reference or a var declaration, not a
      * property access expression. */
-    public static final int CALLSITE_SCOPE                = 0x01;
+    public static final int CALLSITE_SCOPE      = 1 << 0;
     /** Flags that the call site is in code that uses ECMAScript strict mode. */
-    public static final int CALLSITE_STRICT               = 0x02;
+    public static final int CALLSITE_STRICT     = 1 << 1;
     /** Flags that a property getter or setter call site references a scope variable that is located at a known distance
      * in the scope chain. Such getters and setters can often be linked more optimally using these assumptions. */
-    public static final int CALLSITE_FAST_SCOPE    = 0x400;
+    public static final int CALLSITE_FAST_SCOPE = 1 << 2;
+    /** Flags that a callsite type is optimistic, i.e. we might get back a wider return value than encoded in the
+     * descriptor, and in that case we have to throw an UnwarrantedOptimismException */
+    public static final int CALLSITE_OPTIMISTIC = 1 << 3;
 
     /** Flags that the call site is profiled; Contexts that have {@code "profile.callsites"} boolean property set emit
      * code where call sites have this flag set. */
-    public static final int CALLSITE_PROFILE          = 0x10;
+    public static final int CALLSITE_PROFILE        = 1 << 4;
     /** Flags that the call site is traced; Contexts that have {@code "trace.callsites"} property set emit code where
      * call sites have this flag set. */
-    public static final int CALLSITE_TRACE            = 0x20;
+    public static final int CALLSITE_TRACE          = 1 << 5;
     /** Flags that the call site linkage miss (and thus, relinking) is traced; Contexts that have the keyword
      * {@code "miss"} in their {@code "trace.callsites"} property emit code where call sites have this flag set. */
-    public static final int CALLSITE_TRACE_MISSES     = 0x40;
+    public static final int CALLSITE_TRACE_MISSES   = 1 << 6;
     /** Flags that entry/exit to/from the method linked at call site are traced; Contexts that have the keyword
-     * {@code "enterexit"} in their {@code "trace.callsites"} property emit code where call sites have this flag
-     * set. */
-    public static final int CALLSITE_TRACE_ENTEREXIT  = 0x80;
+     * {@code "enterexit"} in their {@code "trace.callsites"} property emit code where call sites have this flag set. */
+    public static final int CALLSITE_TRACE_ENTEREXIT = 1 << 7;
     /** Flags that values passed as arguments to and returned from the method linked at call site are traced; Contexts
      * that have the keyword {@code "values"} in their {@code "trace.callsites"} property emit code where call sites
      * have this flag set. */
-    public static final int CALLSITE_TRACE_VALUES    = 0x100;
+    public static final int CALLSITE_TRACE_VALUES   = 1 << 8;
     /** Ordinarily, when {@link #CALLSITE_TRACE_VALUES} is set, scope objects are not printed in the trace but instead
      * the word {@code "SCOPE"} is printed instead With this flag, scope objects are also printed. Contexts that have
      * the keyword {@code "scope"} in their {@code "trace.callsites"} property emit code where call sites have this flag
      * set. */
-    public static final int CALLSITE_TRACE_SCOPE      = 0x200;
+    public static final int CALLSITE_TRACE_SCOPE      = 1 << 9;
+
+    /**
+     * Number of bits the program point is shifted to the left in the flags (lowest bit containing a program point).
+     * Always one larger than the largest flag shift. Note that introducing a new flag halves the number of program
+     * points we can have.
+     * TODO: rethink if we need the various profile/trace flags or the linker can use the Context instead to query its
+     * trace/profile settings.
+     */
+    public static final int CALLSITE_PROGRAM_POINT_SHIFT = 10;
+
+    /**
+     * Maximum program point value. 22 bits should be enough for anyone
+     */
+    public static final int MAX_PROGRAM_POINT_VALUE = (1 << (32 - CALLSITE_PROGRAM_POINT_SHIFT)) - 1;
+
+    /**
+     * Flag mask to get the program point flags
+     */
+    public static final int FLAGS_MASK = (1 << CALLSITE_PROGRAM_POINT_SHIFT) - 1;
 
     private static final ClassValue<ConcurrentMap<NashornCallSiteDescriptor, NashornCallSiteDescriptor>> canonicals =
             new ClassValue<ConcurrentMap<NashornCallSiteDescriptor,NashornCallSiteDescriptor>>() {
@@ -262,6 +283,25 @@
         return isFlag(desc, CALLSITE_STRICT);
     }
 
+    /**
+     * Is this an optimistic call site
+     * @param desc descriptor
+     * @return true if optimistic
+     */
+    public static boolean isOptimistic(final CallSiteDescriptor desc) {
+        return isFlag(desc, CALLSITE_OPTIMISTIC);
+    }
+
+    /**
+     * Get a program point from a descriptor (must be optimistic)
+     * @param desc descriptor
+     * @return program point
+     */
+    public static int getProgramPoint(final CallSiteDescriptor desc) {
+        assert isOptimistic(desc) : "program point requested from non-optimistic descriptor " + desc;
+        return getFlags(desc) >> CALLSITE_PROGRAM_POINT_SHIFT;
+    }
+
     boolean isProfile() {
         return isFlag(CALLSITE_PROFILE);
     }
@@ -286,6 +326,10 @@
         return isFlag(CALLSITE_TRACE_SCOPE);
     }
 
+    boolean isOptimistic() {
+        return isFlag(CALLSITE_OPTIMISTIC);
+    }
+
     @Override
     public CallSiteDescriptor changeMethodType(final MethodType newMethodType) {
         return get(getLookup(), operator, operand, newMethodType, flags);
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/NashornGuards.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/NashornGuards.java	Wed Feb 26 13:17:57 2014 +0100
@@ -29,40 +29,22 @@
 
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
+
 import jdk.nashorn.internal.runtime.PropertyMap;
-import jdk.nashorn.internal.runtime.ScriptFunction;
 import jdk.nashorn.internal.runtime.ScriptObject;
 
 /**
  * Constructor of method handles used to guard call sites.
  */
 public final class NashornGuards {
-    private static final MethodHandle IS_SCRIPTOBJECT          = findOwnMH("isScriptObject", boolean.class, Object.class);
-    private static final MethodHandle IS_SCRIPTFUNCTION        = findOwnMH("isScriptFunction", boolean.class, Object.class);
-    private static final MethodHandle IS_MAP                   = findOwnMH("isMap", boolean.class, Object.class, PropertyMap.class);
-    private static final MethodHandle IS_INSTANCEOF_2          = findOwnMH("isInstanceOf2", boolean.class, Object.class, Class.class, Class.class);
+    private static final MethodHandle IS_MAP          = findOwnMH("isMap", boolean.class, ScriptObject.class, PropertyMap.class);
+    private static final MethodHandle IS_INSTANCEOF_2 = findOwnMH("isInstanceOf2", boolean.class, Object.class, Class.class, Class.class);
 
     // don't create me!
     private NashornGuards() {
     }
 
     /**
-     * Get the guard that checks if an item is a {@code ScriptObject}
-     * @return method handle for guard
-     */
-    public static MethodHandle getScriptObjectGuard() {
-        return IS_SCRIPTOBJECT;
-    }
-
-    /**
-     * Get the guard that checks if an item is a {@code ScriptFunction}
-     * @return method handle for guard
-     */
-    public static MethodHandle getScriptFunctionGuard() {
-        return IS_SCRIPTFUNCTION;
-    }
-
-    /**
      * Get the guard that checks if a {@link PropertyMap} is equal to
      * a known map, using reference comparison
      *
@@ -86,18 +68,8 @@
     }
 
     @SuppressWarnings("unused")
-    private static boolean isScriptObject(final Object self) {
-        return self instanceof ScriptObject;
-    }
-
-    @SuppressWarnings("unused")
-    private static boolean isScriptFunction(final Object self) {
-        return self instanceof ScriptFunction;
-    }
-
-    @SuppressWarnings("unused")
-    private static boolean isMap(final Object self, final PropertyMap map) {
-        return self instanceof ScriptObject && ((ScriptObject)self).getMap() == map;
+    private static boolean isMap(final ScriptObject self, final PropertyMap map) {
+        return self.getMap() == map;
     }
 
     @SuppressWarnings("unused")
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/NashornLinker.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/NashornLinker.java	Wed Feb 26 13:17:57 2014 +0100
@@ -91,6 +91,10 @@
             return null;
         }
 
+        return Bootstrap.asTypeSafeReturn(getGuardedInvocation(self,  request, desc), linkerServices, desc);
+    }
+
+    private static GuardedInvocation getGuardedInvocation(final Object self, final LinkRequest request, final CallSiteDescriptor desc) {
         final GuardedInvocation inv;
         if (self instanceof ScriptObject) {
             inv = ((ScriptObject)self).lookup(desc, request);
@@ -100,7 +104,7 @@
             throw new AssertionError(); // Should never reach here.
         }
 
-        return Bootstrap.asType(inv, linkerServices, desc);
+        return inv;
     }
 
     @Override
@@ -213,7 +217,7 @@
         // it's probably better to explicitly spell out the supported target types
         if (targetType == Map.class || targetType == Bindings.class || targetType == JSObject.class || targetType == ScriptObjectMirror.class) {
             if(ScriptObject.class.isAssignableFrom(sourceType)) {
-                return new GuardedInvocation(CREATE_MIRROR, null);
+                return new GuardedInvocation(CREATE_MIRROR);
             }
             return new GuardedInvocation(CREATE_MIRROR, IS_SCRIPT_OBJECT);
         }
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/NashornPrimitiveLinker.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/NashornPrimitiveLinker.java	Wed Feb 26 13:17:57 2014 +0100
@@ -65,7 +65,7 @@
         final GlobalObject global = (GlobalObject) Context.getGlobal();
         final NashornCallSiteDescriptor desc = (NashornCallSiteDescriptor) request.getCallSiteDescriptor();
 
-        return Bootstrap.asType(global.primitiveLookup(request, self), linkerServices, desc);
+        return Bootstrap.asTypeSafeReturn(global.primitiveLookup(request, self), linkerServices, desc);
     }
 
     /**
--- a/nashorn/src/jdk/nashorn/internal/runtime/resources/Messages.properties	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/resources/Messages.properties	Wed Feb 26 13:17:57 2014 +0100
@@ -78,6 +78,7 @@
 type.error.not.a.function={0} is not a function
 type.error.not.a.constructor={0} is not a constructor function
 type.error.not.a.file={0} is not a File
+type.error.not.a.numeric.array={0} is not a numeric array
 type.error.not.a.bytebuffer={0} is not a java.nio.ByteBuffer
 
 # operations not permitted on undefined
--- a/nashorn/src/jdk/nashorn/internal/runtime/resources/Options.properties	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/resources/Options.properties	Wed Feb 26 13:17:57 2014 +0100
@@ -187,12 +187,6 @@
     desc="Generate local variable table in .class files." \
 }
 
-nashorn.option.lazy.compilation = {                                                                      \
-    name="--lazy-compilation",                                                                           \
-    is_undocumented=true,                                                                                \
-    desc="EXPERIMENTAL: Use lazy code generation strategies - do not compile the entire script at once." \
-}
-
 nashorn.option.loader.per.compile = {              \
     name="--loader-per-compile",                   \
     is_undocumented=true,                          \
@@ -251,8 +245,11 @@
 
 nashorn.option.print.code = { \
     name="--print-code",      \
+    short_name="-pc",         \
     is_undocumented=true,     \
-    desc="Print bytecode."    \
+    params="[dir:<output-dir>,function:<name>]", \
+    type=keyvalues,           \
+    desc="Print generated bytecode. If a directory is specified, nothing will be dumped to stderr. Also, in that case, .dot files will be generated for all functions or for the function with the specified name only."  \
 }
 
 nashorn.option.print.mem.usage = {                            \
@@ -269,12 +266,14 @@
 
 nashorn.option.print.parse = {   \
     name="--print-parse",        \
+    short_name="-pp",            \
     is_undocumented=true,        \
     desc="Print the parse tree." \
 }
 
 nashorn.option.print.lower.parse = {            \
     name="--print-lower-parse",                 \
+    short_name="-plp",                          \
     is_undocumented=true,                       \
     desc="Print the parse tree after lowering." \
 }
--- a/nashorn/src/jdk/nashorn/internal/scripts/JO.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/scripts/JO.java	Wed Feb 26 13:17:57 2014 +0100
@@ -25,6 +25,7 @@
 
 package jdk.nashorn.internal.scripts;
 
+import jdk.nashorn.internal.codegen.SpillObjectCreator;
 import jdk.nashorn.internal.runtime.PropertyMap;
 import jdk.nashorn.internal.runtime.ScriptObject;
 
@@ -63,6 +64,18 @@
     }
 
     /**
+     * Constructor that takes a pre-initialized spill pool. Used for
+     * by {@link SpillObjectCreator} for intializing object literals
+     *
+     * @param map            property map
+     * @param primitiveSpill primitive spill pool
+     * @param objectSpill    reference spill pool
+     */
+    public JO(final PropertyMap map, final long[] primitiveSpill, final Object[] objectSpill) {
+        super(map, primitiveSpill, objectSpill);
+    }
+
+    /**
      * A method handle of this method is passed to the ScriptFunction constructor.
      *
      * @param map  the property map to use for allocatorMap
--- a/nashorn/src/jdk/nashorn/tools/Shell.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/tools/Shell.java	Wed Feb 26 13:17:57 2014 +0100
@@ -37,8 +37,10 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.ResourceBundle;
+
 import jdk.nashorn.api.scripting.NashornException;
 import jdk.nashorn.internal.codegen.Compiler;
+import jdk.nashorn.internal.codegen.CompilerConstants;
 import jdk.nashorn.internal.ir.FunctionNode;
 import jdk.nashorn.internal.ir.debug.ASTWriter;
 import jdk.nashorn.internal.ir.debug.PrintVisitor;
@@ -223,6 +225,7 @@
 
     /**
      * Compiles the given script files in the command line
+     * This is called only when using the --compile-only flag
      *
      * @param context the nashorn context
      * @param global the global scope
@@ -243,7 +246,7 @@
 
             // For each file on the command line.
             for (final String fileName : files) {
-                final FunctionNode functionNode = new Parser(env, new Source(fileName, new File(fileName)), errors).parse();
+                final FunctionNode functionNode = new Parser(env, new Source(fileName, new File(fileName)), errors, env._strict).parse();
 
                 if (errors.getNumberOfErrors() != 0) {
                     return COMPILATION_ERROR;
@@ -258,7 +261,8 @@
                 }
 
                 //null - pass no code installer - this is compile only
-                new Compiler(env).compile(functionNode);
+                new Compiler(env).
+                    compile(CompilerConstants.DEFAULT_SCRIPT_NAME.symbolName(), functionNode);
             }
         } finally {
             env.getOut().flush();
--- a/nashorn/test/script/assert.js	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/test/script/assert.js	Wed Feb 26 13:17:57 2014 +0100
@@ -29,7 +29,7 @@
 
 // Assert is TestNG's Assert class
 Object.defineProperty(this, "Assert", { 
-    configuable: true,
+    configurable: true,
     enumerable: false,
     writable: true,
     value: Packages.org.testng.Assert
@@ -37,7 +37,7 @@
 
 // fail function to call TestNG Assert.fail
 Object.defineProperty(this, "fail", {
-    configuable: true,
+    configurable: true,
     enumerable: false,
     writable: true,
     // 'error' is optional. if present it has to be 
@@ -63,7 +63,7 @@
 });
 
 Object.defineProperty(this, "printError", {
-    configuable: true,
+    configurable: true,
     enumerable: false,
     writable: true,
     value: function (e) {
--- a/nashorn/test/script/basic/JDK-8025515.js	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/test/script/basic/JDK-8025515.js	Wed Feb 26 13:17:57 2014 +0100
@@ -47,7 +47,7 @@
         var stack = e.nashornException.getStackTrace();
         var name = getFirstScriptFrame(stack).methodName;
         if (name !== expected) {
-            fail("got " + stack[0].methodName + ", expected " + expected);
+            fail("got " + name + ", expected " + expected);
         }
     }
 }
@@ -61,7 +61,7 @@
 var f = (function() {
     return function() { a.b.c; };
 })();
-testMethodName(f, "L:61$L:62");
+testMethodName(f, "f$L:62");
 
 testMethodName((function() {
     return function() { return a.b.c; };
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8029384.js	Wed Feb 26 13:17:57 2014 +0100
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ * 
+ * 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.
+ */
+
+/**
+ * JDK-8029384: Function expression self-symbol not available for eval
+ *
+ * @test
+ * @run
+ */
+
+var g = function f() { print(eval("f.x")) }
+g.x = "tada!"
+g()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8029384.js.EXPECTED	Wed Feb 26 13:17:57 2014 +0100
@@ -0,0 +1,1 @@
+tada!
--- a/nashorn/test/script/basic/JDK-8029467.js	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/test/script/basic/JDK-8029467.js	Wed Feb 26 13:17:57 2014 +0100
@@ -32,3 +32,4 @@
 print((function (x) { if(x) { return true } else { return 0 } })(true))
 print(typeof (function (x) { return x ? 1 : "123" })(true) === "number")
 print(typeof (function (x) { if(x) { return 1 } else { return "123" } })(true) === "number")
+
--- a/nashorn/test/script/basic/JDK-8029667.js	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/test/script/basic/JDK-8029667.js	Wed Feb 26 13:17:57 2014 +0100
@@ -87,5 +87,3 @@
 
 print(withScopes[0].func);
 print(withScopes[1].func);
-
-
--- a/nashorn/test/script/basic/exprclosure.js.EXPECTED	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/test/script/basic/exprclosure.js.EXPECTED	Wed Feb 26 13:17:57 2014 +0100
@@ -1,4 +1,4 @@
-function(x) x*x;
+function(x) x*x
 625
-function fun(x) x * (x + 1) / 2;
+function fun(x) x * (x + 1) / 2
 55
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/hideLocationProperties.js	Wed Feb 26 13:17:57 2014 +0100
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ * 
+ * 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.
+ */
+
+/**
+ * Tests the hiding of location properties by local variables etc.
+ * 
+ * @test
+ * @run
+ */
+
+function x(__FILE__) {
+    print(__FILE__);
+}
+
+function y(__FILE__) {
+    print(__FILE__);
+    function y1() { __FILE__ }
+}
+
+function w(__FILE__) {
+    print(__FILE__);
+    return arguments;
+}
+
+function z(__FILE__) {
+    (function() { print(__FILE__) })()
+}
+
+print(__FILE__)
+
+var o = { __FILE__: "woot" }
+with(o) { print(__FILE__) }
+
+x("foo")
+y("bar")
+w("boo")
+z("baz")
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/hideLocationProperties.js.EXPECTED	Wed Feb 26 13:17:57 2014 +0100
@@ -0,0 +1,6 @@
+test/script/basic/hideLocationProperties.js
+woot
+foo
+bar
+boo
+baz
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/relinkIndexGetter.js	Wed Feb 26 13:17:57 2014 +0100
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ * 
+ * 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.
+ */
+
+/**
+ * relinkIndexGetter: after call site was linked for array, make sure it
+ * gets correctly relinked for boolean.
+ *
+ * @test
+ * @run
+ */
+
+var x = [[], false]
+for(var i in x) { print(x[i][0]) }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/relinkIndexGetter.js.EXPECTED	Wed Feb 26 13:17:57 2014 +0100
@@ -0,0 +1,2 @@
+undefined
+undefined
--- a/nashorn/test/script/basic/run-octane.js	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/test/script/basic/run-octane.js	Wed Feb 26 13:17:57 2014 +0100
@@ -156,6 +156,7 @@
 
     } catch (e) {
 	print_always("*** Aborted and setting score to zero. Reason: " + e);
+	e.printStackTrace();
 	mean_score = min_score = max_score = 0;
 	scores = [0];
     }
--- a/nashorn/test/script/basic/runsunspider-lazy.js	Tue Feb 25 18:56:10 2014 +0530
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,34 +0,0 @@
-/*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- * 
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- * 
- * 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.
- */
-
-/**
- * runsunspider : runs the sunspider tests and checks for compliance
- *
- * @test
- * @option -timezone=PST
- * @option --lazy-compilation
- * @runif external.sunspider
- */
-
-load(__DIR__ + "runsunspider.js");
-
--- a/nashorn/test/script/basic/runsunspider.js	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/test/script/basic/runsunspider.js	Wed Feb 26 13:17:57 2014 +0100
@@ -37,32 +37,31 @@
     }
 }
 
+function pprint(x) {
+    if (verbose_run) {
+	print(x);
+    }
+}
+
 var runs = 0;
-var iterations__ = 1;
 var total_time = 0;
 
 function runbench(name) {
     var filename = name.split("/").pop();
-    if (verbose_run) {
-        print("Running " + filename);
-    }
+    pprint("Running (warmup/sanity) " + filename);
 
     var start = new Date;
-    for (var i = 0; i < iterations__; i++) {
-        load(name);
-    }
+    load(name);
     var stop = new Date - start;
     total_time += stop;
 
-    if (verbose_run) {
-        print(filename + " done in " + stop + " ms");
-    }
+    pprint(filename + " done in " + stop + " ms");
     runs++;
 }
 
-var m_w = 4711;
-var m_z = 17;
-var MAXINT = 0x7fffffff;
+var m_w;
+var m_z;
+var MAXINT;
 
 //produce deterministic random numbers for test suite
 function pseudorandom() {
@@ -71,25 +70,115 @@
     return (Math.abs((m_z << 16) + m_w) & MAXINT) / MAXINT;
 }
 
-function runsuite(tests) {
-    var changed = false;
+function initrandom() {
+    m_w = 4711;
+    m_z = 17;
+    MAXINT = 0x7fffffff;
+    Math.random = pseudorandom;
+}
+
+var rtimes = 0;
+var dir = (typeof(__DIR__) == 'undefined') ? "test/script/basic/" : __DIR__;
+var single;
+var verbose_run = false;
 
+var args = [];
+if (typeof $ARGS !== 'undefined') {
+    args = $ARGS;
+} else if (typeof arguments !== 'undefined' && arguments.length != 0) {
+    args = arguments;
+}
+
+for (var i = 0; i < args.length; i++) {
+    if (args[i] === '--verbose') {
+        verbose_run = true;
+    } else if (args[i] === '--times') {
+	i++;
+	rtimes = +args[i];
+    } else if (args[i] === '--single') {
+	i++;
+	single = args[i];
+    }
+}
+
+function runsuite(tests) {
+    var changed   = false;
+    var res       = [];
     var oldRandom = Math.random;
-    Math.random = pseudorandom;
 
     try {
-        for (var n = 0; n < tests.length; n++) {
-            path = dir + '../external/sunspider/tests/sunspider-1.0/' + tests[n].name
+	for (var n = 0; n < tests.length; n++) {
+            path = dir + '../external/sunspider/tests/sunspider-1.0.2/' + tests[n].name
+
+	    initrandom();
+
+	    var dd = new Date;
+
             runbench(path);
             if (typeof tests[n].actual !== 'undefined') {
                 assertEq(tests[n].actual(), tests[n].expected());
             }
+
+	    if (typeof tests[n].rerun !== 'undefined' && tests[n].times > 0) {
+		pprint("rerunning " + tests[n].name + " " + tests[n].times + " times...");
+		var times = 0;
+		var to = tests[n].times;
+
+		var elemsPerPercent = to / 100;
+		var po = 0|(to / 10);
+
+		times = 0;
+		for (; times < to; times++) {
+		    initrandom();
+		    tests[n].rerun();
+		    if ((times % (po|0)) == 0) {
+			pprint(times/to * 100 + "%");
+		    }		    
+		}
+	    }
+	    
+	    var t = new Date - dd;		    
+	    pprint("time: " + t + " ms");
+	    if (typeof tests[n].actual !== 'undefined') {
+		assertEq(tests[n].actual(), tests[n].expected());		    			
+	    }
+	    res.push(t);
+
+	    pprint("");
+
             changed = true;
         }
+    } catch (e) {
+	print("FAIL!");
+	throw e;
         // no scripting or something, silently fail
     } finally {
+	Math.random = oldRandom;
     }
-    Math.random = oldRandom;
+
+    for (var n = 0; n < tests.length; n++) {	
+	
+	var time = "" + res[n];
+	while (time.length < 4) {
+	    time = " " + time;
+	}
+	time += " ms";
+	if (res[n] == -1) {
+	    time = "<couldn't be rerun>";
+	}
+	var str = tests[n].name;
+	for (var spaces = str.length; spaces < 32; spaces++) {
+	    str += " ";
+	}
+	str += " ";
+	str += time;
+
+	if (tests[n].times > 0) {
+	    str += " [";
+	    str += tests[n].times + " reruns]";
+	} 
+	pprint(str);
+    }
 
     return changed;
 }
@@ -106,20 +195,47 @@
 }
 
 var tests = [
+
+    { name: 'regexp-dna.js',
+      actual: function() {
+	  return dnaOutputString + dnaInput;
+      },      
+      expected: function() {
+	  return expectedDNAOutputString + expectedDNAInput;
+      },
+    },      
+
     { name: 'string-base64.js',
       actual: function() {
           return hash(str);
       },
       expected: function() {
           return 1544571068;
-      }
-    },
-    { name: 'string-validate-input.js',
-      actual: function() {
-          return hash(endResult);
       },
-      expected: function() {
-          return 2016572373;
+      times: rtimes,
+      rerun: function() {
+	  toBinaryTable = [
+		  -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+		  -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+		  -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
+	          52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1,
+		  -1, 0, 1, 2,  3, 4, 5, 6,  7, 8, 9,10, 11,12,13,14,
+	           15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
+		  -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
+	          41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
+	  ];   
+	  var str = "";	  
+	  for (var i = 0; i < 8192; i++)
+              str += String.fromCharCode((25 * Math.random()) + 97);
+	  
+	  for (var i = 8192; i <= 16384; i *= 2) {	      
+	      var base64;
+	      base64 = toBase64(str);
+	      var encoded = base64ToString(base64);
+
+	      str += str;
+	  }
+	  toBinaryTable = null;
       }
     },
     { name: 'date-format-xparb.js',
@@ -128,7 +244,29 @@
       },
       expected: function() {
           return "2017-09-05Tuesday, September 05, 2017 8:43:48 AM";
+      },
+      times: rtimes,
+      rerun: function() {
+	  date = new Date("1/1/2007 1:11:11");	  
+	  for (i = 0; i < 4000; ++i) {
+	      var shortFormat = date.dateFormat("Y-m-d");
+	      var longFormat = date.dateFormat("l, F d, Y g:i:s A");
+	      date.setTime(date.getTime() + 84266956);
+	  }
       }
+	  
+    },
+    { name: 'string-validate-input.js',
+      actual: function() {
+          return hash(endResult);
+      },
+      expected: function() {
+          return 726038055;
+      },
+      times: rtimes,
+      rerun: function() {
+	  doTest();
+      },	 
     },
     { name: '3d-morph.js',
       actual: function() {
@@ -137,6 +275,20 @@
       },
       expected: function() {
           return true;
+      },
+      times: rtimes,
+      rerun: function() {	  
+	  a = Array()
+	  for (var i=0; i < nx*nz*3; ++i) 
+	      a[i] = 0
+	  for (var i = 0; i < loops; ++i) {
+	      morph(a, i/loops)
+	  }
+	  testOutput = 0;
+	  for (var i = 0; i < nx; i++)
+	      testOutput += a[3*(i*nx+i)+1];
+	  a = null;
+	  
       }
     },
     { name: 'crypto-aes.js',
@@ -145,6 +297,12 @@
       },
       expected: function() {
           return decryptedText;
+      },
+      times: rtimes,
+      rerun: function() {
+	  cipherText = AESEncryptCtr(plainText, password, 256);
+	  decryptedText = AESDecryptCtr(cipherText, password, 256);
+	  
       }
     },
     { name: 'crypto-md5.js',
@@ -153,32 +311,56 @@
       },
       expected: function() {
           return "a831e91e0f70eddcb70dc61c6f82f6cd";
+      },
+      times: rtimes,
+      rerun: function() {
+	  md5Output = hex_md5(plainText);
       }
     },
+    
     { name: 'crypto-sha1.js',
       actual: function() {
           return sha1Output;
       },
       expected: function() {
           return "2524d264def74cce2498bf112bedf00e6c0b796d";
+      },
+      times: rtimes,
+      rerun: function() {
+	  sha1Output = hex_sha1(plainText);	  
       }
     },
+
     { name: 'bitops-bitwise-and.js',
       actual: function() {
           return result;
       },
       expected: function() {
           return 0;
+      },
+      times: rtimes,
+      rerun: function() {
+	  bitwiseAndValue = 4294967296;
+	  for (var i = 0; i < 600000; i++) {
+	      bitwiseAndValue = bitwiseAndValue & i;	  
+	  }
+	  result = bitwiseAndValue;
       }
     },
+
     { name: 'bitops-bits-in-byte.js',
       actual: function() {
           return result;
       },
       expected: function() {
           return 358400;
+      },
+      times: rtimes,
+      rerun: function() {
+	  result = TimeFunc(bitsinbyte);
       }
     },
+
     { name: 'bitops-nsieve-bits.js',
       actual: function() {
           var ret = 0;
@@ -190,40 +372,101 @@
       },
       expected: function() {
           return -1286749539853;
+      },
+      times: rtimes,
+      rerun: function() {
+	  result = sieve();
       }
     },
+
     { name: 'bitops-3bit-bits-in-byte.js',
       actual: function() {
           return sum;
       },
       expected: function() {
           return 512000;
+      },
+      times: rtimes,
+      rerun: function() {
+	  sum = TimeFunc(fast3bitlookup);	 
       }
     },
+
     { name: 'access-nbody.js',
       actual: function() {
           return ret;
       },
       expected: function() {
             return -1.3524862408537381;
+      },
+      times: rtimes,
+      rerun: function() {
+	  var ret = 0;	  
+	  for (var n = 3; n <= 24; n *= 2) {
+	      (function(){
+		  var bodies = new NBodySystem( Array(
+		      Sun(),Jupiter(),Saturn(),Uranus(),Neptune()
+		  ));
+		  var max = n * 100;
+		  
+		  ret += bodies.energy();
+		  for (var i=0; i<max; i++){
+		      bodies.advance(0.01);
+		  }
+		  ret += bodies.energy();
+	      })();
+	  }
       }
     },
+
     { name: 'access-binary-trees.js',
       actual: function() {
           return ret;
       },
       expected: function() {
           return -4;
+      },
+      times: rtimes,
+      rerun: function() {
+	  ret = 0;
+	  
+	  for (var n = 4; n <= 7; n += 1) {
+	      var minDepth = 4;
+	      var maxDepth = Math.max(minDepth + 2, n);
+	      var stretchDepth = maxDepth + 1;
+	      
+	      var check = bottomUpTree(0,stretchDepth).itemCheck();
+	      
+	      var longLivedTree = bottomUpTree(0,maxDepth);
+	      for (var depth=minDepth; depth<=maxDepth; depth+=2){
+		  var iterations = 1 << (maxDepth - depth + minDepth);
+		  
+		  check = 0;
+		  for (var i=1; i<=iterations; i++){
+		      check += bottomUpTree(i,depth).itemCheck();
+		      check += bottomUpTree(-i,depth).itemCheck();
+		  }
+	      }
+	      
+	      ret += longLivedTree.itemCheck();
+	  }
       }
     },
+
     { name: 'access-fannkuch.js',
       actual: function() {
           return ret;
       },
       expected: function() {
           return 22;
+      },
+      times: rtimes,
+      rerun: function() {
+	  n = 8;
+	  ret = fannkuch(n);	  
       }
     },
+
     { name: 'math-spectral-norm.js',
       actual: function() {
           var ret = '';
@@ -234,32 +477,43 @@
       },
       expected: function() {
           return "1.2657786149754053,1.2727355112619148,1.273989979775574,1.274190125290389,";
+      },
+      times: rtimes,
+      rerun: function() {
+	  total = 0;	  
+	  for (var i = 6; i <= 48; i *= 2) {
+	      total += spectralnorm(i);
+	  }
       }
     },
+
     { name: '3d-raytrace.js',
       actual: function() {
           return hash(testOutput);
       },
       expected: function() {
           return 230692593;
+      },
+      times: rtimes,
+      rerun: function() {
+	  testOutput = arrayToCanvasCommands(raytraceScene());	  
       }
     },
-    { name: 'regexp-dna.js',
-      actual: function() {
-          return dnaOutputString;
-      },
-      expected: function() {
-          return "agggtaaa|tttaccct 0\n[cgt]gggtaaa|tttaccc[acg] 9\na[act]ggtaaa|tttacc[agt]t 27\nag[act]gtaaa|tttac[agt]ct 24\nagg[act]taaa|ttta[agt]cct 30\naggg[acg]aaa|ttt[cgt]ccct 9\nagggt[cgt]aa|tt[acg]accct 12\nagggta[cgt]a|t[acg]taccct 9\nagggtaa[cgt]|[acg]ttaccct 15\n";
-      }
-    },
+
     { name: 'math-cordic.js',
       actual: function() {
           return total;
       },
       expected: function() {
           return 10362.570468755888;
+      },
+      times: rtimes,
+      rerun: function() {
+	  total = 0;
+	  cordic(25000);
       }
     },
+
     { name: 'controlflow-recursive.js',
       actual: function() {
           var ret = 0;
@@ -272,16 +526,36 @@
       },
       expected: function() {
           return 57775;
+      },
+      times: rtimes,
+      rerun: function() {
+	  result = 0;	  
+	  for (var i = 3; i <= 5; i++) {
+	      result += ack(3,i);
+	      result += fib(17.0+i);
+	      result += tak(3*i+3,2*i+2,i+1);
+	  }
       }
     },
+
     { name: 'date-format-tofte.js',
       actual: function() {
           return shortFormat + longFormat;
       },
       expected: function() {
           return "2008-05-01Thursday, May 01, 2008 6:31:22 PM";
+      },
+      times: rtimes,
+      rerun: function() {
+	  date = new Date("1/1/2007 1:11:11");	  
+	  for (i = 0; i < 500; ++i) {
+	      var shortFormat = date.formatDate("Y-m-d");
+	      var longFormat = date.formatDate("l, F d, Y g:i:s A");
+	      date.setTime(date.getTime() + 84266956);
+	  }
       }
     },
+
     { name: 'string-tagcloud.js',
       actual: function() {
           // The result string embeds floating-point numbers, which can vary a bit on different platforms,
@@ -291,8 +565,71 @@
       },
       expected: function() {
           return 295906;
+      },
+      times: rtimes,
+      rerun: function() {	  
+	  tagInfo = tagInfoJSON.parseJSON(function(a, b) { if (a == "popularity") { return Math.log(b) / log2; } else {return b; } });
+	  tagcloud = makeTagCloud(tagInfo);
+      }
+    },
+
+    { name: 'math-partial-sums.js',
+      actual: function() {
+	  return total;
+      },
+      expected: function() {
+	  return 60.08994194659945;
+      },
+      times: rtimes,
+      rerun: function() {
+	  total = 0;
+	  for (var i = 1024; i <= 16384; i *= 2) {
+	      total += partial(i);
+	  }
       }
     },
+
+    { name: 'access-nsieve.js',
+      actual: function() {
+	  return result;
+      },
+      expected: function() {
+	  return 14302;
+      },
+      times: rtimes,
+      rerun: function() {
+	  result = sieve();
+      }      
+    },
+
+    { name: '3d-cube.js',
+      times: rtimes,
+      rerun: function() {
+	  Q = new Array();
+	  MTrans = new Array();  // transformation matrix
+	  MQube = new Array();  // position information of qube
+	  I = new Array();      // entity matrix
+	  Origin = new Object();
+	  Testing = new Object();	  
+	  for ( var i = 20; i <= 160; i *= 2 ) {
+	      Init(i);
+	  }
+      }
+    },
+
+    //TODO no easy way to sanity check result
+    { name: 'string-fasta.js',
+      times: rtimes,
+      rerun: function() {
+	  ret = 0;
+	  count = 7;
+	  fastaRepeat(2*count*100000, ALU);
+	  fastaRandom(3*count*1000, IUB);
+	  fastaRandom(5*count*1000, HomoSap);
+      }
+    },      
+
+    //TODO no easy way to sanity check result
     { name: 'string-unpack-code.js',
       actual: function() {
           return decompressedMochiKit.length == 106415 &&
@@ -301,43 +638,32 @@
               decompressedMochiKit[82556] == '>';
       },
       expected: function() {
-          return true;
-      }
+	  return true;
+      },
     },
-    //TODO no easy way to sanity check result
-    { name: 'string-fasta.js' },
-    //TODO no easy way to sanity check result
-    { name: 'math-partial-sums.js' },
-    //TODO no easy way to sanity check result
-    { name: 'access-nsieve.js' },
-    //TODO no easy way to sanity check result
-    { name: '3d-cube.js' },
+
 ];
 
+tests.sort(function(a,b) { return a.name.localeCompare(b.name); });
+if (typeof single !== 'undefined') {
+    for (i in tests) {
+	if (tests[i].name === single) {
+	    singleTest = tests[i];
+	    tests = [singleTest];
+	    break;
+	}
+    }
+    if (tests.length != 1) {
+	throw "unknown single test '" + single + "'";
+    }
+}
+
+
 // handle the case this script may be run by a JS engine that doesn't
 // support __DIR__ global variable.
-var dir = (typeof(__DIR__) == 'undefined') ? "test/script/basic/" : __DIR__;
-
-var verbose_run = false;
-
-var args = [];
-if (typeof $ARGS !== 'undefined') {
-    args = $ARGS;
-} else if (typeof arguments !== 'undefined' && arguments.length != 0) {
-    args = arguments;
-}
-
-for (i in args) {
-    if (args[i] === '--verbose') {
-        verbose_run = true;
-        break;
-    }
-}
 
 runsuite(tests);
 
-if (verbose_run) {
-    print('\n' + runs + "/" + tests.length + " tests were successfully run in " + total_time + " ms ");
-}
+pprint('\n' + runs + "/" + tests.length + " tests were successfully run in " + total_time + " ms ");
 
 print("Sunspider finished!");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/typedarrays2.js	Wed Feb 26 13:17:57 2014 +0100
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ * 
+ * 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.
+ */
+
+/**
+ * Typed array test 2
+ *
+ * @test
+ * @run
+ */
+
+var b = new ArrayBuffer(8);
+
+var v1 = new Int32Array(b);
+var v2 = new Uint8Array(b, 2);
+var v3 = new Int16Array(b, 2, 2);
+
+v3[1000] = 17;
+v3.apa = 17;
+print(v3[1000]);
+var arr = [1,2,3];
+arr.gegg = 17;
+print(arr.gegg);
+var v4 = new Int32Array(arr);
+print(v4.gegg);
+
+v2[0] = 0xff;
+v2[1] = 0xff;
+if (v2[1] != 0xff) {
+    print("Error 1: " + v2[1]);
+}
+if (v3[0] != -1) {
+    print("Error 2: " + v3[0]);
+}
+
+print("Done");
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/typedarrays2.js.EXPECTED	Wed Feb 26 13:17:57 2014 +0100
@@ -0,0 +1,4 @@
+undefined
+17
+undefined
+Done
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/currently-failing/property_delete.js	Wed Feb 26 13:17:57 2014 +0100
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2014 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.
+ * 
+ * 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.
+ */
+
+/**
+ * @test
+ * @option -Dnashorn.debug=true
+ * @fork
+ */
+
+load(__DIR__ + "maputil.js");
+
+function Foo() {
+    this.x = 33;
+}
+
+var obj1 = new Foo();
+var obj2 = new Foo();
+
+assertSameMap(obj1, obj2);
+
+// property deletion at same callsite
+function deleteX(obj) {
+   delete obj.x;
+}
+deleteX(obj1);
+deleteX(obj2);
+
+assertSameMap(obj1, obj2);
--- a/nashorn/test/script/maptests/constructor.js	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/test/script/maptests/constructor.js	Wed Feb 26 13:17:57 2014 +0100
@@ -33,4 +33,4 @@
 // These objects should share the map
 assertSameMap(new Point(2, 3), new Point(43, 23));
 assertSameMap(new Point(), new Point());
-assertSameMap(new Point(), new Point(3, 1));
+assertEqualWithoutTypeMap(new Point(), new Point(3, 1));
--- a/nashorn/test/script/maptests/maputil.js	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/test/script/maptests/maputil.js	Wed Feb 26 13:17:57 2014 +0100
@@ -27,7 +27,7 @@
 
 function assertSameMap(obj1, obj2, msg) {
     if (! Debug.identical(Debug.map(obj1), Debug.map(obj2))) {
-        fail(obj1.constructor + " instances don't share map");
+        fail(obj1.constructor + " instances don't share map " + Debug.diffPropertyMaps(Debug.map(obj1), Debug.map(obj2)));
     }
 }
 
@@ -36,3 +36,9 @@
         fail(obj1.constructor + " and " + obj2.constructor + " instances share map");
     }
 }
+
+function assertEqualWithoutTypeMap(obj1, obj2, msg) {
+    if (!Debug.equalWithoutType(Debug.map(obj1), Debug.map(obj2))) {
+        fail(obj1.constructor + " and " + obj2.constructor + " instances don't have identical (without considering property types) maps");
+    }
+}
--- a/nashorn/test/script/maptests/object_literals.js	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/test/script/maptests/object_literals.js	Wed Feb 26 13:17:57 2014 +0100
@@ -42,5 +42,5 @@
 
 // Object literals created at different callsites
 assertSameMap({}, {});
-assertSameMap({foo: 4}, {foo: 'hello'});
-assertSameMap({foo: 34, bar: 'fdgd'}, {foo: 'world', bar: 54});
+assertEqualWithoutTypeMap({foo: 4}, {foo: 'hello'});
+assertEqualWithoutTypeMap({foo: 34, bar: 'fdgd'}, {foo: 'world', bar: 54});
--- a/nashorn/test/script/maptests/point.js	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/test/script/maptests/point.js	Wed Feb 26 13:17:57 2014 +0100
@@ -46,4 +46,4 @@
 
 assertSameMap(new Point(2, 3), new Point(43, 23));
 assertSameMap(new Point(), new Point());
-assertSameMap(new Point(), new Point(3, 1));
+assertEqualWithoutTypeMap(new Point(), new Point(3, 1));
--- a/nashorn/test/script/maptests/property_add.js	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/test/script/maptests/property_add.js	Wed Feb 26 13:17:57 2014 +0100
@@ -43,4 +43,4 @@
 addX(obj1, 3);
 addX(obj2, 'hello');
 
-assertSameMap(obj1, obj2);
+assertEqualWithoutTypeMap(obj1, obj2);
--- a/nashorn/test/script/maptests/property_delete.js	Tue Feb 25 18:56:10 2014 +0530
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
-/*
- * Copyright (c) 2014 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.
- * 
- * 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.
- */
-
-/**
- * @test
- * @option -Dnashorn.debug=true
- * @fork
- */
-
-load(__DIR__ + "maputil.js");
-
-function Foo() {
-    this.x = 33;
-}
-
-var obj1 = new Foo();
-var obj2 = new Foo();
-
-assertSameMap(obj1, obj2);
-
-// property deletion at same callsite
-function deleteX(obj) {
-   delete obj.x;
-}
-deleteX(obj1);
-deleteX(obj2);
-
-assertSameMap(obj1, obj2);
--- a/nashorn/test/script/trusted/JDK-8006529.js	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/test/script/trusted/JDK-8006529.js	Wed Feb 26 13:17:57 2014 +0100
@@ -61,13 +61,13 @@
 var getStatementsMethod = Block.class.getMethod("getStatements");
 var getInitMethod = VarNode.class.getMethod("getInit");
 var getExpressionMethod = ExpressionStatement.class.getMethod("getExpression")
-var rhsMethod = UnaryNode.class.getMethod("rhs")
+var rhsMethod = UnaryNode.class.getMethod("getExpression")
 var lhsMethod = BinaryNode.class.getMethod("lhs")
 var binaryRhsMethod = BinaryNode.class.getMethod("rhs")
 var debugIdMethod = Debug.class.getMethod("id", java.lang.Object.class)
 
 // These are method names of methods in FunctionNode class
-var allAssertionList = ['isVarArg', 'needsParentScope', 'needsCallee', 'hasScopeBlock', 'needsSelfSymbol', 'isSplit', 'hasEval', 'allVarsInScope', 'isStrict']
+var allAssertionList = ['isVarArg', 'needsParentScope', 'needsCallee', 'hasScopeBlock', 'usesSelfSymbol', 'isSplit', 'hasEval', 'allVarsInScope', 'isStrict']
 
 // corresponding Method objects of FunctionNode class
 var functionNodeMethods = {};
@@ -203,7 +203,7 @@
 
 // A self-referencing function defined as an expression needs a self symbol,
 // as it can't obtain itself from the parent scope.
-testFirstFn("(function f() { f() })", 'needsCallee', 'needsSelfSymbol')
+testFirstFn("(function f() { f() })", 'needsCallee', 'usesSelfSymbol')
 
 // A child function accessing parent's variable triggers the need for scope
 // in parent
@@ -239,11 +239,11 @@
 
 // Using "eval" triggers pretty much everything. The function even needs to be
 // vararg, 'cause we don't know if eval will be using "arguments".
-testFirstFn("(function f() { eval() })", 'needsParentScope', 'needsCallee', 'hasScopeBlock', 'hasEval', 'isVarArg', 'allVarsInScope')
+testFirstFn("(function f() { eval() })", 'usesSelfSymbol', 'needsParentScope', 'needsCallee', 'hasScopeBlock', 'hasEval', 'isVarArg', 'allVarsInScope')
 
 // Nested function using "eval" is almost the same as parent function using
 // eval, but at least the parent doesn't have to be vararg.
-testFirstFn("(function f() { function g() { eval() } })", 'needsParentScope', 'needsCallee', 'hasScopeBlock', 'allVarsInScope')
+testFirstFn("(function f() { function g() { eval() } })", 'usesSelfSymbol', 'needsParentScope', 'needsCallee', 'hasScopeBlock', 'allVarsInScope')
 
 // Function with 250 named parameters is ordinary
 testFirstFn("function f(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, p30, p31, p32, p33, p34, p35, p36, p37, p38, p39, p40, p41, p42, p43, p44, p45, p46, p47, p48, p49, p50, p51, p52, p53, p54, p55, p56, p57, p58, p59, p60, p61, p62, p63, p64, p65, p66, p67, p68, p69, p70, p71, p72, p73, p74, p75, p76, p77, p78, p79, p80, p81, p82, p83, p84, p85, p86, p87, p88, p89, p90, p91, p92, p93, p94, p95, p96, p97, p98, p99, p100, p101, p102, p103, p104, p105, p106, p107, p108, p109, p110, p111, p112, p113, p114, p115, p116, p117, p118, p119, p120, p121, p122, p123, p124, p125, p126, p127, p128, p129, p130, p131, p132, p133, p134, p135, p136, p137, p138, p139, p140, p141, p142, p143, p144, p145, p146, p147, p148, p149, p150, p151, p152, p153, p154, p155, p156, p157, p158, p159, p160, p161, p162, p163, p164, p165, p166, p167, p168, p169, p170, p171, p172, p173, p174, p175, p176, p177, p178, p179, p180, p181, p182, p183, p184, p185, p186, p187, p188, p189, p190, p191, p192, p193, p194, p195, p196, p197, p198, p199, p200, p201, p202, p203, p204, p205, p206, p207, p208, p209, p210, p211, p212, p213, p214, p215, p216, p217, p218, p219, p220, p221, p222, p223, p224, p225, p226, p227, p228, p229, p230, p231, p232, p233, p234, p235, p236, p237, p238, p239, p240, p241, p242, p243, p244, p245, p246, p247, p248, p249, p250) { p250 = p249 }")
--- a/nashorn/test/src/jdk/nashorn/api/javaaccess/NumberAccessTest.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/test/src/jdk/nashorn/api/javaaccess/NumberAccessTest.java	Wed Feb 26 13:17:57 2014 +0100
@@ -32,6 +32,7 @@
 import javax.script.ScriptEngine;
 import javax.script.ScriptEngineManager;
 import javax.script.ScriptException;
+import jdk.nashorn.api.scripting.NashornScriptEngine;
 import org.testng.TestNG;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
@@ -44,8 +45,9 @@
  */
 public class NumberAccessTest {
 
-    private static ScriptEngine e = null;
-    private static SharedObject o = null;
+    private static ScriptEngine e;
+    private static SharedObject o;
+    private static boolean optimistic;
 
     public static void main(final String[] args) {
         TestNG.main(args);
@@ -55,6 +57,7 @@
     public static void setUpClass() throws ScriptException {
         final ScriptEngineManager m = new ScriptEngineManager();
         e = m.getEngineByName("nashorn");
+        optimistic = ((NashornScriptEngine)e).isOptimistic();
         o = new SharedObject();
         e.put("o", o);
         e.eval("var SharedObject = Packages.jdk.nashorn.api.javaaccess.SharedObject;");
@@ -264,7 +267,11 @@
     @Test
     public void accessFieldByte() throws ScriptException {
         e.eval("var p_byte = o.publicByte;");
-        assertEquals(o.publicByte, e.get("p_byte"));
+        if(optimistic) {
+            assertEquals((int)o.publicByte, e.get("p_byte"));
+        } else {
+            assertEquals(o.publicByte, e.get("p_byte"));
+        }
         e.eval("o.publicByte = 16;");
         assertEquals(16, o.publicByte);
     }
@@ -287,7 +294,11 @@
     @Test
     public void accessStaticFieldByte() throws ScriptException {
         e.eval("var ps_byte = SharedObject.publicStaticByte;");
-        assertEquals(SharedObject.publicStaticByte, e.get("ps_byte"));
+        if(optimistic) {
+            assertEquals((int)SharedObject.publicStaticByte, e.get("ps_byte"));
+        } else {
+            assertEquals(SharedObject.publicStaticByte, e.get("ps_byte"));
+        }
         e.eval("SharedObject.publicStaticByte = 16;");
         assertEquals(16, SharedObject.publicStaticByte);
     }
@@ -310,7 +321,11 @@
     @Test
     public void accessFinalFieldByte() throws ScriptException {
         e.eval("var pf_byte = o.publicFinalByte;");
-        assertEquals(o.publicFinalByte, e.get("pf_byte"));
+        if(optimistic) {
+            assertEquals((int)o.publicFinalByte, e.get("pf_byte"));
+        } else {
+            assertEquals(o.publicFinalByte, e.get("pf_byte"));
+        }
         e.eval("o.publicFinalByte = 16;");
         assertEquals(-7, o.publicFinalByte);
     }
@@ -333,7 +348,11 @@
     @Test
     public void accessStaticFinalFieldByte() throws ScriptException {
         e.eval("var psf_byte = SharedObject.publicStaticFinalByte;");
-        assertEquals(SharedObject.publicStaticFinalByte, e.get("psf_byte"));
+        if(optimistic) {
+            assertEquals((int)SharedObject.publicStaticFinalByte, e.get("psf_byte"));
+        } else {
+            assertEquals(SharedObject.publicStaticFinalByte, e.get("psf_byte"));
+        }
         e.eval("SharedObject.publicStaticFinalByte = 16;");
         assertEquals(-70, SharedObject.publicStaticFinalByte);
     }
@@ -358,7 +377,11 @@
     @Test
     public void accessFieldShort() throws ScriptException {
         e.eval("var p_short = o.publicShort;");
-        assertEquals(o.publicShort, e.get("p_short"));
+        if(optimistic) {
+            assertEquals((int)o.publicShort, e.get("p_short"));
+        } else {
+            assertEquals(o.publicShort, e.get("p_short"));
+        }
         e.eval("o.publicShort = 18;");
         assertEquals(18, o.publicShort);
     }
@@ -381,7 +404,11 @@
     @Test
     public void accessStaticFieldShort() throws ScriptException {
         e.eval("var ps_short = SharedObject.publicStaticShort;");
-        assertEquals(SharedObject.publicStaticShort, e.get("ps_short"));
+        if(optimistic) {
+            assertEquals((int)SharedObject.publicStaticShort, e.get("ps_short"));
+        } else {
+            assertEquals(SharedObject.publicStaticShort, e.get("ps_short"));
+        }
         e.eval("SharedObject.publicStaticShort = 180;");
         assertEquals(180, SharedObject.publicStaticShort);
     }
@@ -404,7 +431,11 @@
     @Test
     public void accessFinalFieldShort() throws ScriptException {
         e.eval("var pf_short = o.publicFinalShort;");
-        assertEquals(o.publicFinalShort, e.get("pf_short"));
+        if(optimistic) {
+            assertEquals((int)o.publicFinalShort, e.get("pf_short"));
+        } else {
+            assertEquals(o.publicFinalByte, e.get("pf_byte"));
+        }
         e.eval("o.publicFinalShort = 180;");
         assertEquals(31220, o.publicFinalShort);
     }
@@ -427,7 +458,11 @@
     @Test
     public void accessStaticFinalFieldShort() throws ScriptException {
         e.eval("var psf_short = SharedObject.publicStaticFinalShort;");
-        assertEquals(SharedObject.publicStaticFinalShort, e.get("psf_short"));
+        if(optimistic) {
+            assertEquals((int)SharedObject.publicStaticFinalShort, e.get("psf_short"));
+        } else {
+            assertEquals(SharedObject.publicStaticFinalShort, e.get("psf_short"));
+        }
         e.eval("SharedObject.publicStaticFinalShort = 180;");
         assertEquals(8888, SharedObject.publicStaticFinalShort);
     }
@@ -555,7 +590,11 @@
     @Test
     public void accessFieldFloat() throws ScriptException {
         e.eval("var p_float = o.publicFloat;");
-        assertEquals(o.publicFloat, e.get("p_float"));
+        if(optimistic) {
+            assertEquals((double)o.publicFloat, e.get("p_float"));
+        } else {
+            assertEquals(o.publicFloat, e.get("p_float"));
+        }
         o.publicFloat = 0.0f / 0.0f;
         assertEquals(true, e.eval("isNaN(o.publicFloat)"));
         o.publicFloat = 1.0f / 0.0f;
@@ -590,7 +629,12 @@
     @Test
     public void accessStaticFieldFloat() throws ScriptException {
         e.eval("var ps_float = SharedObject.publicStaticFloat;");
-        assertEquals(SharedObject.publicStaticFloat, e.get("ps_float"));
+        if(optimistic) {
+            assertTrue((int)SharedObject.publicStaticFloat == SharedObject.publicStaticFloat);
+            assertEquals((int)SharedObject.publicStaticFloat, e.get("ps_float"));
+        } else {
+            assertEquals(SharedObject.publicStaticFloat, e.get("ps_float"));
+        }
         SharedObject.publicStaticFloat = 0.0f / 0.0f;
         assertEquals(true, e.eval("isNaN(SharedObject.publicStaticFloat)"));
         SharedObject.publicStaticFloat = 1.0f / 0.0f;
@@ -625,7 +669,12 @@
     @Test
     public void accessFinalFloat() throws ScriptException {
         e.eval("var pf_float = o.publicFinalFloat;");
-        assertEquals(o.publicFinalFloat, e.get("pf_float"));
+        if(optimistic) {
+            assertTrue((int)o.publicFinalFloat == o.publicFinalFloat);
+            assertEquals((int)o.publicFinalFloat, e.get("pf_float"));
+        } else {
+            assertEquals(o.publicFinalFloat, e.get("pf_float"));
+        }
         e.eval("o.publicFinalFloat = 20.0;");
         assertEquals(7.72e8f, o.publicFinalFloat, 1e-10);
     }
@@ -648,7 +697,12 @@
     @Test
     public void accessStaticFinalFieldFloat() throws ScriptException {
         e.eval("var psf_float = SharedObject.publicStaticFinalFloat;");
-        assertEquals(SharedObject.publicStaticFinalFloat, e.get("psf_float"));
+        if(optimistic) {
+            assertTrue(((int)SharedObject.publicStaticFinalFloat) == SharedObject.publicStaticFinalFloat);
+            assertEquals((int)SharedObject.publicStaticFinalFloat, e.get("psf_float"));
+        } else {
+            assertEquals(SharedObject.publicStaticFinalFloat, e.get("psf_float"));
+        }
         e.eval("SharedObject.publicStaticFinalFloat = 20.0;");
         assertEquals(0.72e8f, SharedObject.publicStaticFinalFloat, 1e-10);
     }
--- a/nashorn/test/src/jdk/nashorn/api/javaaccess/NumberBoxingTest.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/test/src/jdk/nashorn/api/javaaccess/NumberBoxingTest.java	Wed Feb 26 13:17:57 2014 +0100
@@ -31,6 +31,7 @@
 import javax.script.ScriptEngine;
 import javax.script.ScriptEngineManager;
 import javax.script.ScriptException;
+import jdk.nashorn.api.scripting.NashornScriptEngine;
 import org.testng.TestNG;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
@@ -43,8 +44,9 @@
  */
 public class NumberBoxingTest {
 
-    private static ScriptEngine e = null;
-    private static SharedObject o = null;
+    private static ScriptEngine e;
+    private static SharedObject o;
+    private static boolean optimistic;
 
     public static void main(final String[] args) {
         TestNG.main(args);
@@ -54,6 +56,7 @@
     public static void setUpClass() throws ScriptException {
         final ScriptEngineManager m = new ScriptEngineManager();
         e = m.getEngineByName("nashorn");
+        optimistic = ((NashornScriptEngine)e).isOptimistic();
         o = new SharedObject();
         e.put("o", o);
         e.eval("var SharedObject = Packages.jdk.nashorn.api.javaaccess.SharedObject;");
@@ -77,10 +80,10 @@
 
     @Test
     public void accessStaticFieldLongBoxing() throws ScriptException {
-        e.eval("var ps_long = SharedObject.publicStaticLong;");
-        assertEquals(SharedObject.publicStaticLong, e.get("ps_long"));
-        e.eval("SharedObject.publicStaticLong = 120;");
-        assertEquals(120, SharedObject.publicStaticLong);
+        e.eval("var ps_long = SharedObject.publicStaticLongBox;");
+        assertEquals(SharedObject.publicStaticLongBox, e.get("ps_long"));
+        e.eval("SharedObject.publicStaticLongBox = 120;");
+        assertEquals(120L, SharedObject.publicStaticLongBox.longValue());
     }
 
     @Test
@@ -138,7 +141,7 @@
     @Test
     public void accessFieldByteBoxing() throws ScriptException {
         e.eval("var p_byte = o.publicByteBox;");
-        assertEquals(o.publicByteBox, e.get("p_byte"));
+        assertEqualsInt(o.publicByteBox, "p_byte");
         e.eval("o.publicByteBox = 16;");
         assertEquals(Byte.valueOf((byte)16), o.publicByteBox);
     }
@@ -146,7 +149,7 @@
     @Test
     public void accessStaticFieldByteBoxing() throws ScriptException {
         e.eval("var ps_byte = SharedObject.publicStaticByte;");
-        assertEquals(SharedObject.publicStaticByte, e.get("ps_byte"));
+        assertEqualsInt(SharedObject.publicStaticByte, "ps_byte");
         e.eval("SharedObject.publicStaticByte = 16;");
         assertEquals(16, SharedObject.publicStaticByte);
     }
@@ -154,7 +157,7 @@
     @Test
     public void accessFinalFieldByteBoxing() throws ScriptException {
         e.eval("var pf_byte = o.publicFinalByteBox;");
-        assertEquals(o.publicFinalByteBox, e.get("pf_byte"));
+        assertEqualsInt(o.publicFinalByteBox, "pf_byte");
         e.eval("o.publicFinalByteBox = 16;");
         assertEquals(Byte.valueOf((byte)19), o.publicFinalByteBox);
     }
@@ -162,7 +165,7 @@
     @Test
     public void accessStaticFinalFieldByteBoxing() throws ScriptException {
         e.eval("var psf_byte = SharedObject.publicStaticFinalByte;");
-        assertEquals(SharedObject.publicStaticFinalByte, e.get("psf_byte"));
+        assertEqualsInt(SharedObject.publicStaticFinalByte, "psf_byte");
         e.eval("SharedObject.publicStaticFinalByte = 16;");
         assertEquals(-70, SharedObject.publicStaticFinalByte);
     }
@@ -172,15 +175,23 @@
     @Test
     public void accessFieldShortBoxing() throws ScriptException {
         e.eval("var p_short = o.publicShortBox;");
-        assertEquals(o.publicShortBox, e.get("p_short"));
+        assertEqualsInt(o.publicShortBox, "p_short");
         e.eval("o.publicShortBox = 18;");
         assertEquals(Short.valueOf((short)18), o.publicShortBox);
     }
 
+    private static void assertEqualsInt(Number n, String name) {
+        if(optimistic) {
+            assertEquals(n.intValue(), e.get(name));
+        } else {
+            assertEquals(n, e.get(name));
+        }
+    }
+
     @Test
     public void accessStaticFieldShortBoxing() throws ScriptException {
         e.eval("var ps_short = SharedObject.publicStaticShort;");
-        assertEquals(SharedObject.publicStaticShort, e.get("ps_short"));
+        assertEqualsInt(SharedObject.publicStaticShort, "ps_short");
         e.eval("SharedObject.publicStaticShort = 180;");
         assertEquals(180, SharedObject.publicStaticShort);
     }
@@ -188,7 +199,7 @@
     @Test
     public void accessFinalFieldShortBoxing() throws ScriptException {
         e.eval("var pf_short = o.publicFinalShortBox;");
-        assertEquals(o.publicFinalShortBox, e.get("pf_short"));
+        assertEqualsInt(o.publicFinalShortBox, "pf_short");
         e.eval("o.publicFinalShortBox = 180;");
         assertEquals(Short.valueOf((short)-26777), o.publicFinalShortBox);
     }
@@ -196,7 +207,7 @@
     @Test
     public void accessStaticFinalFieldShortBoxing() throws ScriptException {
         e.eval("var psf_short = SharedObject.publicStaticFinalShort;");
-        assertEquals(SharedObject.publicStaticFinalShort, e.get("psf_short"));
+        assertEqualsInt(SharedObject.publicStaticFinalShort, "psf_short");
         e.eval("SharedObject.publicStaticFinalShort = 180;");
         assertEquals(8888, SharedObject.publicStaticFinalShort);
     }
@@ -247,7 +258,7 @@
     @Test
     public void accessFieldFloatBoxing() throws ScriptException {
         e.eval("var p_float = o.publicFloatBox;");
-        assertEquals(o.publicFloatBox, e.get("p_float"));
+        assertEqualsFloat(o.publicFloatBox, "p_float");
         o.publicFloatBox = 0.0f / 0.0f;
         assertEquals(true, e.eval("isNaN(o.publicFloatBox)"));
         o.publicFloatBox = 1.0f / 0.0f;
@@ -264,10 +275,17 @@
         assertEquals(Float.NEGATIVE_INFINITY, o.publicFloatBox, 1e-10);
     }
 
+    private static void assertEqualsFloat(Float n, String name) {
+        if(optimistic) {
+            assertEquals(n, ((Number)e.get(name)).floatValue());
+        } else {
+            assertEquals(n, e.get(name));
+        }
+    }
     @Test
     public void accessStaticFieldFloatBoxing() throws ScriptException {
         e.eval("var ps_float = SharedObject.publicStaticFloat;");
-        assertEquals(SharedObject.publicStaticFloat, e.get("ps_float"));
+        assertEqualsFloat(SharedObject.publicStaticFloat, "ps_float");
         SharedObject.publicStaticFloat = 0.0f / 0.0f;
         assertEquals(true, e.eval("isNaN(SharedObject.publicStaticFloat)"));
         SharedObject.publicStaticFloat = 1.0f / 0.0f;
@@ -287,7 +305,7 @@
     @Test
     public void accessFinalFloatBoxing() throws ScriptException {
         e.eval("var pf_float = o.publicFinalFloatBox;");
-        assertEquals(o.publicFinalFloatBox, e.get("pf_float"));
+        assertEqualsFloat(o.publicFinalFloatBox, "pf_float");
         e.eval("o.publicFinalFloatBox = 20.0;");
         assertEquals(1.372e4f, o.publicFinalFloatBox, 1e-10);
     }
@@ -295,7 +313,7 @@
     @Test
     public void accessStaticFinalFieldFloatBoxing() throws ScriptException {
         e.eval("var psf_float = SharedObject.publicStaticFinalFloat;");
-        assertEquals(SharedObject.publicStaticFinalFloat, e.get("psf_float"));
+        assertEqualsFloat(SharedObject.publicStaticFinalFloat, "psf_float");
         e.eval("SharedObject.publicStaticFinalFloat = 20.0;");
         assertEquals(0.72e8f, SharedObject.publicStaticFinalFloat, 1e-10);
     }
--- a/nashorn/test/src/jdk/nashorn/api/javaaccess/SharedObject.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/test/src/jdk/nashorn/api/javaaccess/SharedObject.java	Wed Feb 26 13:17:57 2014 +0100
@@ -51,7 +51,7 @@
     public short                  publicShort                   = 32000;
     public short[]                publicShortArray              = { 3240, 8900, -16789, 1, 12 };
     public Short                  publicShortBox                = Short.MIN_VALUE;
-    public float                  publicFloat                   = 0.7e6f;
+    public float                  publicFloat                   = 0.7f;
     public float[]                publicFloatArray              = { -32.01f, 89.3f, -1.3e8f, 3.1f };
     public Float                  publicFloatBox                = 1.377e4f;
     public double                 publicDouble                  = 1.34e20;
--- a/nashorn/test/src/jdk/nashorn/internal/codegen/CompilerTest.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/test/src/jdk/nashorn/internal/codegen/CompilerTest.java	Wed Feb 26 13:17:57 2014 +0100
@@ -179,9 +179,9 @@
             }
         } catch (final Throwable t) {
             log("Compile failed: " + file.getAbsolutePath() + " : " + t);
-            if (VERBOSE) {
+        //            if (VERBOSE) {
                 t.printStackTrace(System.out);
-            }
+        //}
             failed++;
         } finally {
             if (globalChanged) {
--- a/nashorn/test/src/jdk/nashorn/internal/parser/ParserTest.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/test/src/jdk/nashorn/internal/parser/ParserTest.java	Wed Feb 26 13:17:57 2014 +0100
@@ -153,8 +153,8 @@
                 }
             };
             errors.setLimit(0);
-            final Source   source   = new Source(file.getAbsolutePath(), buffer);
-            new Parser(context.getEnv(), source, errors).parse();
+            final Source            source   = new Source(file.getAbsolutePath(), buffer);
+            new Parser(context.getEnv(), source, errors, context.getEnv()._strict).parse();
             if (errors.getNumberOfErrors() > 0) {
                 log("Parse failed: " + file.getAbsolutePath());
                 failed++;
--- a/nashorn/test/src/jdk/nashorn/internal/test/framework/ScriptRunnable.java	Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/test/src/jdk/nashorn/internal/test/framework/ScriptRunnable.java	Wed Feb 26 13:17:57 2014 +0100
@@ -40,10 +40,8 @@
 import java.nio.file.Files;
 import java.nio.file.StandardCopyOption;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
-
 import jdk.nashorn.tools.Shell;
 import org.testng.Assert;
 import org.testng.ITest;
@@ -72,7 +70,11 @@
     @Test
     @Override
     public void runTest() throws IOException {
-        super.runTest();
+        try {
+            super.runTest();
+        } catch(AssertionError e) {
+            throw new AssertionError("Failed executing test " + testFile, e);
+        }
     }
 
     @Override
@@ -175,7 +177,9 @@
         cmd.add(System.getProperty("java.home") + separator + "bin" + separator + "java");
         cmd.add("-Djava.ext.dirs=dist");
         for (String str : forkJVMOptions) {
-            cmd.add(str);
+            if(!str.isEmpty()) {
+                cmd.add(str);
+            }
         }
         cmd.add(Shell.class.getName());
         // now add the rest of the "in process" runtime arguments