8037534: Use scope types to determine optimistic types
authorattila
Mon, 24 Mar 2014 18:41:06 +0100
changeset 24729 2b13051f2122
parent 24728 cb8b23db71ec
child 24730 505fb524e688
8037534: Use scope types to determine optimistic types Reviewed-by: hannesw, lagergren
nashorn/src/jdk/nashorn/internal/codegen/Attr.java
nashorn/src/jdk/nashorn/internal/codegen/ClassEmitter.java
nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java
nashorn/src/jdk/nashorn/internal/codegen/CompilationEnvironment.java
nashorn/src/jdk/nashorn/internal/codegen/ConstantData.java
nashorn/src/jdk/nashorn/internal/runtime/CompiledFunction.java
nashorn/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java
nashorn/src/jdk/nashorn/internal/runtime/RewriteException.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/test/script/currently-failing/OptimisticRecompilationTest.java
--- a/nashorn/src/jdk/nashorn/internal/codegen/Attr.java	Mon Mar 24 18:40:11 2014 +0100
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Attr.java	Mon Mar 24 18:41:06 2014 +0100
@@ -173,6 +173,13 @@
     }
 
     @Override
+    public boolean enterAccessNode(AccessNode accessNode) {
+        tagNeverOptimistic(accessNode.getBase());
+        tagNeverOptimistic(accessNode.getProperty());
+        return true;
+    };
+
+    @Override
     public Node leaveAccessNode(final AccessNode accessNode) {
         return end(ensureSymbolTypeOverride(accessNode, Type.OBJECT));
     }
@@ -355,6 +362,7 @@
         for (final Expression arg : callNode.getArgs()) {
             tagOptimistic(arg);
         }
+        tagNeverOptimistic(callNode.getFunction());
         return true;
     }
 
@@ -669,7 +677,7 @@
         if (!identNode.isInitializedHere()) {
             symbol.increaseUseCount();
         }
-        addLocalUse(identNode.getName());
+        addLocalUse(name);
         IdentNode node = (IdentNode)identNode.setSymbol(lc, symbol);
         if (isTaggedOptimistic(identNode) && symbol.isScope()) {
             node = ensureSymbolTypeOverride(node, symbol.getSymbolType());
@@ -794,6 +802,12 @@
     }
 
     @Override
+    public boolean enterIndexNode(IndexNode indexNode) {
+        tagNeverOptimistic(indexNode.getBase());
+        return true;
+    }
+
+    @Override
     public Node leaveIndexNode(final IndexNode indexNode) {
        // return end(ensureSymbolOptimistic(Type.OBJECT, indexNode));
         return end(ensureSymbolTypeOverride(indexNode, Type.OBJECT));
--- a/nashorn/src/jdk/nashorn/internal/codegen/ClassEmitter.java	Mon Mar 24 18:40:11 2014 +0100
+++ b/nashorn/src/jdk/nashorn/internal/codegen/ClassEmitter.java	Mon Mar 24 18:41:06 2014 +0100
@@ -49,14 +49,13 @@
 import static jdk.nashorn.internal.codegen.CompilerConstants.STRICT_MODE;
 import static jdk.nashorn.internal.codegen.CompilerConstants.className;
 import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor;
-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 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;
@@ -285,7 +284,7 @@
     }
 
     /**
-     * Constructs a primitive specific method for getting the ith entry from the constants table and cast.
+     * Constructs a primitive specific method for getting the ith entry from the constants table as an array.
      * @param clazz Array class.
      */
     private void defineGetArrayMethod(final Class<?> clazz) {
@@ -299,9 +298,8 @@
                       .load(Type.INT, 0)
                       .arrayload()
                       .checkcast(clazz)
-                      .dup()
-                      .arraylength()
-                      .invoke(staticCallNoLookup(Arrays.class, "copyOf", clazz, clazz, int.class))
+                      .invoke(virtualCallNoLookup(clazz, "clone", Object.class))
+                      .checkcast(clazz)
                       ._return();
         getArrayMethod.end();
     }
--- a/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Mon Mar 24 18:40:11 2014 +0100
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Mon Mar 24 18:41:06 2014 +0100
@@ -178,9 +178,9 @@
     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);
+            "<init>", void.class, UnwarrantedOptimismException.class, Object[].class, String[].class, ScriptObject.class);
     private static final Call INIT_REWRITE_EXCEPTION_REST_OF = CompilerConstants.specialCallNoLookup(RewriteException.class,
-            "<init>", void.class, UnwarrantedOptimismException.class, Object[].class, int[].class);
+            "<init>", void.class, UnwarrantedOptimismException.class, Object[].class, String[].class, ScriptObject.class, int[].class);
 
     private static final Call ENSURE_INT = CompilerConstants.staticCallNoLookup(OptimisticReturnFilters.class,
             "ensureInt", int.class, Object.class, int.class);
@@ -1535,7 +1535,7 @@
         try {
             final boolean markOptimistic;
             if (emittedMethods.add(functionNode.getName())) {
-                markOptimistic = generateUnwarrantedOptimismExceptionHandlers();
+                markOptimistic = generateUnwarrantedOptimismExceptionHandlers(functionNode);
                 generateContinuationHandler();
                 method.end(); // wrap up this method
                 unit   = lc.popCompileUnit(functionNode.getCompileUnit());
@@ -4133,7 +4133,7 @@
      * entry to its immediately preceding one for longest matching prefix.
      * @return true if there is at least one exception handler
      */
-    private boolean generateUnwarrantedOptimismExceptionHandlers() {
+    private boolean generateUnwarrantedOptimismExceptionHandlers(final FunctionNode fn) {
         if(!useOptimisticTypes()) {
             return false;
         }
@@ -4274,8 +4274,14 @@
                 method.dup(2);
                 method.dup(2);
                 method.pop();
+                loadConstant(getByteCodeSymbolNames(fn));
+                if (fn.compilerConstant(SCOPE).hasSlot()) {
+                    method.loadCompilerConstant(SCOPE);
+                } else {
+                    method.loadNull();
+                }
                 final CompilationEnvironment env = compiler.getCompilationEnvironment();
-                if(env.isCompileRestOf()) {
+                if (env.isCompileRestOf()) {
                     loadConstant(env.getContinuationEntryPoints());
                     method.invoke(INIT_REWRITE_EXCEPTION_REST_OF);
                 } else {
@@ -4287,6 +4293,26 @@
         return true;
     }
 
+    private static String[] getByteCodeSymbolNames(final FunctionNode fn) {
+        // Only names of local variables on the function level are captured. This information is used to reduce
+        // deoptimizations, so as much as we can capture will help. We rely on the fact that function wide variables are
+        // all live all the time, so the array passed to rewrite exception contains one element for every slotted symbol
+        // here.
+        final List<String> names = new ArrayList<>();
+        for (final Symbol symbol: fn.getBody().getSymbols()) {
+            if (symbol.hasSlot()) {
+                if (symbol.isScope()) {
+                    // slot + scope can only be true for parameters
+                    assert symbol.isParam();
+                    names.add(null);
+                } else {
+                    names.add(symbol.getName());
+                }
+            }
+        }
+        return names.toArray(new String[names.size()]);
+    }
+
     private static String commonPrefix(final String s1, final String s2) {
         final int l1 = s1.length();
         final int l = Math.min(l1, s2.length());
--- a/nashorn/src/jdk/nashorn/internal/codegen/CompilationEnvironment.java	Mon Mar 24 18:40:11 2014 +0100
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CompilationEnvironment.java	Mon Mar 24 18:41:06 2014 +0100
@@ -35,9 +35,17 @@
 import java.util.Map;
 import java.util.Set;
 import jdk.nashorn.internal.codegen.types.Type;
+import jdk.nashorn.internal.ir.AccessNode;
+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.Optimistic;
+import jdk.nashorn.internal.objects.NativeArray;
+import jdk.nashorn.internal.runtime.FindProperty;
+import jdk.nashorn.internal.runtime.Property;
 import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
+import jdk.nashorn.internal.runtime.ScriptObject;
 
 /**
  * Class for managing metadata during a compilation, e.g. which phases
@@ -52,6 +60,10 @@
 
     private RecompilableScriptFunctionData compiledFunction;
 
+    // Runtime scope in effect at the time of the compilation. Used to evaluate types of expressions and prevent overly
+    // optimistic assumptions (which will lead to unnecessary deoptimizing recompilations).
+    private final ScriptObject runtimeScope;
+
     private boolean strict;
 
     private final boolean onDemand;
@@ -169,14 +181,18 @@
     public CompilationEnvironment(
         final CompilationPhases phases,
         final boolean strict) {
-        this(phases, null, null, null, null, strict, false);
+        this(phases, null, null, null, null, null, strict, false);
     }
 
     /**
      * Constructor for compilation environment of the rest-of method
      * @param phases compilation phases
      * @param strict strict mode
-     * @param compiledFunction recompiled function
+     * @param compiledFunction the function being compiled
+     * @param runtimeScope the runtime scope in effect at the time of compilation. It can be used to evaluate types of
+     * scoped variables to guide the optimistic compilation, should the call to this method trigger code compilation.
+     * Can be null if current runtime scope is not known, but that might cause compilation of code that will need more
+     * subsequent deoptimization passes.
      * @param paramTypeMap known parameter types if any exist
      * @param invalidatedProgramPoints map of invalidated program points to their type
      * @param continuationEntryPoint program points used as the continuation entry points in the current rest-of sequence
@@ -186,11 +202,12 @@
         final CompilationPhases phases,
         final boolean strict,
         final RecompilableScriptFunctionData compiledFunction,
+        final ScriptObject runtimeScope,
         final ParamTypeMap paramTypeMap,
         final Map<Integer, Type> invalidatedProgramPoints,
         final int[] continuationEntryPoint,
         final boolean onDemand) {
-            this(phases, paramTypeMap, invalidatedProgramPoints, compiledFunction, continuationEntryPoint, strict, onDemand);
+            this(phases, paramTypeMap, invalidatedProgramPoints, compiledFunction, runtimeScope, continuationEntryPoint, strict, onDemand);
     }
 
     /**
@@ -198,6 +215,10 @@
      * @param phases compilation phases
      * @param strict strict mode
      * @param compiledFunction recompiled function
+     * @param runtimeScope the runtime scope in effect at the time of compilation. It can be used to evaluate types of
+     * scoped variables to guide the optimistic compilation, should the call to this method trigger code compilation.
+     * Can be null if current runtime scope is not known, but that might cause compilation of code that will need more
+     * subsequent deoptimization passes.
      * @param paramTypeMap known parameter types
      * @param invalidatedProgramPoints map of invalidated program points to their type
      * @param onDemand is this an on demand compilation
@@ -206,10 +227,11 @@
         final CompilationPhases phases,
         final boolean strict,
         final RecompilableScriptFunctionData compiledFunction,
+        final ScriptObject runtimeScope,
         final ParamTypeMap paramTypeMap,
         final Map<Integer, Type> invalidatedProgramPoints,
         final boolean onDemand) {
-        this(phases, paramTypeMap, invalidatedProgramPoints, compiledFunction, null, strict, onDemand);
+        this(phases, paramTypeMap, invalidatedProgramPoints, compiledFunction, runtimeScope, null, strict, onDemand);
     }
 
     private CompilationEnvironment(
@@ -217,17 +239,16 @@
             final ParamTypeMap paramTypes,
             final Map<Integer, Type> invalidatedProgramPoints,
             final RecompilableScriptFunctionData compiledFunction,
+            final ScriptObject runtimeScope,
             final int[] continuationEntryPoints,
             final boolean strict,
             final boolean onDemand) {
         this.phases                   = phases;
         this.paramTypes               = paramTypes;
         this.continuationEntryPoints  = continuationEntryPoints;
-        this.invalidatedProgramPoints =
-            invalidatedProgramPoints == null ?
-                Collections.unmodifiableMap(new HashMap<Integer, Type>()) :
-                invalidatedProgramPoints;
+        this.invalidatedProgramPoints = invalidatedProgramPoints == null ? new HashMap<Integer, Type>() : invalidatedProgramPoints;
         this.compiledFunction         = compiledFunction;
+        this.runtimeScope             = runtimeScope;
         this.strict                   = strict;
         this.optimistic               = phases.contains(CompilationPhase.PROGRAM_POINT_PHASE);
         this.onDemand                 = onDemand;
@@ -352,13 +373,94 @@
      */
     Type getOptimisticType(final Optimistic node) {
         assert useOptimisticTypes();
-        final Type invalidType = invalidatedProgramPoints.get(node.getProgramPoint());
-        if (invalidType != null) {
-            return invalidType;//.nextWider();
+        final int programPoint = node.getProgramPoint();
+        final Type validType = invalidatedProgramPoints.get(programPoint);
+        if (validType != null) {
+            return validType;
+        }
+        final Type mostOptimisticType = node.getMostOptimisticType();
+        final Type evaluatedType = getEvaluatedType(node);
+        if(evaluatedType != null) {
+            if(evaluatedType.widerThan(mostOptimisticType)) {
+                final Type newValidType = evaluatedType.isObject() || evaluatedType.isBoolean() ? Type.OBJECT : evaluatedType;
+                // Update invalidatedProgramPoints so we don't re-evaluate the expression next time. This is a heuristic
+                // as we're doing a tradeoff. Re-evaluating expressions on each recompile takes time, but it might
+                // notice a widening in the type of the expression and thus prevent an unnecessary deoptimization later.
+                // We'll presume though that the types of expressions are mostly stable, so if we evaluated it in one
+                // compilation, we'll keep to that and risk a low-probability deoptimization if its type gets widened
+                // in the future.
+                invalidatedProgramPoints.put(node.getProgramPoint(), newValidType);
+            }
+            return evaluatedType;
         }
         return node.getMostOptimisticType();
     }
 
+
+    private Type getEvaluatedType(final Optimistic expr) {
+        if(expr instanceof IdentNode) {
+            return runtimeScope == null ? null : getPropertyType(runtimeScope, ((IdentNode)expr).getName());
+        } else if(expr instanceof AccessNode) {
+            final AccessNode accessNode = (AccessNode)expr;
+            final Object base = evaluateSafely(accessNode.getBase());
+            if(!(base instanceof ScriptObject)) {
+                return null;
+            }
+            return getPropertyType((ScriptObject)base, accessNode.getProperty().getName());
+        } else if(expr instanceof IndexNode) {
+            final IndexNode indexNode = (IndexNode)expr;
+            final Object base = evaluateSafely(indexNode.getBase());
+            if(!(base instanceof NativeArray)) {
+                // We only know how to deal with NativeArray. TODO: maybe manage buffers too
+                return null;
+            }
+            // NOTE: optimistic array getters throw UnwarrantedOptimismException based on the type of their underlying
+            // array storage, not based on values of individual elements. Thus, a LongArrayData will throw UOE for every
+            // optimistic int linkage attempt, even if the long value being returned in the first invocation would be
+            // representable as int. That way, we can presume that the array's optimistic type is the most optimistic
+            // type for which an element getter has a chance of executing successfully.
+            return ((NativeArray)base).getArray().getOptimisticType();
+        }
+        return null;
+    }
+
+    private static Type getPropertyType(final ScriptObject sobj, final String name) {
+        final FindProperty find = sobj.findProperty(name, true);
+        if(find == null) {
+            return null;
+        }
+        final Class<?> clazz = find.getProperty().getCurrentType();
+        return clazz == null ? null : Type.typeFor(clazz);
+    }
+
+    private Object evaluateSafely(Expression expr) {
+        if(expr instanceof IdentNode) {
+            return runtimeScope == null ? null : evaluatePropertySafely(runtimeScope, ((IdentNode)expr).getName());
+        } else if(expr instanceof AccessNode) {
+            final AccessNode accessNode = (AccessNode)expr;
+            final Object base = evaluateSafely(accessNode.getBase());
+            if(!(base instanceof ScriptObject)) {
+                return null;
+            }
+            return evaluatePropertySafely((ScriptObject)base, accessNode.getProperty().getName());
+        }
+        return null;
+    }
+
+    private static Object evaluatePropertySafely(final ScriptObject sobj, final String name) {
+        final FindProperty find = sobj.findProperty(name, true);
+        if(find == null) {
+            return null;
+        }
+        final Property property = find.getProperty();
+        final ScriptObject owner = find.getOwner();
+        if(property.hasGetterFunction(owner)) {
+            // Possible side effects; can't evaluate safely
+            return null;
+        }
+        return property.getObjectValue(owner, owner);
+    }
+
     /**
      * Should this compilation use optimistic types in general.
      * If this is false we will only set non-object types to things that can
--- a/nashorn/src/jdk/nashorn/internal/codegen/ConstantData.java	Mon Mar 24 18:40:11 2014 +0100
+++ b/nashorn/src/jdk/nashorn/internal/codegen/ConstantData.java	Mon Mar 24 18:41:06 2014 +0100
@@ -63,7 +63,7 @@
         private int calcHashCode() {
             final Class<?> cls = array.getClass();
 
-            if (cls == Object[].class) {
+            if (!cls.getComponentType().isPrimitive()) {
                 return Arrays.hashCode((Object[])array);
             } else if (cls == double[].class) {
                 return Arrays.hashCode((double[])array);
@@ -91,7 +91,7 @@
             final Class<?> cls = array.getClass();
 
             if (cls == otherArray.getClass()) {
-                if (cls == Object[].class) {
+                if (!cls.getComponentType().isPrimitive()) {
                     return Arrays.equals((Object[])array, (Object[])otherArray);
                 } else if (cls == double[].class) {
                     return Arrays.equals((double[])array, (double[])otherArray);
--- a/nashorn/src/jdk/nashorn/internal/runtime/CompiledFunction.java	Mon Mar 24 18:40:11 2014 +0100
+++ b/nashorn/src/jdk/nashorn/internal/runtime/CompiledFunction.java	Mon Mar 24 18:41:06 2014 +0100
@@ -620,7 +620,7 @@
                 return null;
             }
             SwitchPoint.invalidateAll(new SwitchPoint[] { optimisticAssumptions });
-            return data.compile(callSiteType, invalidatedProgramPoints, "Deoptimizing recompilation");
+            return data.compile(callSiteType, invalidatedProgramPoints, e.getRuntimeScope(), "Deoptimizing recompilation");
         }
 
         MethodHandle compileRestOfMethod(final MethodType callSiteType, final RewriteException e) {
@@ -634,7 +634,7 @@
                 System.arraycopy(prevEntryPoints, 0, entryPoints, 1, l);
             }
             entryPoints[0] = e.getProgramPoint();
-            return data.compileRestOfMethod(callSiteType, invalidatedProgramPoints, entryPoints);
+            return data.compileRestOfMethod(callSiteType, invalidatedProgramPoints, entryPoints, e.getRuntimeScope());
         }
     }
 
--- a/nashorn/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java	Mon Mar 24 18:40:11 2014 +0100
+++ b/nashorn/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java	Mon Mar 24 18:41:06 2014 +0100
@@ -320,7 +320,7 @@
         return sb.toString();
     }
 
-    MethodHandle compileRestOfMethod(final MethodType fnCallSiteType, final Map<Integer, Type> invalidatedProgramPoints, final int[] continuationEntryPoints) {
+    MethodHandle compileRestOfMethod(final MethodType fnCallSiteType, final Map<Integer, Type> invalidatedProgramPoints, final int[] continuationEntryPoints, final ScriptObject runtimeScope) {
         LOG.info("Rest-of compilation of '", functionName, "' signature: ", fnCallSiteType, " ", stringifyInvalidations(invalidatedProgramPoints));
 
         final String scriptName = RECOMPILATION_PREFIX + RECOMPILE_ID.incrementAndGet() + "$restOf";
@@ -331,6 +331,7 @@
                     CompilationPhases.EAGER.makeOptimistic(),
                     isStrict(),
                     this,
+                    runtimeScope,
                     isVariableArity() ? null : new ParamTypeMap(functionNodeId, explicitParams(fnCallSiteType)),
                     invalidatedProgramPoints,
                     continuationEntryPoints,
@@ -345,11 +346,11 @@
         return lookupWithExplicitType(fn, MethodType.methodType(fn.getReturnType().getTypeClass(), RewriteException.class));
     }
 
-    private FunctionNode compileTypeSpecialization(final MethodType actualCallSiteType) {
-        return compile(actualCallSiteType, null, "Type specialized compilation");
+    private FunctionNode compileTypeSpecialization(final MethodType actualCallSiteType, final ScriptObject runtimeScope) {
+        return compile(actualCallSiteType, null, runtimeScope, "Type specialized compilation");
     }
 
-    FunctionNode compile(final MethodType actualCallSiteType, final Map<Integer, Type> invalidatedProgramPoints, final String reason) {
+    FunctionNode compile(final MethodType actualCallSiteType, final Map<Integer, Type> invalidatedProgramPoints, final ScriptObject runtimeScope, 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));
@@ -361,6 +362,7 @@
                 phases.makeOptimistic(ScriptEnvironment.globalOptimistic()),
                 isStrict(),
                 this,
+                runtimeScope,
                 fnCallSiteType == null || isVariableArity() ?
                     null :
                     new ParamTypeMap(
@@ -506,11 +508,11 @@
     }
 
     @Override
-    CompiledFunction getBest(final MethodType callSiteType) {
+    CompiledFunction getBest(final MethodType callSiteType, final ScriptObject runtimeScope) {
         synchronized(code) {
-            final CompiledFunction existingBest = super.getBest(callSiteType);
+            final CompiledFunction existingBest = super.getBest(callSiteType, runtimeScope);
             // TODO: what if callSiteType is vararg?
-            return existingBest != null ? existingBest : addCode(compileTypeSpecialization(callSiteType), callSiteType);
+            return existingBest != null ? existingBest : addCode(compileTypeSpecialization(callSiteType, runtimeScope), callSiteType);
         }
     }
 
--- a/nashorn/src/jdk/nashorn/internal/runtime/RewriteException.java	Mon Mar 24 18:40:11 2014 +0100
+++ b/nashorn/src/jdk/nashorn/internal/runtime/RewriteException.java	Mon Mar 24 18:41:06 2014 +0100
@@ -39,6 +39,7 @@
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.lookup.MethodHandleFactory;
 import jdk.nashorn.internal.lookup.MethodHandleFunctionality;
+import jdk.nashorn.internal.objects.Global;
 
 /**
  * Used to signal to the linker to relink the callee
@@ -47,6 +48,9 @@
 public class RewriteException extends Exception {
     private static final MethodHandleFunctionality MH = MethodHandleFactory.getFunctionality();
 
+    // Runtime scope in effect at the time of the compilation. Used to evaluate types of expressions and prevent overly
+    // optimistic assumptions (which will lead to unnecessary deoptimizing recompilations).
+    private ScriptObject runtimeScope;
     //contents of bytecode slots
     private Object[] byteCodeSlots;
     private final int[] previousContinuationEntryPoints;
@@ -84,8 +88,8 @@
      * @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);
+    public RewriteException(final UnwarrantedOptimismException e, final Object[] byteCodeSlots, final String[] byteCodeSymbolNames, final ScriptObject runtimeScope) {
+        this(e, byteCodeSlots, byteCodeSymbolNames, runtimeScope, null);
     }
 
     /**
@@ -95,12 +99,28 @@
      * @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) {
+    public RewriteException(final UnwarrantedOptimismException e, final Object[] byteCodeSlots, final String[] byteCodeSymbolNames, final ScriptObject runtimeScope, final int[] previousContinuationEntryPoints) {
         super("", e, false, Context.DEBUG);
         this.byteCodeSlots = byteCodeSlots;
+        this.runtimeScope = mergeSlotsWithScope(byteCodeSlots, byteCodeSymbolNames, runtimeScope);
         this.previousContinuationEntryPoints = previousContinuationEntryPoints;
     }
 
+    private static ScriptObject mergeSlotsWithScope(final Object[] byteCodeSlots, final String[] byteCodeSymbolNames,
+            final ScriptObject runtimeScope) {
+        final ScriptObject locals = Global.newEmptyInstance();
+        final int l = Math.min(byteCodeSlots.length, byteCodeSymbolNames.length);
+        for(int i = 0; i < l; ++i) {
+            final String name = byteCodeSymbolNames[i];
+            final Object value = byteCodeSlots[i];
+            if(name != null) {
+                locals.set(name, value, true);
+            }
+        }
+        locals.setProto(runtimeScope);
+        return locals;
+    }
+
     /**
      * Array populator used for saving the local variable state into the array contained in the
      * RewriteException
@@ -127,6 +147,7 @@
     public Object getReturnValueDestructive() {
         assert byteCodeSlots != null;
         byteCodeSlots = null;
+        runtimeScope = null;
         return getUOE().getReturnValueDestructive();
     }
 
@@ -165,6 +186,14 @@
         return previousContinuationEntryPoints;
     }
 
+    /**
+     * Returns the runtime scope that was in effect when the exception was thrown.
+     * @return the runtime scope.
+     */
+    public ScriptObject getRuntimeScope() {
+        return runtimeScope;
+    }
+
     private static String stringify(final Object returnValue) {
         if (returnValue == null) {
             return "null";
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java	Mon Mar 24 18:40:11 2014 +0100
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java	Mon Mar 24 18:41:06 2014 +0100
@@ -331,7 +331,7 @@
      * assumptions.
      */
     private GuardedInvocation getBestInvoker(final MethodType callSiteType, final int callerProgramPoint) {
-        return data.getBestInvoker(callSiteType, callerProgramPoint);
+        return data.getBestInvoker(callSiteType, callerProgramPoint, scope);
     }
 
     /**
@@ -342,7 +342,7 @@
      * @return bound invoke handle
      */
     public final MethodHandle getBoundInvokeHandle(final Object self) {
-        return MH.bindTo(bindToCalleeIfNeeded(data.getGenericInvoker()), self);
+        return MH.bindTo(bindToCalleeIfNeeded(data.getGenericInvoker(scope)), self);
     }
 
     /**
@@ -471,7 +471,7 @@
     protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc) {
         final MethodType type = desc.getMethodType();
         assert desc.getMethodType().returnType() == Object.class && !NashornCallSiteDescriptor.isOptimistic(desc);
-        final GuardedInvocation bestCtorInv = data.getBestConstructor(type);
+        final GuardedInvocation bestCtorInv = data.getBestConstructor(type, scope);
         //TODO - ClassCastException
         return new GuardedInvocation(pairArguments(bestCtorInv.getInvocation(), type), getFunctionGuard(this), bestCtorInv.getSwitchPoint());
     }
@@ -697,7 +697,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(data.getGenericInvoker()), bindName), type);
+        return pairArguments(bindToNameIfNeeded(bindToCalleeIfNeeded(data.getGenericInvoker(scope)), bindName), type);
     }
 
     private static MethodHandle bindToNameIfNeeded(final MethodHandle methodHandle, final String bindName) {
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java	Mon Mar 24 18:40:11 2014 +0100
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java	Mon Mar 24 18:41:06 2014 +0100
@@ -38,7 +38,6 @@
 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;
 
 /**
@@ -203,18 +202,18 @@
      * @return guarded invocation with method handle to best invoker and potentially a switch point guarding optimistic
      * assumptions.
      */
-     final GuardedInvocation getBestInvoker(final MethodType callSiteType, final int callerProgramPoint) {
-        final CompiledFunction cf = getBest(callSiteType);
+     final GuardedInvocation getBestInvoker(final MethodType callSiteType, final int callerProgramPoint, final ScriptObject runtimeScope) {
+        final CompiledFunction cf = getBest(callSiteType, runtimeScope);
         assert cf != null;
         return new GuardedInvocation(cf.createInvoker(callSiteType.returnType(), callerProgramPoint), cf.getOptimisticAssumptionsSwitchPoint());
     }
 
-    final GuardedInvocation getBestConstructor(final MethodType callSiteType) {
+    final GuardedInvocation getBestConstructor(final MethodType callSiteType, final ScriptObject runtimeScope) {
         if (!isConstructor()) {
             throw typeError("not.a.constructor", toSource());
         }
         // 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));
+        final CompiledFunction cf = getBest(callSiteType.insertParameterTypes(1, Object.class), runtimeScope);
         return new GuardedInvocation(cf.getConstructor(), cf.getOptimisticAssumptionsSwitchPoint());
     }
 
@@ -232,12 +231,12 @@
      * is generated, get the most generic of all versions of this function and adapt it
      * to Objects.
      *
-     * TODO this is only public because {@link JavaAdapterFactory} can't supply us with
-     * a MethodType that we can use for lookup due to boostrapping problems. Can be fixed
-     *
+     * @param runtimeScope the runtime scope. It can be used to evaluate types of scoped variables to guide the
+     * optimistic compilation, should the call to this method trigger code compilation. Can be null if current runtime
+     * scope is not known, but that might cause compilation of code that will need more deoptimization passes.
      * @return generic invoker of this script function
      */
-    public final MethodHandle getGenericInvoker() {
+    final MethodHandle getGenericInvoker(final ScriptObject runtimeScope) {
         MethodHandle invoker;
         final Reference<MethodHandle> ref = GENERIC_INVOKERS.get(this);
         if(ref != null) {
@@ -246,16 +245,16 @@
                 return invoker;
             }
         }
-        invoker = createGenericInvoker();
+        invoker = createGenericInvoker(runtimeScope);
         GENERIC_INVOKERS.put(this, new WeakReference<>(invoker));
         return invoker;
     }
 
-    private MethodHandle createGenericInvoker() {
-        return makeGenericMethod(getGeneric().createComposableInvoker());
+    private MethodHandle createGenericInvoker(final ScriptObject runtimeScope) {
+        return makeGenericMethod(getGeneric(runtimeScope).createComposableInvoker());
     }
 
-    final MethodHandle getGenericConstructor() {
+    final MethodHandle getGenericConstructor(final ScriptObject runtimeScope) {
         MethodHandle constructor;
         final Reference<MethodHandle> ref = GENERIC_CONSTRUCTORS.get(this);
         if(ref != null) {
@@ -264,29 +263,32 @@
                 return constructor;
             }
         }
-        constructor = createGenericConstructor();
+        constructor = createGenericConstructor(runtimeScope);
         GENERIC_CONSTRUCTORS.put(this, new WeakReference<>(constructor));
         return constructor;
     }
 
-    private MethodHandle createGenericConstructor() {
-        return makeGenericMethod(getGeneric().createComposableConstructor());
+    private MethodHandle createGenericConstructor(final ScriptObject runtimeScope) {
+        return makeGenericMethod(getGeneric(runtimeScope).createComposableConstructor());
     }
 
     /**
      * 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...])}.
+     * @param runtimeScope the runtime scope. It can be used to evaluate types of scoped variables to guide the
+     * optimistic compilation, should the call to this method trigger code compilation. Can be null if current runtime
+     * scope is not known, but that might cause compilation of code that will need more deoptimization passes.
      * @return the best function for the specified call site type.
      */
-    CompiledFunction getBest(final MethodType callSiteType) {
+    CompiledFunction getBest(final MethodType callSiteType, final ScriptObject runtimeScope) {
         return code.best(callSiteType, isRecompilable());
     }
 
     abstract boolean isRecompilable();
 
-    CompiledFunction getGeneric() {
-        return getBest(getGenericType());
+    CompiledFunction getGeneric(final ScriptObject runtimeScope) {
+        return getBest(getGenericType(), runtimeScope);
     }
 
 
@@ -326,7 +328,8 @@
         final int boundFlags = flags & ~NEEDS_CALLEE & ~USES_THIS;
 
         final CompiledFunctions boundList = new CompiledFunctions(fn.getName());
-        final CompiledFunction bindTarget = new CompiledFunction(getGenericInvoker(), getGenericConstructor());
+        final ScriptObject runtimeScope = fn.getScope();
+        final CompiledFunction bindTarget = new CompiledFunction(getGenericInvoker(runtimeScope), getGenericConstructor(runtimeScope));
         boundList.add(bind(bindTarget, fn, self, allArgs));
 
         return new FinalScriptFunctionData(name, Math.max(0, getArity() - length), boundList, boundFlags);
@@ -518,7 +521,7 @@
      * @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 MethodHandle mh      = getGenericInvoker(fn.getScope());
         final Object       selfObj = convertThisObject(self);
         final Object[]     args    = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments;
 
@@ -572,7 +575,7 @@
     }
 
     Object construct(final ScriptFunction fn, final Object... arguments) throws Throwable {
-        final MethodHandle mh   = getGenericConstructor();
+        final MethodHandle mh   = getGenericConstructor(fn.getScope());
         final Object[]     args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments;
 
         if (isVarArg(mh)) {
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java	Mon Mar 24 18:40:11 2014 +0100
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java	Mon Mar 24 18:41:06 2014 +0100
@@ -3024,7 +3024,7 @@
                 assert sobj != null : "no parent global object in scope";
             }
             //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
+            //property supports primitive types, so it doesn't matter that it comes
             //in as an Object.
             sobj.addSpillProperty(key, 0, value, true);
         }
--- a/nashorn/test/script/currently-failing/OptimisticRecompilationTest.java	Mon Mar 24 18:40:11 2014 +0100
+++ b/nashorn/test/script/currently-failing/OptimisticRecompilationTest.java	Mon Mar 24 18:41:06 2014 +0100
@@ -24,18 +24,19 @@
  */
 package jdk.nashorn.internal.runtime;
 
+import static org.testng.Assert.fail;
+
 import java.io.ByteArrayOutputStream;
 import java.io.PrintStream;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
-import static org.testng.Assert.fail;
-import org.testng.annotations.Test;
-import org.testng.annotations.AfterTest;
-import org.testng.annotations.BeforeTest;
 import javax.script.ScriptEngine;
 import javax.script.ScriptEngineFactory;
 import javax.script.ScriptEngineManager;
 import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
+import org.testng.annotations.AfterTest;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
 
 /**
  * @test
@@ -102,56 +103,54 @@
 
     @Test
     public void divisionByZeroTest() {
-       //Check that two Deoptimizing recompilations and RewriteExceptions happened
-       runTest("function f() {var x = { a: 2, b:1 }; x.a = Number.POSITIVE_INFINITY;"
-               + " x.b = 0; print(x.a/x.b);} f()",
-               getRecompilationPattern("double", "Infinity"), 2);
+        //Check that one Deoptimizing recompilation and RewriteExceptions happened
+        runTest("function f() {var x1 = { a: 2, b:1 }; x1.a = Number.POSITIVE_INFINITY;"
+                + " x1.b = 0; print(x1.a/x1.b);} f()",
+                getRecompilationPattern("double", "Infinity"), 1);
     }
 
     @Test
     public void divisionWithRemainderTest() {
        //Check that one Deoptimizing recompilation and RewriteException happened
-       runTest("function f() {var x = { a: 7, b:2 }; print(x.a/x.b);} f()",
+        runTest("function f() {var x2 = { a: 7, b:2 }; print(x2.a/x2.b);} f()",
                getRecompilationPattern("double", "3.5"), 1);
     }
 
     @Test
     public void infinityMultiplicationTest() {
-       //Check that three Deoptimizing recompilations and RewriteExceptions happened
-       runTest("function f() {var x = { a: Number.POSITIVE_INFINITY, "
-               + "b: Number.POSITIVE_INFINITY}; print(x.a*x.b);} f()",
-               getRecompilationPattern("double", "Infinity"), 3);
+        //Check that one deoptimizing recompilation and RewriteExceptions happened
+        runTest("function f() {var x3 = { a: Number.POSITIVE_INFINITY, "
+                + "b: Number.POSITIVE_INFINITY}; print(x3.a*x3.b);} f()",
+                getRecompilationPattern("double", "Infinity"), 1);
     }
 
     @Test
     public void maxValueMultiplicationTest() {
-       runTest("function f() {var x = { a: Number.MAX_VALUE, b: Number.MAX_VALUE};"
-               + " print(x.a*x.b);} f()",
-               getRecompilationPattern("double", "1.7976931348623157E308"), 3);
+        runTest("function f() {var x4 = { a: Number.MAX_VALUE, b: Number.MAX_VALUE};"
+                + " print(x4.a*x4.b);} f()",
+                getRecompilationPattern("double", "1.7976931348623157E308"), 1);
     }
 
     @Test
     public void divisionByInfinityTest() {
-       //Check that two Deoptimizing recompilations and RewriteExceptions happened
-       runTest("function f() {var x = { a: -1, b: Number.POSITIVE_INFINITY};"
-               + " print(x.a/x.b);} f()",
-               getRecompilationPattern("double", "Infinity"), 2);
+        //Check that one Deoptimizing recompilation and RewriteExceptions happened
+        runTest("function f() {var x5 = { a: -1, b: Number.POSITIVE_INFINITY};"
+                + " print(x5.a/x5.b);} f()",
+                getRecompilationPattern("double", "Infinity"), 1);
     }
 
     @Test
     public void divisionByStringTest() {
-       //Check that three Deoptimizing recompilations and RewriteExceptions happened
-       String str1 = getRecompilationPattern("double", "Infinity");
-       String str2 = getRecompilationPattern("object", "Hello");
-       runTest("function f() {var x = { a: Number.POSITIVE_INFINITY, b: 'Hello'};"
-               + " print(x.a/x.b);} f()", String.format("(?s)%s.*%1$s.*%s", str1, str2), 1);
+        //Check that one deoptimizing recompilations and RewriteExceptions happened
+        runTest("function f() {var x6 = { a: Number.POSITIVE_INFINITY, b: 'Hello'};"
+                + " print(x6.a/x6.b);} f()", getRecompilationPattern("double", "Infinity"), 1);
     }
 
     @Test
     public void nestedFunctionTest() {
        //Check that one Deoptimizing recompilations and RewriteExceptions happened
-       runTest("var a=3,b,c; function f() {var x = 2, y =1; function g(){ "
-               + "var y = x; var z = a; z = x*y; print(a*b); } g() } f()",
+        runTest("var a=3,b,c; function f() {var x7 = 2, y =1; function g(){ "
+                + "var y = x7; var z = a; z = x7*y; print(a*b); } g() } f()",
                getRecompilationPattern("object", "undefined"), 1);
     }
 
@@ -165,7 +164,7 @@
     @Test
     public void functionTest() {
        //Check that one Deoptimizing recompilations and RewriteExceptions happened
-       runTest("function f(a,b,c) { d = (a + b) * c; print(d);} f()",
+        runTest("function f(a,b,c) { h = (a + b) * c; print(h);} f()",
                getRecompilationPattern("double", "NaN"), 1);
     }
 }