changeset 824:ba08b3188eb2

8033334: Make sure that scope depth information is maintained in the RecompilableScriptFunctionDatas, to avoid unnecessary slow proto linkage when doing on demand compilation Summary: Compute RecompiledScriptFunctionDatas eagerly, annotate them with scope depth information and use them in recompilations. Reviewed-by: attila, hannesw, jlaskey
author lagergren
date Thu, 20 Mar 2014 16:16:42 +0100
parents 949577ac683d
children d7807721d24a
files src/jdk/nashorn/internal/codegen/Attr.java src/jdk/nashorn/internal/codegen/ClassEmitter.java src/jdk/nashorn/internal/codegen/CodeGenerator.java src/jdk/nashorn/internal/codegen/CompilationEnvironment.java src/jdk/nashorn/internal/codegen/CompilationPhase.java src/jdk/nashorn/internal/codegen/CompilerConstants.java src/jdk/nashorn/internal/codegen/FindScopeDepths.java src/jdk/nashorn/internal/codegen/MethodEmitter.java src/jdk/nashorn/internal/codegen/SharedScopeCall.java src/jdk/nashorn/internal/codegen/types/Type.java src/jdk/nashorn/internal/ir/Block.java src/jdk/nashorn/internal/ir/FunctionNode.java src/jdk/nashorn/internal/ir/LexicalContext.java src/jdk/nashorn/internal/ir/Symbol.java src/jdk/nashorn/internal/ir/debug/NashornTextifier.java src/jdk/nashorn/internal/parser/Parser.java src/jdk/nashorn/internal/runtime/CompiledFunction.java src/jdk/nashorn/internal/runtime/Context.java src/jdk/nashorn/internal/runtime/Debug.java src/jdk/nashorn/internal/runtime/Property.java src/jdk/nashorn/internal/runtime/PropertyDescriptor.java src/jdk/nashorn/internal/runtime/PropertyMap.java src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java src/jdk/nashorn/internal/runtime/ScriptFunction.java src/jdk/nashorn/internal/runtime/ScriptObject.java src/jdk/nashorn/internal/runtime/ScriptRuntime.java src/jdk/nashorn/internal/runtime/arrays/ByteBufferArrayData.java src/jdk/nashorn/internal/runtime/linker/NashornCallSiteDescriptor.java
diffstat 28 files changed, 838 insertions(+), 304 deletions(-) [+]
line wrap: on
line diff
--- a/src/jdk/nashorn/internal/codegen/Attr.java	Mon Mar 17 15:05:18 2014 +0100
+++ b/src/jdk/nashorn/internal/codegen/Attr.java	Thu Mar 20 16:16:42 2014 +0100
@@ -52,10 +52,13 @@
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Deque;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+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.BinaryNode;
@@ -139,6 +142,7 @@
 
     private final Set<Long> optimistic = new HashSet<>();
     private final Set<Long> neverOptimistic = new HashSet<>();
+    private final Map<String, Symbol> globalSymbols = new HashMap<>(); //reuse the same global symbol
 
     private int catchNestingLevel;
 
@@ -454,7 +458,7 @@
             }
 
             // Create and add to appropriate block.
-            symbol = new Symbol(name, flags);
+            symbol = createSymbol(name, flags);
             symbolBlock.putSymbol(lc, symbol);
 
             if ((flags & Symbol.KINDMASK) != IS_GLOBAL) {
@@ -467,6 +471,19 @@
         return symbol;
     }
 
+    private Symbol createSymbol(final String name, final int flags) {
+        if ((flags & Symbol.KINDMASK) == IS_GLOBAL) {
+            //reuse global symbols so they can be hashed
+            Symbol global = globalSymbols.get(name);
+            if (global == null) {
+                global = new Symbol(name, flags);
+                globalSymbols.put(name, global);
+            }
+            return global;
+        }
+        return new Symbol(name, flags);
+    }
+
     @Override
     public boolean enterExpressionStatement(final ExpressionStatement expressionStatement) {
         final Expression expr = expressionStatement.getExpression();
@@ -555,10 +572,16 @@
         }
 
         final int optimisticFlag = lc.hasOptimisticAssumptions() ? FunctionNode.IS_OPTIMISTIC : 0;
+
         newFunctionNode = newFunctionNode.setState(lc, CompilationState.ATTR).setFlag(lc, optimisticFlag);
 
         popLocals();
 
+        if (!env.isOnDemandCompilation() && newFunctionNode.isProgram()) {
+            newFunctionNode = newFunctionNode.setBody(lc, newFunctionNode.getBody().setFlag(lc, Block.IS_GLOBAL_SCOPE));
+            assert newFunctionNode.getId() == 1;
+        }
+
         return end(newFunctionNode, false);
     }
 
@@ -576,7 +599,7 @@
         final IdentNode init = compilerConstant(initConstant);
         assert init.getSymbol() != null && init.getSymbol().hasSlot();
 
-        VarNode synthVar = new VarNode(fn.getLineNumber(), fn.getToken(), fn.getFinish(), name, init);
+        final VarNode synthVar = new VarNode(fn.getLineNumber(), fn.getToken(), fn.getFinish(), name, init);
 
         final Symbol nameSymbol = fn.getBody().getExistingSymbol(name.getName());
         assert nameSymbol != null;
@@ -631,7 +654,7 @@
             maybeForceScope(symbol);
         } else {
             LOG.info("No symbol exists. Declare undefined: ", symbol);
-            symbol = defineSymbol(block, name, IS_GLOBAL);
+            symbol = defineGlobalSymbol(block, name);
             // we have never seen this before, it can be undefined
             newType(symbol, Type.OBJECT); // TODO unknown -we have explicit casts anyway?
             symbol.setCanBeUndefined();
@@ -652,6 +675,10 @@
         return end(node);
     }
 
+    private Symbol defineGlobalSymbol(final Block block, final String name) {
+        return defineSymbol(block, name, IS_GLOBAL);
+    }
+
     private boolean inCatch() {
         return catchNestingLevel > 0;
     }
@@ -908,7 +935,7 @@
     }
 
     @Override
-    public boolean enterNOT(UnaryNode unaryNode) {
+    public boolean enterNOT(final UnaryNode unaryNode) {
         tagNeverOptimistic(unaryNode.getExpression());
         return true;
     }
@@ -1021,7 +1048,7 @@
         return end(coerce(unaryNode, Type.BOOLEAN));
     }
 
-    private IdentNode compilerConstant(CompilerConstants cc) {
+    private IdentNode compilerConstant(final CompilerConstants cc) {
         return (IdentNode)createImplicitIdentifier(cc.symbolName()).setSymbol(lc, lc.getCurrentFunction().compilerConstant(cc));
     }
 
@@ -1040,7 +1067,7 @@
     public Node leaveTYPEOF(final UnaryNode unaryNode) {
         final Expression rhs = unaryNode.getExpression();
 
-        List<Expression> args = new ArrayList<>();
+        final List<Expression> args = new ArrayList<>();
         if (rhs instanceof IdentNode && !rhs.getSymbol().isParam() && !rhs.getSymbol().isVar()) {
             args.add(compilerConstant(SCOPE));
             args.add((Expression)LiteralNode.newInstance(rhs, ((IdentNode)rhs).getName()).accept(this)); //null
@@ -1099,7 +1126,7 @@
         //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) {
+        if (argumentsType.getTypeClass() == String.class) {
             assert binaryNode.isTokenType(TokenType.ADD);
             argumentsType = Type.OBJECT;
         }
@@ -1151,7 +1178,7 @@
             final Symbol symbol = findSymbol(block, name);
 
             if (symbol == null) {
-                defineSymbol(block, name, IS_GLOBAL);
+                defineGlobalSymbol(block, name);
             } else {
                 maybeForceScope(symbol);
             }
@@ -1169,7 +1196,7 @@
         return end(ensureSymbol(binaryNode, type));
     }
 
-    private boolean isLocal(FunctionNode function, Symbol symbol) {
+    private boolean isLocal(final FunctionNode function, final Symbol symbol) {
         final FunctionNode definingFn = lc.getDefiningFunction(symbol);
         // Temp symbols are not assigned to a block, so their defining fn is null; those can be assumed local
         return definingFn == null || definingFn == function;
@@ -1372,7 +1399,7 @@
         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());
+        final Type widest = Type.widest(lhs.getType(), rhs.getType());
         ensureSymbol(lhs, widest);
         ensureSymbol(rhs, widest);
         return end(ensureSymbol(binaryNode, Type.BOOLEAN));
@@ -1390,7 +1417,7 @@
     }
 
     @Override
-    public boolean enterEQ(BinaryNode binaryNode) {
+    public boolean enterEQ(final BinaryNode binaryNode) {
         return enterBinaryArithmetic(binaryNode);
     }
 
@@ -1549,7 +1576,7 @@
     }
 
     @Override
-    public boolean enterForNode(ForNode forNode) {
+    public boolean enterForNode(final ForNode forNode) {
         tagNeverOptimistic(forNode.getTest());
         return true;
     }
@@ -1570,7 +1597,7 @@
     }
 
     @Override
-    public boolean enterTernaryNode(TernaryNode ternaryNode) {
+    public boolean enterTernaryNode(final TernaryNode ternaryNode) {
         tagNeverOptimistic(ternaryNode.getTest());
         return true;
     }
@@ -1675,7 +1702,7 @@
             newParams.add((IdentNode)param.setSymbol(lc, paramSymbol));
 
             assert paramSymbol != null;
-            Type type = paramSymbol.getSymbolType();
+            final Type type = paramSymbol.getSymbolType();
 
             // all param types are initialized to unknown
             // first we check if we do have a type (inferred during generation)
@@ -1705,7 +1732,7 @@
             }
         }
 
-        FunctionNode newFunctionNode = functionNode;
+        final FunctionNode newFunctionNode = functionNode;
 
         return newFunctionNode.setParameters(lc, newParams);
     }
@@ -1721,7 +1748,7 @@
 
         for (final Property property : map.getProperties()) {
             final String key    = property.getKey();
-            final Symbol symbol = defineSymbol(block, key, IS_GLOBAL);
+            final Symbol symbol = defineGlobalSymbol(block, key);
             newType(symbol, Type.OBJECT);
             LOG.info("Added global symbol from property map ", symbol);
         }
@@ -1761,9 +1788,11 @@
                     if (node instanceof LiteralNode) {
                         return node;
                     }
-                    Type from = node.getType();
+                    final Type from = node.getType();
                     if (!Type.areEquivalent(from, to) && Type.widest(from, to) == to) {
-                        LOG.fine("Had to post pass widen '", node, "' ", Debug.id(node), " from ", node.getType(), " to ", to);
+                        if (LOG.isEnabled()) {
+                            LOG.fine("Had to post pass widen '", node, "' ", Debug.id(node), " from ", node.getType(), " to ", to);
+                        }
                         Symbol symbol = node.getSymbol();
                         if (symbol.isShared() && symbol.wouldChangeType(to)) {
                             symbol = temporarySymbols.getTypedTemporarySymbol(to);
@@ -1875,7 +1904,7 @@
                 }
 
                 @Override
-                public Node leaveTernaryNode(TernaryNode ternaryNode) {
+                public Node leaveTernaryNode(final TernaryNode ternaryNode) {
                     return widen(ternaryNode, Type.widest(ternaryNode.getTrueExpression().getType(), ternaryNode.getFalseExpression().getType()));
                 }
 
@@ -1938,19 +1967,19 @@
     }
 
     @Override
-    public boolean enterReturnNode(ReturnNode returnNode) {
+    public boolean enterReturnNode(final ReturnNode returnNode) {
         tagOptimistic(returnNode.getExpression());
         return true;
     }
 
     @Override
-    public boolean enterIfNode(IfNode ifNode) {
+    public boolean enterIfNode(final IfNode ifNode) {
         tagNeverOptimistic(ifNode.getTest());
         return true;
     }
 
     @Override
-    public boolean enterWhileNode(WhileNode whileNode) {
+    public boolean enterWhileNode(final WhileNode whileNode) {
         tagNeverOptimistic(whileNode.getTest());
         return true;
     }
@@ -1966,7 +1995,7 @@
      * @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();
+        return (long)lc.getCurrentFunction().getId() << 32 | expr.getProgramPoint();
     }
 
     /**
@@ -2000,7 +2029,7 @@
         return optimistic.contains(tag(expr));
     }
 
-    private Type getOptimisticType(Optimistic expr) {
+    private Type getOptimisticType(final Optimistic expr) {
         return useOptimisticTypes() ? env.getOptimisticType(expr) : expr.getMostPessimisticType();
     }
 
@@ -2138,7 +2167,7 @@
     }
 
     private BinaryNode coerce(final BinaryNode binaryNode, final Type pessimisticType, final Type argumentsType) {
-        BinaryNode newNode = ensureSymbolTypeOverride(binaryNode, pessimisticType, argumentsType);
+        final BinaryNode newNode = ensureSymbolTypeOverride(binaryNode, pessimisticType, argumentsType);
         inferParameter(binaryNode.lhs(), newNode.getType());
         inferParameter(binaryNode.rhs(), newNode.getType());
         return newNode;
@@ -2161,7 +2190,7 @@
 
     private static String name(final Node node) {
         final String cn = node.getClass().getName();
-        int lastDot = cn.lastIndexOf('.');
+        final int lastDot = cn.lastIndexOf('.');
         if (lastDot == -1) {
             return cn;
         }
--- a/src/jdk/nashorn/internal/codegen/ClassEmitter.java	Mon Mar 17 15:05:18 2014 +0100
+++ b/src/jdk/nashorn/internal/codegen/ClassEmitter.java	Thu Mar 20 16:16:42 2014 +0100
@@ -277,51 +277,52 @@
         }
 
         // $getXXXX$array - get the ith entry from the constants table and cast to XXXX[].
-        for (final Class<?> cls : constantMethodNeeded) {
-            if (cls.isArray()) {
-                defineGetArrayMethod(cls);
+        for (final Class<?> clazz : constantMethodNeeded) {
+            if (clazz.isArray()) {
+                defineGetArrayMethod(clazz);
             }
         }
     }
 
     /**
      * Constructs a primitive specific method for getting the ith entry from the constants table and cast.
-     * @param cls Array class.
+     * @param clazz Array class.
      */
-    private void defineGetArrayMethod(final Class<?> cls) {
+    private void defineGetArrayMethod(final Class<?> clazz) {
         assert unitClassName != null;
 
-        final String        methodName     = getArrayMethodName(cls);
-        final MethodEmitter getArrayMethod = method(EnumSet.of(Flag.PRIVATE, Flag.STATIC), methodName, cls, int.class);
+        final String        methodName     = getArrayMethodName(clazz);
+        final MethodEmitter getArrayMethod = method(EnumSet.of(Flag.PRIVATE, Flag.STATIC), methodName, clazz, int.class);
 
         getArrayMethod.begin();
         getArrayMethod.getStatic(unitClassName, CONSTANTS.symbolName(), CONSTANTS.descriptor())
                       .load(Type.INT, 0)
                       .arrayload()
-                      .checkcast(cls)
+                      .checkcast(clazz)
                       .dup()
                       .arraylength()
-                      .invoke(staticCallNoLookup(Arrays.class, "copyOf", cls, cls, int.class))
+                      .invoke(staticCallNoLookup(Arrays.class, "copyOf", clazz, clazz, int.class))
                       ._return();
         getArrayMethod.end();
     }
 
+
     /**
      * Generate the name of a get array from constant pool method.
-     * @param cls Name of array class.
+     * @param clazz Name of array class.
      * @return Method name.
      */
-    static String getArrayMethodName(final Class<?> cls) {
-        assert cls.isArray();
-        return GET_ARRAY_PREFIX.symbolName() + cls.getComponentType().getSimpleName() + GET_ARRAY_SUFFIX.symbolName();
+    static String getArrayMethodName(final Class<?> clazz) {
+        assert clazz.isArray();
+        return GET_ARRAY_PREFIX.symbolName() + clazz.getComponentType().getSimpleName() + GET_ARRAY_SUFFIX.symbolName();
     }
 
     /**
      * Ensure a get constant method is issued for the class.
-     * @param cls Class of constant.
+     * @param clazz Class of constant.
      */
-    void needGetConstantMethod(final Class<?> cls) {
-        constantMethodNeeded.add(cls);
+    void needGetConstantMethod(final Class<?> clazz) {
+        constantMethodNeeded.add(clazz);
     }
 
     /**
@@ -672,7 +673,7 @@
         }
     }
 
-    private MethodVisitor methodVisitor(EnumSet<Flag> flags, final String methodName, final Class<?> rtype, final Class<?>... ptypes) {
+    private MethodVisitor methodVisitor(final EnumSet<Flag> flags, final String methodName, final Class<?> rtype, final Class<?>... ptypes) {
         return cw.visitMethod(Flag.getValue(flags), methodName, methodDescriptor(rtype, ptypes), null, null);
     }
 
--- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Mon Mar 17 15:05:18 2014 +0100
+++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Thu Mar 20 16:16:42 2014 +0100
@@ -47,8 +47,6 @@
 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;
@@ -76,6 +74,7 @@
 import java.util.RandomAccess;
 import java.util.Set;
 import java.util.TreeMap;
+
 import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
 import jdk.nashorn.internal.codegen.CompilerConstants.Call;
 import jdk.nashorn.internal.codegen.RuntimeCallSite.SpecializedRuntimeNode;
@@ -224,11 +223,10 @@
     // 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<>();
 
+    private final Set<Integer> initializedFunctionIds = new HashSet<>();
+
     /**
      * Constructor.
      *
@@ -299,6 +297,7 @@
                 loadFastScopeVar(identNode, type, flags, isCompileTimePropertyName);
             }
         } else {
+            //slow scope load, we have no proto depth
             new OptimisticOperation() {
                 @Override
                 void loadStack() {
@@ -409,9 +408,13 @@
     }
 
     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, symbol, valueType, flags | CALLSITE_FAST_SCOPE);
-        return scopeCall.generateInvoke(method);
+        assert !isOptimisticOrRestOf();
+        if (isFastScope(symbol)) {
+            method.load(getScopeProtoDepth(lc.getCurrentBlock(), symbol));
+        } else {
+            method.load(-1);
+        }
+        return lc.getScopeGet(unit, symbol, valueType, flags | CALLSITE_FAST_SCOPE).generateInvoke(method);
     }
 
     private MethodEmitter loadFastScopeVar(final IdentNode identNode, final Type type, final int flags, final boolean isCompileTimePropertyName) {
@@ -424,7 +427,7 @@
             @Override
             void consumeStack() {
                 dynamicGet(method, identNode, isCompileTimePropertyName ? Type.OBJECT : type, identNode.getSymbol().getName(), flags | CALLSITE_FAST_SCOPE, identNode.isFunction());
-                if(isCompileTimePropertyName) {
+                if (isCompileTimePropertyName) {
                     replaceCompileTimeProperty(identNode, type);
                 }
             }
@@ -438,23 +441,30 @@
     }
 
     private int getScopeProtoDepth(final Block startingBlock, final Symbol symbol) {
+        //walk up the chain from startingblock and when we bump into the current function boundary, add the external
+        //information.
+        final FunctionNode fn   = lc.getCurrentFunction();
+        final int          fnId = fn.getId();
+        final int externalDepth = compiler.getCompilationEnvironment().getScriptFunctionData(fnId).getExternalSymbolDepth(symbol.getName());
+
+        //count the number of scopes from this place to the start of the function
+
+        final int internalDepth = FindScopeDepths.findInternalDepth(lc, fn, startingBlock, symbol);
+        final int scopesToStart = FindScopeDepths.findScopesToStart(lc, fn, startingBlock);
         int depth = 0;
-        final String name = symbol.getName();
-        for(final Iterator<Block> blocks = lc.getBlocks(startingBlock); blocks.hasNext();) {
-            final Block currentBlock = blocks.next();
-            if (currentBlock.getExistingSymbol(name) == symbol) {
-                return depth;
-            }
-            if (currentBlock.needsScope()) {
-                ++depth;
-            }
+        if (internalDepth == -1) {
+            depth = scopesToStart + externalDepth;
+        } else {
+            assert internalDepth <= scopesToStart;
+            depth = internalDepth;
         }
-        return -1;
+
+        return depth;
     }
 
     private void loadFastScopeProto(final Symbol symbol, final boolean swap) {
         final int depth = getScopeProtoDepth(lc.getCurrentBlock(), symbol);
-        assert depth != -1 : "Couldn't find scope depth for symbol " + symbol.getName();
+        assert depth != -1 : "Couldn't find scope depth for symbol " + symbol.getName() + " in " + lc.getCurrentFunction();
         if (depth > 0) {
             if (swap) {
                 method.swap();
@@ -588,7 +598,7 @@
             }
 
             @Override
-            public boolean enterFunctionNode(FunctionNode functionNode) {
+            public boolean enterFunctionNode(final FunctionNode functionNode) {
                 // function nodes will always leave a constructed function object on stack, no need to load the symbol
                 // separately as in enterDefault()
                 lc.pop(functionNode);
@@ -603,12 +613,12 @@
             }
 
             @Override
-            public boolean enterCallNode(CallNode callNode) {
+            public boolean enterCallNode(final CallNode callNode) {
                 return codegen.enterCallNode(callNode, type);
             }
 
             @Override
-            public boolean enterLiteralNode(LiteralNode<?> literalNode) {
+            public boolean enterLiteralNode(final LiteralNode<?> literalNode) {
                 return codegen.enterLiteralNode(literalNode, type);
             }
 
@@ -658,7 +668,7 @@
 
             if (symbol.hasSlot()) {
                 final Type type = symbol.getSymbolType();
-                if(symbol.canBeUndefined() && !isInternal) {
+                if (symbol.canBeUndefined() && !isInternal) {
                     if (type.isNumber()) {
                         numbers.add(symbol);
                     } else if (type.isObject()) {
@@ -1122,7 +1132,7 @@
      * @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) {
+    private static MethodEmitter dynamicGet(final MethodEmitter method, final Expression expr, final Type desiredType, final String name, final int flags, final boolean isMethod) {
         final int finalFlags = maybeRemoveOptimisticFlags(desiredType, flags);
         if(isOptimistic(finalFlags)) {
             return method.dynamicGet(getOptimisticCoercedType(desiredType, expr), name, finalFlags, isMethod).convert(desiredType);
@@ -1130,7 +1140,7 @@
         return method.dynamicGet(desiredType, name, finalFlags, isMethod);
     }
 
-    private static MethodEmitter dynamicGetIndex(MethodEmitter method, Expression expr, Type desiredType, int flags, boolean isMethod) {
+    private static MethodEmitter dynamicGetIndex(final MethodEmitter method, final Expression expr, final Type desiredType, final int flags, final boolean isMethod) {
         final int finalFlags = maybeRemoveOptimisticFlags(desiredType, flags);
         if(isOptimistic(finalFlags)) {
             return method.dynamicGetIndex(getOptimisticCoercedType(desiredType, expr), finalFlags, isMethod).convert(desiredType);
@@ -1138,7 +1148,7 @@
         return method.dynamicGetIndex(desiredType, finalFlags, isMethod);
     }
 
-    private static MethodEmitter dynamicCall(MethodEmitter method, Expression expr, Type desiredType, int argCount, int flags) {
+    private static MethodEmitter dynamicCall(final MethodEmitter method, final Expression expr, final Type desiredType, final int argCount, final int flags) {
         final int finalFlags = maybeRemoveOptimisticFlags(desiredType, flags);
         if(isOptimistic(finalFlags)) {
             return method.dynamicCall(getOptimisticCoercedType(desiredType, expr), argCount, finalFlags).convert(desiredType);
@@ -1176,7 +1186,7 @@
      * @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) {
+    private static int maybeRemoveOptimisticFlags(final Type type, final int flags) {
         return type.isObject() ? nonOptimisticFlags(flags) : flags;
     }
 
@@ -1185,8 +1195,8 @@
      * @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);
+    static int nonOptimisticFlags(final int flags) {
+        return flags & ~(CALLSITE_OPTIMISTIC | (-1 << CALLSITE_PROGRAM_POINT_SHIFT));
     }
 
     @Override
@@ -1423,7 +1433,7 @@
             }
 
             method.storeCompilerConstant(SCOPE);
-            if(!isFunctionBody) {
+            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.
@@ -1474,13 +1484,7 @@
     @Override
     public boolean enterFunctionNode(final 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.
@@ -1544,11 +1548,12 @@
             }
 
             FunctionNode newFunctionNode = functionNode.setState(lc, CompilationState.EMITTED);
-            if(markOptimistic) {
+            if (markOptimistic) {
                 newFunctionNode = newFunctionNode.setFlag(lc, FunctionNode.IS_OPTIMISTIC);
             }
 
             newFunctionObject(newFunctionNode, true);
+
             return newFunctionNode;
         } catch (final Throwable t) {
             Context.printStackTrace(t);
@@ -1614,7 +1619,7 @@
         lineNumber(statement.getLineNumber());
     }
 
-    private void lineNumber(int lineNumber) {
+    private void lineNumber(final int lineNumber) {
         if (lineNumber != lastLineNumber) {
             method.lineNumber(lineNumber);
         }
@@ -3696,7 +3701,7 @@
              */
             target.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
                 @Override
-                protected boolean enterDefault(Node node) {
+                protected boolean enterDefault(final Node node) {
                     throw new AssertionError("Unexpected node " + node + " in store epilogue");
                 }
 
@@ -3754,42 +3759,28 @@
 
         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);
-            }
+        final RecompilableScriptFunctionData data = env.getScriptFunctionData(fnId);
+
+        assert data != null : functionNode.getName() + " has no data";
+
+        final FunctionNode parentFn = lc.getParentFunction(functionNode);
+        if (parentFn == null && functionNode.isProgram()) {
+            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();
         }
 
-        if(addInitializer && !env.isOnDemandCompilation()) {
+        if (addInitializer && !initializedFunctionIds.contains(fnId) && !env.isOnDemandCompilation()) {
             functionNode.getCompileUnit().addFunctionInitializer(data, functionNode);
+            initializedFunctionIds.add(fnId);
         }
 
         // We don't emit a ScriptFunction on stack for the outermost compiled function (as there's no code being
@@ -3806,6 +3797,7 @@
         } else {
             method.loadNull();
         }
+
         method.invoke(constructorNoLookup(SCRIPTFUNCTION_IMPL_NAME, RecompilableScriptFunctionData.class, ScriptObject.class));
     }
 
@@ -3954,7 +3946,7 @@
          * 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) {
+        private final 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
             }
@@ -4059,7 +4051,7 @@
          * @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) {
+        private final int getUsedSlotsWithLiveTemporaries(final List<Type> localVariableTypes, final 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.
@@ -4082,7 +4074,7 @@
         abstract void consumeStack();
     }
 
-    private static boolean everyLocalLoadIsValid(final int[] loads, int localCount) {
+    private static boolean everyLocalLoadIsValid(final int[] loads, final int localCount) {
         for (final int load : loads) {
             if(load < 0 || load >= localCount) {
                 return false;
@@ -4297,7 +4289,7 @@
         return true;
     }
 
-    private static String commonPrefix(String s1, String s2) {
+    private static String commonPrefix(final String s1, final String s2) {
         final int l1 = s1.length();
         final int l = Math.min(l1, s2.length());
         for(int i = 0; i < l; ++i) {
@@ -4313,7 +4305,7 @@
         private final boolean catchTarget;
         private boolean delegationTarget;
 
-        OptimismExceptionHandlerSpec(final String lvarSpec, boolean catchTarget) {
+        OptimismExceptionHandlerSpec(final String lvarSpec, final boolean catchTarget) {
             this.lvarSpec = lvarSpec;
             this.catchTarget = catchTarget;
             if(!catchTarget) {
@@ -4322,7 +4314,7 @@
         }
 
         @Override
-        public int compareTo(OptimismExceptionHandlerSpec o) {
+        public int compareTo(final OptimismExceptionHandlerSpec o) {
             return lvarSpec.compareTo(o.lvarSpec);
         }
 
--- a/src/jdk/nashorn/internal/codegen/CompilationEnvironment.java	Mon Mar 17 15:05:18 2014 +0100
+++ b/src/jdk/nashorn/internal/codegen/CompilationEnvironment.java	Thu Mar 20 16:16:42 2014 +0100
@@ -50,10 +50,12 @@
 
     private final ParamTypeMap paramTypes;
 
-    private final RecompilableScriptFunctionData compiledFunction;
+    private RecompilableScriptFunctionData compiledFunction;
 
     private boolean strict;
 
+    private final boolean onDemand;
+
     /**
      * 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.
@@ -88,12 +90,13 @@
             CompilationPhase.ATTRIBUTION_PHASE,
             CompilationPhase.RANGE_ANALYSIS_PHASE,
             CompilationPhase.TYPE_FINALIZATION_PHASE,
+            CompilationPhase.SCOPE_DEPTH_COMPUTATION_PHASE,
             CompilationPhase.BYTECODE_GENERATION_PHASE
         };
 
         private final static List<CompilationPhase> SEQUENCE_EAGER;
         static {
-            LinkedList<CompilationPhase> eager = new LinkedList<>();
+            final LinkedList<CompilationPhase> eager = new LinkedList<>();
             for (final CompilationPhase phase : SEQUENCE_EAGER_ARRAY) {
                 eager.add(phase);
             }
@@ -121,7 +124,7 @@
 
         private CompilationPhases addAfter(final CompilationPhase phase, final CompilationPhase newPhase) {
             final LinkedList<CompilationPhase> list = new LinkedList<>();
-            for (CompilationPhase p : phases) {
+            for (final CompilationPhase p : phases) {
                 list.add(p);
                 if (p == phase) {
                     list.add(newPhase);
@@ -166,80 +169,90 @@
     public CompilationEnvironment(
         final CompilationPhases phases,
         final boolean strict) {
-        this(phases, null, null, null, null, strict);
+        this(phases, null, null, null, null, strict, false);
     }
 
     /**
      * Constructor for compilation environment of the rest-of method
      * @param phases compilation phases
      * @param strict strict mode
-     * @param recompiledFunction recompiled function
+     * @param compiledFunction recompiled function
+     * @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
-     * @param invalidatedProgramPoints map of invalidated program points to their type
-     * @param paramTypeMap known parameter types if any exist
+     * @param onDemand is this an on demand compilation
      */
     public CompilationEnvironment(
         final CompilationPhases phases,
         final boolean strict,
-        final RecompilableScriptFunctionData recompiledFunction,
+        final RecompilableScriptFunctionData compiledFunction,
         final ParamTypeMap paramTypeMap,
         final Map<Integer, Type> invalidatedProgramPoints,
-        final int[] continuationEntryPoint) {
-            this(phases, paramTypeMap, invalidatedProgramPoints, recompiledFunction, continuationEntryPoint, strict);
+        final int[] continuationEntryPoint,
+        final boolean onDemand) {
+            this(phases, paramTypeMap, invalidatedProgramPoints, compiledFunction, continuationEntryPoint, strict, onDemand);
     }
 
     /**
      * Constructor
      * @param phases compilation phases
      * @param strict strict mode
-     * @param recompiledFunction recompiled function
+     * @param compiledFunction recompiled function
      * @param paramTypeMap known parameter types
      * @param invalidatedProgramPoints map of invalidated program points to their type
+     * @param onDemand is this an on demand compilation
      */
     public CompilationEnvironment(
         final CompilationPhases phases,
         final boolean strict,
-        final RecompilableScriptFunctionData recompiledFunction,
+        final RecompilableScriptFunctionData compiledFunction,
         final ParamTypeMap paramTypeMap,
-        final Map<Integer, Type> invalidatedProgramPoints) {
-        this(phases, paramTypeMap, invalidatedProgramPoints, recompiledFunction, null, strict);
+        final Map<Integer, Type> invalidatedProgramPoints,
+        final boolean onDemand) {
+        this(phases, paramTypeMap, invalidatedProgramPoints, compiledFunction, null, strict, onDemand);
     }
 
-    @SuppressWarnings("null")
     private CompilationEnvironment(
             final CompilationPhases phases,
             final ParamTypeMap paramTypes,
             final Map<Integer, Type> invalidatedProgramPoints,
             final RecompilableScriptFunctionData compiledFunction,
             final int[] continuationEntryPoints,
-            final boolean strict) {
+            final boolean strict,
+            final boolean onDemand) {
         this.phases                   = phases;
         this.paramTypes               = paramTypes;
-        this.continuationEntryPoints = continuationEntryPoints;
+        this.continuationEntryPoints  = continuationEntryPoints;
         this.invalidatedProgramPoints =
             invalidatedProgramPoints == null ?
                 Collections.unmodifiableMap(new HashMap<Integer, Type>()) :
                 invalidatedProgramPoints;
-        this.compiledFunction       = compiledFunction;
+        this.compiledFunction         = compiledFunction;
         this.strict                   = strict;
         this.optimistic               = phases.contains(CompilationPhase.PROGRAM_POINT_PHASE);
+        this.onDemand                 = onDemand;
 
         // 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));
+        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])) {
+    private static boolean containsAll(final Set<Integer> set, final int[] array) {
+        for (int i = 0; i < array.length; ++i) {
+            if (!set.contains(array[i])) {
                 return false;
             }
         }
         return true;
     }
 
+    void setData(final RecompilableScriptFunctionData data) {
+        assert this.compiledFunction == null : data;
+        this.compiledFunction = data;
+    }
+
     boolean isStrict() {
         return strict;
     }
@@ -291,7 +304,7 @@
      * @return true if this is an on-demand compilation, false if this is an eager compilation.
      */
     boolean isOnDemandCompilation() {
-        return compiledFunction != null;
+        return onDemand; //data != null;
     }
 
     /**
@@ -300,9 +313,9 @@
      * @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) {
+        if (continuationEntryPoints != null) {
+            for (final int continuationEntryPoint : continuationEntryPoints) {
+                if (continuationEntryPoint == programPoint) {
                     return true;
                 }
             }
@@ -338,7 +351,6 @@
      * @return most optimistic type in current environment
      */
     Type getOptimisticType(final Optimistic node) {
-
         assert useOptimisticTypes();
         final Type invalidType = invalidatedProgramPoints.get(node.getProgramPoint());
         if (invalidType != null) {
--- a/src/jdk/nashorn/internal/codegen/CompilationPhase.java	Mon Mar 17 15:05:18 2014 +0100
+++ b/src/jdk/nashorn/internal/codegen/CompilationPhase.java	Thu Mar 20 16:16:42 2014 +0100
@@ -6,6 +6,7 @@
 import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.INITIALIZED;
 import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.LOWERED;
 import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.PARSED;
+import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SCOPE_DEPTHS_COMPUTED;
 import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SPLIT;
 
 import java.util.ArrayDeque;
@@ -13,6 +14,7 @@
 import java.util.Deque;
 import java.util.EnumSet;
 import java.util.List;
+
 import jdk.nashorn.internal.codegen.types.Range;
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.Expression;
@@ -120,10 +122,14 @@
         @Override
         FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
             final TemporarySymbols ts = compiler.getTemporarySymbols();
-            final FunctionNode newFunctionNode = (FunctionNode)enterAttr(fn, ts).accept(new Attr(compiler.getCompilationEnvironment(), 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());
             }
+
             return newFunctionNode;
         }
 
@@ -271,12 +277,24 @@
         }
     },
 
+    SCOPE_DEPTH_COMPUTATION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR, SPLIT, FINALIZED)) {
+        @Override
+        FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
+            return (FunctionNode)fn.accept(new FindScopeDepths(compiler));
+        }
+
+        @Override
+        public String toString() {
+            return "[Scope Depth Computation]";
+        }
+    },
+
     /**
      * Bytecode generation:
      *
      * Generate the byte code class(es) resulting from the compiled FunctionNode
      */
-    BYTECODE_GENERATION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR, SPLIT, FINALIZED)) {
+    BYTECODE_GENERATION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR, SPLIT, FINALIZED, SCOPE_DEPTHS_COMPUTED)) {
         @Override
         FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
             final ScriptEnvironment env = compiler.getEnv();
--- a/src/jdk/nashorn/internal/codegen/CompilerConstants.java	Mon Mar 17 15:05:18 2014 +0100
+++ b/src/jdk/nashorn/internal/codegen/CompilerConstants.java	Thu Mar 20 16:16:42 2014 +0100
@@ -170,6 +170,16 @@
     /** get array suffix */
     GET_ARRAY_SUFFIX("$array");
 
+    /** To save memory - intern the compiler constant symbol names, as they are frequently reused */
+    static {
+        for (final CompilerConstants c : values()) {
+            final String symbolName = c.symbolName();
+            if (symbolName != null) {
+                symbolName.intern();
+            }
+        }
+    }
+
     /**
      * Prefix used for internal methods generated in script clases.
      */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/codegen/FindScopeDepths.java	Thu Mar 20 16:16:42 2014 +0100
@@ -0,0 +1,275 @@
+/*
+ * 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.  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.getClassName;
+import static jdk.nashorn.internal.codegen.ObjectClassGenerator.getPaddedFieldCount;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import jdk.nashorn.internal.ir.Block;
+import jdk.nashorn.internal.ir.Expression;
+import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
+import jdk.nashorn.internal.ir.LexicalContext;
+import jdk.nashorn.internal.ir.Node;
+import jdk.nashorn.internal.ir.Symbol;
+import jdk.nashorn.internal.ir.visitor.NodeVisitor;
+import jdk.nashorn.internal.runtime.PropertyMap;
+import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
+
+/**
+ * Establishes depth of scope for non local symbols at the start of method.
+ * If this is a recompilation, the previous data from eager compilation is
+ * stored in the RecompilableScriptFunctionData and is transferred to the
+ * FunctionNode being compiled
+ */
+
+final class FindScopeDepths extends NodeVisitor<LexicalContext> {
+
+    private final Compiler compiler;
+    private final CompilationEnvironment env;
+    private final Map<Integer, Map<Integer, RecompilableScriptFunctionData>> fnIdToNestedFunctions = new HashMap<>();
+    private final Map<Integer, Map<String, Integer>> externalSymbolDepths = new HashMap<>();
+
+    FindScopeDepths(final Compiler compiler) {
+        super(new LexicalContext());
+        this.compiler = compiler;
+        this.env = compiler.getCompilationEnvironment();
+    }
+
+    static int findScopesToStart(final LexicalContext lc, final FunctionNode fn, final Block block) {
+        final Block bodyBlock = findBodyBlock(lc, fn, block);
+        final Iterator<Block> iter = lc.getBlocks(block);
+        Block b = iter.next();
+        int scopesToStart = 0;
+        while (true) {
+            if (b.needsScope()) {
+                scopesToStart++;
+            }
+            if (b == bodyBlock) {
+                break;
+            }
+            b = iter.next();
+        }
+        return scopesToStart;
+    }
+
+    static int findInternalDepth(final LexicalContext lc, final FunctionNode fn, final Block block, final Symbol symbol) {
+        final Block bodyBlock = findBodyBlock(lc, fn, block);
+        final Iterator<Block> iter = lc.getBlocks(block);
+        Block b = iter.next();
+        int scopesToStart = 0;
+        while (true) {
+            if (definedInBlock(b, symbol)) {
+                return scopesToStart;
+            }
+            if (b.needsScope()) {
+                scopesToStart++;
+            }
+            if (b == bodyBlock) {
+                break; //don't go past body block, but process it
+            }
+            b = iter.next();
+        }
+        return -1;
+    }
+
+    private static boolean definedInBlock(final Block block, final Symbol symbol) {
+        if (symbol.isGlobal()) {
+            if (block.isGlobalScope()) {
+                return true;
+            }
+            //globals cannot be defined anywhere else
+            return false;
+        }
+        return block.getExistingSymbol(symbol.getName()) == symbol;
+    }
+
+    static Block findBodyBlock(final LexicalContext lc, final FunctionNode fn, final Block block) {
+        final Iterator<Block> iter = lc.getBlocks(block);
+        while (iter.hasNext()) {
+            final Block next = iter.next();
+            if (fn.getBody() == next) {
+                return next;
+            }
+        }
+        return null;
+    }
+
+    private static Block findGlobalBlock(final LexicalContext lc, final FunctionNode fn, final Block block) {
+        final Iterator<Block> iter = lc.getBlocks(block);
+        Block globalBlock = null;
+        while (iter.hasNext()) {
+            globalBlock = iter.next();
+        }
+        return globalBlock;
+    }
+
+    @Override
+    public boolean enterFunctionNode(final FunctionNode functionNode) {
+        if (env.isOnDemandCompilation()) {
+            return true;
+        }
+
+        final int fnId = functionNode.getId();
+        Map<Integer, RecompilableScriptFunctionData> nestedFunctions = fnIdToNestedFunctions.get(fnId);
+        if (nestedFunctions == null) {
+            nestedFunctions = new HashMap<>();
+            fnIdToNestedFunctions.put(fnId, nestedFunctions);
+        }
+
+        return true;
+    }
+
+    //external symbols hold the scope depth of sc11 from global at the start of the method
+    @Override
+    public Node leaveFunctionNode(final FunctionNode functionNode) {
+        final FunctionNode newFunctionNode = functionNode.setState(lc, CompilationState.SCOPE_DEPTHS_COMPUTED);
+        if (env.isOnDemandCompilation()) {
+            final RecompilableScriptFunctionData data = env.getScriptFunctionData(newFunctionNode.getId());
+            assert data != null : newFunctionNode.getName() + " lacks data";
+            return newFunctionNode;
+        }
+
+        //create recompilable scriptfunctiondata
+        final int fnId = newFunctionNode.getId();
+        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(newFunctionNode.countThisProperties());
+        final String      allocatorClassName = Compiler.binaryName(getClassName(fieldCount));
+        final PropertyMap allocatorMap       = PropertyMap.newMap(null, 0, fieldCount, 0);
+        final RecompilableScriptFunctionData data = new RecompilableScriptFunctionData(
+                newFunctionNode,
+                compiler.getCodeInstaller(),
+                allocatorClassName,
+                allocatorMap,
+                nestedFunctions,
+                compiler.getSourceURL(),
+                externalSymbolDepths.get(fnId)
+                );
+
+        if (lc.getOutermostFunction() != newFunctionNode) {
+            final FunctionNode parentFn = lc.getParentFunction(newFunctionNode);
+            if (parentFn != null) {
+                fnIdToNestedFunctions.get(parentFn.getId()).put(fnId, data);
+            }
+        } else {
+            env.setData(data);
+        }
+
+        return newFunctionNode;
+    }
+
+    @Override
+    public boolean enterBlock(final Block block) {
+        if (env.isOnDemandCompilation()) {
+            return true;
+        }
+
+        if (!lc.isFunctionBody()) {
+            return true;
+        }
+
+        //the below part only happens on eager compilation when we have the entire hierarchy
+        //block is a function body
+        final FunctionNode fn = lc.getCurrentFunction();
+
+        //get all symbols that are referenced inside this function body
+        final Set<Symbol> symbols = new HashSet<>();
+        block.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
+            @Override
+            public final boolean enterDefault(final Node node) {
+                if (!env.isOnDemandCompilation()) {
+                    if (node instanceof Expression) {
+                        final Symbol symbol = ((Expression)node).getSymbol();
+                        if (symbol != null && symbol.isScope()) {
+                            //if this is an internal symbol, skip it.
+                            symbols.add(symbol);
+                        }
+                    }
+                }
+                return true;
+            }
+        });
+
+        final Map<String, Integer> internals = new HashMap<>();
+
+        for (final Symbol symbol : symbols) {
+            Iterator<Block> iter;
+
+            final Block globalBlock = findGlobalBlock(lc, fn, block);
+            final Block bodyBlock   = findBodyBlock(lc, fn, block);
+
+            assert globalBlock != null;
+            assert bodyBlock   != null;
+
+            final int internalDepth = findInternalDepth(lc, fn, block, symbol);
+            final boolean internal = internalDepth >= 0;
+            if (internal) {
+                internals.put(symbol.getName(), internalDepth);
+            }
+
+            // if not internal, we have to continue walking until we reach the top. We
+            // start outside the body and each new scope adds a depth count. When we
+            // find the symbol, we store its depth count
+            if (!internal) {
+                int depthAtStart = 0;
+                //not internal - keep looking.
+                iter = lc.getAncestorBlocks(bodyBlock);
+                while (iter.hasNext()) {
+                    final Block b2 = iter.next();
+                    if (definedInBlock(b2, symbol)) {
+                        addExternalSymbol(fn, symbol, depthAtStart);
+                        break;
+                    }
+                    if (b2.needsScope()) {
+                        depthAtStart++;
+                    }
+                }
+            }
+        }
+
+        return true;
+    }
+
+    private void addExternalSymbol(final FunctionNode functionNode, final Symbol symbol, final int depthAtStart) {
+        final int fnId = functionNode.getId();
+        Map<String, Integer> depths = externalSymbolDepths.get(fnId);
+        if (depths == null) {
+            depths = new HashMap<>();
+            externalSymbolDepths.put(fnId, depths);
+        }
+        //System.err.println("PUT " + functionNode.getName() + " " + symbol + " " +depthAtStart);
+        depths.put(symbol.getName(), depthAtStart);
+    }
+}
--- a/src/jdk/nashorn/internal/codegen/MethodEmitter.java	Mon Mar 17 15:05:18 2014 +0100
+++ b/src/jdk/nashorn/internal/codegen/MethodEmitter.java	Thu Mar 20 16:16:42 2014 +0100
@@ -44,6 +44,10 @@
 import static jdk.internal.org.objectweb.asm.Opcodes.IF_ACMPNE;
 import static jdk.internal.org.objectweb.asm.Opcodes.IF_ICMPEQ;
 import static jdk.internal.org.objectweb.asm.Opcodes.IF_ICMPNE;
+import static jdk.internal.org.objectweb.asm.Opcodes.IF_ICMPGE;
+import static jdk.internal.org.objectweb.asm.Opcodes.IF_ICMPGT;
+import static jdk.internal.org.objectweb.asm.Opcodes.IF_ICMPLE;
+import static jdk.internal.org.objectweb.asm.Opcodes.IF_ICMPLT;
 import static jdk.internal.org.objectweb.asm.Opcodes.INSTANCEOF;
 import static jdk.internal.org.objectweb.asm.Opcodes.INVOKEINTERFACE;
 import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESPECIAL;
@@ -1571,6 +1575,16 @@
     }
 
     /**
+     * Generate an if_icmplt
+     *
+     * @param label label to true case
+     */
+    void if_icmplt(final Label label) {
+        debug("if_icmplt", label);
+        jump(IF_ICMPLT, label, 2);
+    }
+
+    /**
      * Generate an ifle
      *
      * @param label label to true case
@@ -1581,6 +1595,16 @@
     }
 
     /**
+     * Generate an if_icmple
+     *
+     * @param label label to true case
+     */
+    void if_icmple(final Label label) {
+        debug("if_icmple", label);
+        jump(IF_ICMPLE, label, 2);
+    }
+
+    /**
      * Generate an ifgt
      *
      * @param label label to true case
@@ -1591,6 +1615,16 @@
     }
 
     /**
+     * Generate an if_icmpgt
+     *
+     * @param label label to true case
+     */
+    void if_icmpgt(final Label label) {
+        debug("if_icmpgt", label);
+        jump(IF_ICMPGT, label, 2);
+    }
+
+    /**
      * Generate an ifge
      *
      * @param label label to true case
@@ -1601,6 +1635,16 @@
     }
 
     /**
+     * Generate an if_icmpge
+     *
+     * @param label label to true case
+     */
+    void if_icmpge(final Label label) {
+        debug("if_icmpge", label);
+        jump(IF_ICMPGE, label, 2);
+    }
+
+    /**
      * Unconditional jump to a label
      *
      * @param label destination label
@@ -1995,7 +2039,7 @@
     }
 
 
-    private static String getProgramPoint(int flags) {
+    private static String getProgramPoint(final int flags) {
         if((flags & CALLSITE_OPTIMISTIC) == 0) {
             return "";
         }
--- a/src/jdk/nashorn/internal/codegen/SharedScopeCall.java	Mon Mar 17 15:05:18 2014 +0100
+++ b/src/jdk/nashorn/internal/codegen/SharedScopeCall.java	Thu Mar 20 16:16:42 2014 +0100
@@ -165,12 +165,10 @@
             method.loadNull();
             int slot = 2;
             for (final Type type : paramTypes) {
-                method.load(type, slot++);
-                if (type == Type.NUMBER || type == Type.LONG) {
-                    slot++;
-                }
+                method.load(type, slot);
+                slot += type.getSlots();
             }
-            // Shared scope calls disabled in optimistic world.
+            // Shared scope calls disabled in optimistic world. TODO is this right?
             method.dynamicCall(returnType, 2 + paramTypes.length, flags);
         }
 
--- a/src/jdk/nashorn/internal/codegen/types/Type.java	Mon Mar 17 15:05:18 2014 +0100
+++ b/src/jdk/nashorn/internal/codegen/types/Type.java	Thu Mar 20 16:16:42 2014 +0100
@@ -58,6 +58,7 @@
 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.ScriptObject;
 import jdk.nashorn.internal.runtime.Undefined;
 import jdk.nashorn.internal.runtime.linker.Bootstrap;
 
@@ -224,7 +225,7 @@
         case jdk.internal.org.objectweb.asm.Type.OBJECT:
             try {
                 return Type.typeFor(Class.forName(itype.getClassName()));
-            } catch(ClassNotFoundException e) {
+            } catch(final ClassNotFoundException e) {
                 throw new AssertionError(e);
             }
         case jdk.internal.org.objectweb.asm.Type.VOID:
@@ -424,7 +425,7 @@
      * @return true if types are equivalent, false otherwise
      */
     public boolean isEquivalentTo(final Type type) {
-        return this.weight() == type.weight() || (isObject() && type.isObject());
+        return this.weight() == type.weight() || isObject() && type.isObject();
     }
 
     /**
@@ -779,6 +780,11 @@
     public static final Type UNDEFINED = putInCache(new ObjectType(Undefined.class));
 
     /**
+     * This is the singleton for ScriptObjects
+     */
+    public static final Type SCRIPT_OBJECT = putInCache(new ObjectType(ScriptObject.class));
+
+    /**
      * This is the singleton for integer arrays
      */
     public static final ArrayType INT_ARRAY = new ArrayType(int[].class) {
--- a/src/jdk/nashorn/internal/ir/Block.java	Mon Mar 17 15:05:18 2014 +0100
+++ b/src/jdk/nashorn/internal/ir/Block.java	Thu Mar 20 16:16:42 2014 +0100
@@ -77,6 +77,12 @@
     public static final int IS_TERMINAL = 1 << 2;
 
     /**
+     * Is this block the eager global scope - i.e. the original program. This isn't true for the
+     * outermost level of recompiles
+     */
+    public static final int IS_GLOBAL_SCOPE = 1 << 3;
+
+    /**
      * Constructor
      *
      * @param token      token
@@ -91,7 +97,7 @@
         this.entryLabel = new Label("block_entry");
         this.breakLabel = new Label("block_break");
         final int len = statements.length;
-        this.flags = (len > 0 && statements[len - 1].hasTerminalFlags()) ? IS_TERMINAL : 0;
+        this.flags = len > 0 && statements[len - 1].hasTerminalFlags() ? IS_TERMINAL : 0;
     }
 
     /**
@@ -116,6 +122,15 @@
     }
 
     /**
+     * Is this block the outermost eager global scope - i.e. the primordial program?
+     * Used for global anchor point for scope depth computation for recompilation code
+     * @return true if outermost eager global scope
+     */
+    public boolean isGlobalScope() {
+        return getFlag(IS_GLOBAL_SCOPE);
+    }
+
+    /**
      * Clear the symbols in a block
      * TODO: make this immutable
      */
@@ -292,7 +307,7 @@
     }
 
     @Override
-    public Block setFlags(final LexicalContext lc, int flags) {
+    public Block setFlags(final LexicalContext lc, final int flags) {
         if (this.flags == flags) {
             return this;
         }
@@ -300,12 +315,12 @@
     }
 
     @Override
-    public Block clearFlag(final LexicalContext lc, int flag) {
+    public Block clearFlag(final LexicalContext lc, final int flag) {
         return setFlags(lc, flags & ~flag);
     }
 
     @Override
-    public Block setFlag(final LexicalContext lc, int flag) {
+    public Block setFlag(final LexicalContext lc, final int flag) {
         return setFlags(lc, flags | flag);
     }
 
@@ -354,7 +369,7 @@
     }
 
     @Override
-    public Node accept(NodeVisitor<? extends LexicalContext> visitor) {
+    public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
         return Acceptor.accept(this, visitor);
     }
 }
--- a/src/jdk/nashorn/internal/ir/FunctionNode.java	Mon Mar 17 15:05:18 2014 +0100
+++ b/src/jdk/nashorn/internal/ir/FunctionNode.java	Thu Mar 20 16:16:42 2014 +0100
@@ -32,6 +32,7 @@
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
+
 import jdk.nashorn.internal.codegen.CompileUnit;
 import jdk.nashorn.internal.codegen.Compiler;
 import jdk.nashorn.internal.codegen.CompilerConstants;
@@ -84,6 +85,8 @@
         SPLIT,
         /** method has had its types finalized */
         FINALIZED,
+        /** computed scope depths for symbols */
+        SCOPE_DEPTHS_COMPUTED,
         /** method has been emitted to bytecode */
         EMITTED
     }
@@ -139,6 +142,7 @@
     /** //@ sourceURL or //# sourceURL for program function nodes */
     private final String sourceURL;
 
+    /** Line number of function start */
     private final int lineNumber;
 
     /** Is anonymous function flag. */
@@ -214,8 +218,11 @@
      *  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;
 
+    /** Used to signify "null", e.g. if someone asks for the parent of the program node */
+    public static final int NO_FUNCTION_ID = 0;
+
     /** Where to start assigning global and unique function node ids */
-    public static final int FIRST_FUNCTION_ID = 1;
+    public static final int FIRST_FUNCTION_ID = NO_FUNCTION_ID + 1;
 
     /** What is the return type of this function? */
     private Type returnType = Type.UNKNOWN;
@@ -275,7 +282,7 @@
         final FunctionNode functionNode,
         final long lastToken,
         final int flags,
-        String sourceURL,
+        final String sourceURL,
         final String name,
         final Type returnType,
         final CompileUnit compileUnit,
@@ -335,7 +342,7 @@
      * @return name for the script source
      */
     public String getSourceName() {
-        return (sourceURL != null)? sourceURL : source.getName();
+        return sourceURL != null? sourceURL : source.getName();
     }
 
     /**
@@ -472,7 +479,7 @@
     }
 
     @Override
-    public FunctionNode setFlags(final LexicalContext lc, int flags) {
+    public FunctionNode setFlags(final LexicalContext lc, final int flags) {
         if (this.flags == flags) {
             return this;
         }
@@ -700,7 +707,6 @@
         return name;
     }
 
-
     /**
      * Set the internal name for this function
      * @param lc    lexical context
@@ -823,7 +829,9 @@
                 type,
                 compileUnit,
                 compilationState,
-                body.setReturnType(type), parameters));
+                body.setReturnType(type),
+                parameters
+                ));
    }
 
     /**
--- a/src/jdk/nashorn/internal/ir/LexicalContext.java	Mon Mar 17 15:05:18 2014 +0100
+++ b/src/jdk/nashorn/internal/ir/LexicalContext.java	Thu Mar 20 16:16:42 2014 +0100
@@ -235,7 +235,7 @@
     public LexicalContextNode replace(final LexicalContextNode oldNode, final LexicalContextNode 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";
+                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";
                 stack[i] = newNode;
                 break;
             }
@@ -269,6 +269,17 @@
         return iter.hasNext() ? iter.next() : null;
     }
 
+    /*
+    public FunctionNode getProgram() {
+        final Iterator<FunctionNode> iter = getFunctions();
+        FunctionNode last = null;
+        while (iter.hasNext()) {
+            last = iter.next();
+        }
+        assert last != null;
+        return last;
+    }*/
+
     /**
      * Returns an iterator over all ancestors block of the given block, with its parent block first.
      * @param block the block whose ancestors are returned
@@ -381,7 +392,7 @@
      * @param symbol symbol
      * @return function node in which this symbol is defined, assert if no such symbol exists in context
      */
-    public FunctionNode getDefiningFunction(Symbol symbol) {
+    public FunctionNode getDefiningFunction(final Symbol symbol) {
         if (symbol.isTemp()) {
             return null;
         }
@@ -392,7 +403,7 @@
                 while (iter.hasNext()) {
                     final LexicalContextNode next2 = iter.next();
                     if (next2 instanceof FunctionNode) {
-                        return ((FunctionNode)next2);
+                        return (FunctionNode)next2;
                     }
                 }
                 throw new AssertionError("Defining block for symbol " + name + " has no function in the context");
@@ -437,11 +448,11 @@
         int n = 0;
         for (final Iterator<LexicalContextNode> iter = getAllNodes(); iter.hasNext();) {
             final LexicalContextNode node = iter.next();
-            if(node == until) {
+            if (node == until) {
                 break;
             }
             assert !(node instanceof FunctionNode); // Can't go outside current function
-            if(node instanceof WithNode || (node instanceof Block && ((Block)node).needsScope())) {
+            if (node instanceof WithNode || node instanceof Block && ((Block)node).needsScope()) {
                 n++;
             }
         }
@@ -618,7 +629,7 @@
             if (next == null) {
                 throw new NoSuchElementException();
             }
-            T lnext = next;
+            final T lnext = next;
             next = findNext();
             return lnext;
         }
--- a/src/jdk/nashorn/internal/ir/Symbol.java	Mon Mar 17 15:05:18 2014 +0100
+++ b/src/jdk/nashorn/internal/ir/Symbol.java	Thu Mar 20 16:16:42 2014 +0100
@@ -31,6 +31,7 @@
 import java.util.HashSet;
 import java.util.Set;
 import java.util.StringTokenizer;
+
 import jdk.nashorn.internal.codegen.types.Range;
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.runtime.Context;
@@ -109,7 +110,7 @@
         if (stacktrace != null) {
             trace = stacktrace; //stacktrace always implies trace as well
             TRACE_SYMBOLS_STACKTRACE = new HashSet<>();
-            for (StringTokenizer st = new StringTokenizer(stacktrace, ","); st.hasMoreTokens(); ) {
+            for (final StringTokenizer st = new StringTokenizer(stacktrace, ","); st.hasMoreTokens(); ) {
                 TRACE_SYMBOLS_STACKTRACE.add(st.nextToken());
             }
         } else {
@@ -119,7 +120,7 @@
 
         if (trace != null) {
             TRACE_SYMBOLS = new HashSet<>();
-            for (StringTokenizer st = new StringTokenizer(trace, ","); st.hasMoreTokens(); ) {
+            for (final StringTokenizer st = new StringTokenizer(trace, ","); st.hasMoreTokens(); ) {
                 TRACE_SYMBOLS.add(st.nextToken());
             }
         } else {
@@ -351,7 +352,7 @@
      * @return true if this is scoped
      */
     public boolean isScope() {
-        assert ((flags & KINDMASK) != IS_GLOBAL) || ((flags & IS_SCOPE) == IS_SCOPE) : "global without scope flag";
+        assert (flags & KINDMASK) != IS_GLOBAL || (flags & IS_SCOPE) == IS_SCOPE : "global without scope flag";
         return (flags & IS_SCOPE) == IS_SCOPE;
     }
 
--- a/src/jdk/nashorn/internal/ir/debug/NashornTextifier.java	Mon Mar 17 15:05:18 2014 +0100
+++ b/src/jdk/nashorn/internal/ir/debug/NashornTextifier.java	Thu Mar 20 16:16:42 2014 +0100
@@ -39,6 +39,7 @@
 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;
@@ -48,6 +49,7 @@
 import jdk.internal.org.objectweb.asm.util.Printer;
 import jdk.internal.org.objectweb.asm.util.TraceSignatureVisitor;
 import jdk.nashorn.internal.runtime.ScriptEnvironment;
+import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
 
 /**
  * Pretty printer for --print-code.
@@ -160,8 +162,8 @@
         }
         if (interfaces != null && interfaces.length > 0) {
             sb.append(" implements ");
-            for (int i = 0; i < interfaces.length; ++i) {
-                appendDescriptor(sb, INTERNAL_NAME, interfaces[i]);
+            for (final String interface1 : interfaces) {
+                appendDescriptor(sb, INTERNAL_NAME, interface1);
                 sb.append(' ');
             }
         }
@@ -222,8 +224,8 @@
             sb.append(tab);
             appendDescriptor(sb, FIELD_SIGNATURE, signature);
 
-            TraceSignatureVisitor sv = new TraceSignatureVisitor(0);
-            SignatureReader r = new SignatureReader(signature);
+            final TraceSignatureVisitor sv = new TraceSignatureVisitor(0);
+            final SignatureReader r = new SignatureReader(signature);
             r.acceptType(sv);
             sb.append(tab).
                 append("// declaration: ").
@@ -249,7 +251,7 @@
         sb.append(";\n");
         addText(sb);
 
-        NashornTextifier t = createNashornTextifier();
+        final NashornTextifier t = createNashornTextifier();
         addText(t.getText());
 
         return t;
@@ -280,12 +282,12 @@
             sb.append(tab);
             appendDescriptor(sb, METHOD_SIGNATURE, signature);
 
-            TraceSignatureVisitor v = new TraceSignatureVisitor(0);
-            SignatureReader r = new SignatureReader(signature);
+            final TraceSignatureVisitor v = new TraceSignatureVisitor(0);
+            final SignatureReader r = new SignatureReader(signature);
             r.accept(v);
-            String genericDecl = v.getDeclaration();
-            String genericReturn = v.getReturnType();
-            String genericExceptions = v.getExceptions();
+            final String genericDecl = v.getDeclaration();
+            final String genericReturn = v.getReturnType();
+            final String genericExceptions = v.getExceptions();
 
             sb.append(tab).
                 append("// declaration: ").
@@ -316,8 +318,8 @@
         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]);
+            for (final String exception : exceptions) {
+                appendDescriptor(sb, INTERNAL_NAME, exception);
                 sb.append(' ');
             }
         }
@@ -325,7 +327,7 @@
         sb.append('\n');
         addText(sb);
 
-        NashornTextifier t = createNashornTextifier();
+        final NashornTextifier t = createNashornTextifier();
         addText(t.getText());
         return t;
     }
@@ -345,7 +347,7 @@
         final StringBuilder sb = new StringBuilder();
         sb.append(tab2).append("// parameter ");
         appendAccess(sb, access);
-        sb.append(' ').append((name == null) ? "<no name>" : name)
+        sb.append(' ').append(name == null ? "<no name>" : name)
                 .append('\n');
         addText(sb);
     }
@@ -393,11 +395,11 @@
     }
 
     private StringBuilder appendOpcode(final StringBuilder sb, final int opcode) {
-        Label next = labelIter == null ? null : labelIter.next();
+        final Label next = labelIter == null ? null : labelIter.next();
         if (next instanceof NashornLabel) {
             final int bci = next.getOffset();
             if (bci != -1) {
-                String bcis = "" + bci;
+                final String bcis = "" + bci;
                 for (int i = 0; i < 5 - bcis.length(); i++) {
                     sb.append(' ');
                 }
@@ -480,7 +482,6 @@
         }
         sb.append(" [");
         appendHandle(sb, bsm);
-        sb.append(" args=");
         if (bsmArgs.length == 0) {
             sb.append("none");
         } else {
@@ -492,12 +493,12 @@
                 } 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;
+                    final int c = (Integer)cst;
+                    final int pp = c >> CALLSITE_PROGRAM_POINT_SHIFT;
                     if (pp != 0) {
-                        sb.append("pp=").append(pp).append(' ');
+                        sb.append(" pp=").append(pp);
                     }
-                    sb.append("0x").append(Integer.toHexString(c & FLAGS_MASK));
+                    sb.append(NashornCallSiteDescriptor.toString(c & FLAGS_MASK));
                 } else {
                     sb.append(cst);
                 }
@@ -539,7 +540,7 @@
     public void visitJumpInsn(final int opcode, final Label label) {
         final StringBuilder sb = new StringBuilder();
         appendOpcode(sb, opcode).append(' ');
-        String to = appendLabel(sb, label);
+        final String to = appendLabel(sb, label);
         sb.append('\n');
         addText(sb);
         checkNoFallThru(opcode, to);
@@ -556,7 +557,7 @@
     public void visitLabel(final Label label) {
         final StringBuilder sb = new StringBuilder();
         sb.append("\n");
-        String name = appendLabel(sb, label);
+        final String name = appendLabel(sb, label);
         sb.append(" [bci=");
         sb.append(label.info);
         sb.append("]");
@@ -689,8 +690,8 @@
             sb.append(tab2);
             appendDescriptor(sb, FIELD_SIGNATURE, signature);
 
-            TraceSignatureVisitor sv = new TraceSignatureVisitor(0);
-            SignatureReader r = new SignatureReader(signature);
+            final TraceSignatureVisitor sv = new TraceSignatureVisitor(0);
+            final SignatureReader r = new SignatureReader(signature);
             r.acceptType(sv);
             sb.append(tab2).append("// declaration: ")
                     .append(sv.getDeclaration()).append('\n');
@@ -718,7 +719,7 @@
 
     private void printToDir(final Graph g) {
         if (env._print_code_dir != null) {
-            File dir = new File(env._print_code_dir);
+            final File dir = new File(env._print_code_dir);
             if (!dir.exists() && !dir.mkdirs()) {
                 throw new RuntimeException(dir.toString());
             }
@@ -726,14 +727,14 @@
             File file;
             int uniqueId = 0;
             do {
-                String fileName = g.getName() + (uniqueId == 0 ? "" : "_" + uniqueId) +  ".dot";
+                final 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) {
+            } catch (final FileNotFoundException e) {
                 throw new RuntimeException(e);
             }
         }
@@ -869,7 +870,7 @@
                 sb.append(' ');
             }
             if (o[i] instanceof String) {
-                String desc = (String) o[i];
+                final String desc = (String) o[i];
                 if (desc.startsWith("[")) {
                     appendDescriptor(sb, FIELD_DESCRIPTOR, desc);
                 } else {
@@ -926,7 +927,7 @@
                 }
             }
         } else {
-            int lastSlash = desc.lastIndexOf('/');
+            final int lastSlash = desc.lastIndexOf('/');
             sb.append(lastSlash == -1 ? desc : desc.substring(lastSlash + 1));
         }
     }
@@ -934,7 +935,7 @@
     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);
+            final char c = s.charAt(i);
             if (c == '\n') {
                 sb.append("\\n");
             } else if (c == '\r') {
@@ -1056,7 +1057,7 @@
         @Override
         public String toString() {
 
-            StringBuilder sb = new StringBuilder();
+            final StringBuilder sb = new StringBuilder();
             sb.append("digraph " + name + " {");
             sb.append("\n");
             sb.append("\tgraph [fontname=courier]\n");
@@ -1083,7 +1084,7 @@
             }
 
             for (final String from : edges.keySet()) {
-                for (String to : edges.get(from)) {
+                for (final String to : edges.get(from)) {
                     sb.append("\t");
                     sb.append(from);
                     sb.append(" -> ");
--- a/src/jdk/nashorn/internal/parser/Parser.java	Mon Mar 17 15:05:18 2014 +0100
+++ b/src/jdk/nashorn/internal/parser/Parser.java	Thu Mar 20 16:16:42 2014 +0100
@@ -61,6 +61,7 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+
 import jdk.internal.dynalink.support.NameCodec;
 import jdk.nashorn.internal.codegen.CompilerConstants;
 import jdk.nashorn.internal.codegen.Namespace;
@@ -270,7 +271,7 @@
             final String end = this + " end '" + scriptName + "'";
             if (Timing.isEnabled()) {
                 Timing.accumulateTime(toString(), System.currentTimeMillis() - t0);
-                LOG.info(end, "' in ", (System.currentTimeMillis() - t0), " ms");
+                LOG.info(end, "' in ", System.currentTimeMillis() - t0, " ms");
             } else {
                 LOG.info(end);
             }
@@ -314,7 +315,7 @@
         try {
             stream = new TokenStream();
             lexer  = new Lexer(source, stream, scripting && !env._no_syntax_extensions);
-            int functionLine = line;
+            final int functionLine = line;
 
             // Set up first token (skips opening EOL.)
             k = -1;
@@ -1076,7 +1077,7 @@
 
         // If is a statement then handle end of line.
         if (isStatement) {
-            boolean semicolon = type == SEMICOLON;
+            final boolean semicolon = type == SEMICOLON;
             endOfLine();
             if (semicolon) {
                 lc.getCurrentBlock().setFinish(finish);
@@ -2663,7 +2664,7 @@
 
     private String getDefaultValidFunctionName(final int functionLine) {
         final String defaultFunctionName = getDefaultFunctionName();
-        return isValidIdentifier(defaultFunctionName) ? defaultFunctionName : (ANON_FUNCTION_PREFIX.symbolName() + functionLine);
+        return isValidIdentifier(defaultFunctionName) ? defaultFunctionName : ANON_FUNCTION_PREFIX.symbolName() + functionLine;
     }
 
     private static boolean isValidIdentifier(String name) {
--- a/src/jdk/nashorn/internal/runtime/CompiledFunction.java	Mon Mar 17 15:05:18 2014 +0100
+++ b/src/jdk/nashorn/internal/runtime/CompiledFunction.java	Thu Mar 20 16:16:42 2014 +0100
@@ -264,7 +264,7 @@
         return betterThanFinal(type(), other.type(), callSiteMethodType);
     }
 
-    static boolean betterThanFinal(final MethodType thisMethodType, MethodType otherMethodType, final MethodType callSiteMethodType) {
+    static boolean betterThanFinal(final MethodType thisMethodType, final MethodType otherMethodType, final MethodType callSiteMethodType) {
         final int thisParamCount = getParamCount(thisMethodType);
         final int otherParamCount = getParamCount(otherMethodType);
         final int callSiteRawParamCount = getParamCount(callSiteMethodType);
@@ -389,7 +389,7 @@
         throw new AssertionError(thisMethodType + " identically applicable to " + otherMethodType + " for " + callSiteMethodType); // Signatures are identical
     }
 
-    private static Type[] toTypeWithoutCallee(final MethodType type, int thisIndex) {
+    private static Type[] toTypeWithoutCallee(final MethodType type, final int thisIndex) {
         final int paramCount = type.parameterCount();
         final Type[] t = new Type[paramCount - thisIndex];
         for(int i = thisIndex; i < paramCount; ++i) {
@@ -398,7 +398,7 @@
         return t;
     }
 
-    private static Type getParamType(int i, Type[] paramTypes, boolean isVarArg) {
+    private static Type getParamType(final int i, final Type[] paramTypes, final boolean isVarArg) {
         final int fixParamCount = paramTypes.length - (isVarArg ? 1 : 0);
         if(i < fixParamCount) {
             return paramTypes[i];
@@ -424,8 +424,8 @@
         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
+        final int fnParamCountNoCallee = fnParamCount - thisThisIndex;
+        final 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));
@@ -464,7 +464,7 @@
         return optimismInfo != null;
     }
 
-    private MethodHandle createComposableInvoker(boolean isConstructor) {
+    private MethodHandle createComposableInvoker(final 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
@@ -498,7 +498,7 @@
         cs.setTarget(target);
     }
 
-    private MethodHandle getInvokerOrConstructor(boolean selectCtor) {
+    private MethodHandle getInvokerOrConstructor(final boolean selectCtor) {
         return selectCtor ? getConstructor() : createInvokerForPessimisticCaller();
     }
 
@@ -535,7 +535,7 @@
         return MH.foldArguments(RESTOF_INVOKER, MH.insertArguments(HANDLE_REWRITE_EXCEPTION, 0, this, optimismInfo));
     }
 
-    private static MethodHandle changeReturnType(MethodHandle mh, Class<?> newReturnType) {
+    private static MethodHandle changeReturnType(final MethodHandle mh, final Class<?> newReturnType) {
         return Bootstrap.getLinkerServices().asType(mh, mh.type().changeReturnType(newReturnType));
     }
 
@@ -571,7 +571,9 @@
             //is recompiled
             assert optimismInfo == oldOptimismInfo;
             isOptimistic = fn.isOptimistic();
-            LOG.info("Recompiled '", fn.getName(), "' (", Debug.id(this), ")", isOptimistic ? " remains optimistic." : " is no longer optimistic.");
+            if (LOG.isEnabled()) {
+                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
--- a/src/jdk/nashorn/internal/runtime/Context.java	Mon Mar 17 15:05:18 2014 +0100
+++ b/src/jdk/nashorn/internal/runtime/Context.java	Thu Mar 20 16:16:42 2014 +0100
@@ -197,7 +197,7 @@
      */
     public static PrintWriter getCurrentErr() {
         final ScriptObject global = getGlobalTrusted();
-        return (global != null)? global.getContext().getErr() : new PrintWriter(System.err);
+        return global != null ? global.getContext().getErr() : new PrintWriter(System.err);
     }
 
     /**
@@ -423,7 +423,7 @@
      * @return the return value of the {@code eval}
      */
     public Object eval(final ScriptObject initialScope, final String string, final Object callThis, final Object location, final boolean strict) {
-        final String  file       = (location == UNDEFINED || location == null) ? "<eval>" : location.toString();
+        final String  file       = location == UNDEFINED || location == null ? "<eval>" : location.toString();
         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();
@@ -468,10 +468,10 @@
             scope = strictEvalScope;
         }
 
-        ScriptFunction func = getProgramFunction(clazz, scope);
+        final ScriptFunction func = getProgramFunction(clazz, scope);
         Object evalThis;
         if (directEval) {
-            evalThis = (callThis instanceof ScriptObject || strictFlag) ? callThis : global;
+            evalThis = callThis instanceof ScriptObject || strictFlag ? callThis : global;
         } else {
             evalThis = global;
         }
@@ -490,7 +490,7 @@
                         public Source run() {
                             try {
                                 final URL resURL = Context.class.getResource(resource);
-                                return (resURL != null)? new Source(srcStr, resURL) : null;
+                                return resURL != null ? new Source(srcStr, resURL) : null;
                             } catch (final IOException exp) {
                                 return null;
                             }
@@ -513,7 +513,7 @@
      * @throws IOException if source cannot be found or loaded
      */
     public Object load(final ScriptObject scope, final Object from) throws IOException {
-        final Object src = (from instanceof ConsString)?  from.toString() : from;
+        final Object src = from instanceof ConsString ? from.toString() : from;
         Source source = null;
 
         // load accepts a String (which could be a URL or a file name), a File, a URL
@@ -521,8 +521,8 @@
         if (src instanceof String) {
             final String srcStr = (String)src;
             if (srcStr.startsWith(LOAD_CLASSPATH)) {
-                URL url = getResourceURL(srcStr.substring(LOAD_CLASSPATH.length()));
-                source = (url != null)? new Source(url.toString(), url) : null;
+                final URL url = getResourceURL(srcStr.substring(LOAD_CLASSPATH.length()));
+                source = url != null ? new Source(url.toString(), url) : null;
             } else {
                 final File file = new File(srcStr);
                 if (srcStr.indexOf(':') != -1) {
@@ -837,7 +837,7 @@
     /**
      * Set the current global scope
      */
-    static void setGlobalTrusted(ScriptObject global) {
+    static void setGlobalTrusted(final ScriptObject global) {
          currentGlobal.set(global);
     }
 
--- a/src/jdk/nashorn/internal/runtime/Debug.java	Mon Mar 17 15:05:18 2014 +0100
+++ b/src/jdk/nashorn/internal/runtime/Debug.java	Thu Mar 20 16:16:42 2014 +0100
@@ -26,7 +26,6 @@
 package jdk.nashorn.internal.runtime;
 
 import static jdk.nashorn.internal.parser.TokenType.EOF;
-
 import jdk.nashorn.internal.parser.Lexer;
 import jdk.nashorn.internal.parser.Token;
 import jdk.nashorn.internal.parser.TokenStream;
@@ -71,7 +70,7 @@
      * @return system identity hashcode as string
      */
     public static String id(final Object x) {
-        return "0x" + Integer.toHexString(System.identityHashCode(x));
+        return String.format("0x%08x", System.identityHashCode(x));
     }
 
     /**
--- a/src/jdk/nashorn/internal/runtime/Property.java	Mon Mar 17 15:05:18 2014 +0100
+++ b/src/jdk/nashorn/internal/runtime/Property.java	Thu Mar 20 16:16:42 2014 +0100
@@ -563,24 +563,33 @@
         return sb.toString();
     }
 
+    private static String indent(final String str, final int indent) {
+        final StringBuilder sb = new StringBuilder();
+        sb.append(str);
+        for (int i = 0; i < indent - str.length(); i++) {
+            sb.append(' ');
+        }
+        return sb.toString();
+     }
+
     @Override
     public String toString() {
         final StringBuilder sb   = new StringBuilder();
         final Class<?>      type = getCurrentType();
 
-        sb.append(getKey()).
+        sb.append(indent(getKey(), 20)).
             append(" id=").
             append(Debug.id(this)).
             append(" (0x").
-            append(Integer.toHexString(flags)).
+            append(indent(Integer.toHexString(flags), 4)).
             append(") ").
             append(getClass().getSimpleName()).
             append(" {").
-            append(type(type)).
+            append(indent(type(type), 5)).
             append('}');
 
         if (slot != -1) {
-            sb.append('[').
+            sb.append(" [").
                append("slot=").
                append(slot).
                append(']');
--- a/src/jdk/nashorn/internal/runtime/PropertyDescriptor.java	Mon Mar 17 15:05:18 2014 +0100
+++ b/src/jdk/nashorn/internal/runtime/PropertyDescriptor.java	Thu Mar 20 16:16:42 2014 +0100
@@ -154,8 +154,8 @@
 
     /**
      * Check existence and compare attributes of descriptors.
-     *
-     * @return true if every field of this desc exists in otherDesc and has the same value.
+     * @param otherDesc other descriptor to compare to
+     * @return true if every field of this descriptor exists in otherDesc and has the same value.
      */
     public boolean hasAndEquals(PropertyDescriptor otherDesc);
 }
--- a/src/jdk/nashorn/internal/runtime/PropertyMap.java	Mon Mar 17 15:05:18 2014 +0100
+++ b/src/jdk/nashorn/internal/runtime/PropertyMap.java	Thu Mar 20 16:16:42 2014 +0100
@@ -31,11 +31,15 @@
 
 import java.lang.invoke.SwitchPoint;
 import java.lang.ref.WeakReference;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.WeakHashMap;
@@ -698,19 +702,16 @@
     public String toString() {
         final StringBuilder sb = new StringBuilder();
 
-        sb.append(" [");
-        boolean isFirst = true;
+        sb.append(Debug.id(this));
+        sb.append(" = {\n");
 
-        for (final Property property : properties.values()) {
-            if (!isFirst) {
-                sb.append(", ");
-            }
-
-            isFirst = false;
+        for (final Property property : plist) {
+            sb.append('\t');
             sb.append(property);
+            sb.append('\n');
         }
 
-        sb.append(']');
+        sb.append('}');
 
         return sb.toString();
     }
--- a/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java	Mon Mar 17 15:05:18 2014 +0100
+++ b/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java	Thu Mar 20 16:16:42 2014 +0100
@@ -36,6 +36,7 @@
 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;
@@ -96,7 +97,10 @@
     /** lazily generated allocator */
     private MethodHandle allocator;
 
-    private Map<Integer, RecompilableScriptFunctionData> nestedFunctions;
+    private final Map<Integer, RecompilableScriptFunctionData> nestedFunctions;
+
+    /** Id to parent function if one exists */
+    private RecompilableScriptFunctionData parent;
 
     private final boolean isDeclared;
     private final boolean isAnonymous;
@@ -109,16 +113,20 @@
 
     private static final DebugLogger LOG = new DebugLogger("recompile");
 
+    private final Map<String, Integer> externalScopeDepths;
+
+    private static final int GET_SET_PREFIX_LENGTH = "*et ".length();
 
     /**
      * Constructor - public as scripts use it
      *
-     * @param functionNode       functionNode that represents this function code
-     * @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
-     * @param sourceURL          source URL
+     * @param functionNode        functionNode that represents this function code
+     * @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
+     * @param sourceURL           source URL
+     * @param externalScopeDepths external scope depths
      */
     public RecompilableScriptFunctionData(
         final FunctionNode functionNode,
@@ -126,7 +134,9 @@
         final String allocatorClassName,
         final PropertyMap allocatorMap,
         final Map<Integer, RecompilableScriptFunctionData> nestedFunctions,
-        final String sourceURL) {
+        final String sourceURL,
+        final Map<String, Integer> externalScopeDepths) {
+
         super(functionName(functionNode),
               Math.min(functionNode.getParameters().size(), MAX_ARITY),
               functionNode.isStrict(),
@@ -134,19 +144,57 @@
               true,
               functionNode.isVarArg());
 
-        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;
+        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;//deepTraverse(nestedFunctions);
+        this.externalScopeDepths = externalScopeDepths;
+
+        for (final RecompilableScriptFunctionData nfn : nestedFunctions.values()) {
+            assert nfn.getParent() == null;
+            nfn.setParent(this);
+        }
+    }
+
+    /**
+     * Return the external symbol table
+     * @param symbolName symbol name
+     * @return the external symbol table with proto depths
+     */
+    public int getExternalSymbolDepth(final String symbolName) {
+        final Map<String, Integer> map = externalScopeDepths;
+        if (map == null) {
+            return -1;
+        }
+        final Integer depth = map.get(symbolName);
+        if (depth == null) {
+            return -1;
+        }
+        return depth;
+    }
+
+    /**
+     * Get the parent of this RecompilableScriptFunctionData. If we are
+     * a nested function, we have a parent. Note that "null" return value
+     * can also mean that we have a parent but it is unknown, so this can
+     * only be used for conservative assumptions.
+     * @return parent data, or null if non exists and also null IF UNKNOWN.
+     */
+    public RecompilableScriptFunctionData getParent() {
+       return parent;
+    }
+
+    void setParent(final RecompilableScriptFunctionData parent) {
+        this.parent = parent;
     }
 
     @Override
@@ -162,6 +210,8 @@
     public String toString() {
         final StringBuilder sb = new StringBuilder();
 
+        sb.append("fid=").append(functionNodeId).append(' ');
+
         if (source != null) {
             sb.append(source.getName())
                 .append(':')
@@ -179,7 +229,7 @@
         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 name.substring(GET_SET_PREFIX_LENGTH);
         }
         return fn.getIdent().getName();
     }
@@ -265,7 +315,8 @@
                     this,
                     isVariableArity() ? null : new ParamTypeMap(functionNodeId, explicitParams(fnCallSiteType)),
                     invalidatedProgramPoints,
-                    continuationEntryPoints
+                    continuationEntryPoints,
+                    true
                     ),
                 installer);
 
@@ -276,7 +327,7 @@
         return lookupWithExplicitType(fn, MethodType.methodType(fn.getReturnType().getTypeClass(), RewriteException.class));
     }
 
-    private FunctionNode compileTypeSpecialization(MethodType actualCallSiteType) {
+    private FunctionNode compileTypeSpecialization(final MethodType actualCallSiteType) {
         return compile(actualCallSiteType, null, "Type specialized compilation");
     }
 
@@ -297,7 +348,8 @@
                     new ParamTypeMap(
                         functionNodeId,
                         explicitParams(fnCallSiteType)),
-                invalidatedProgramPoints),
+                invalidatedProgramPoints,
+                true),
             installer);
 
         fn = compiler.compile(scriptName, fn);
@@ -377,7 +429,7 @@
     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();
+            throw new IllegalStateException(functionNode.getName() + " id=" + functionNode.getId());
         }
         addCode(functionNode);
     }
@@ -409,7 +461,7 @@
 
         final MethodHandle handle = lookup(fn);
         final MethodType fromType = handle.type();
-        MethodType toType = (needsCallee(fromType) ? callSiteType.changeParameterType(0, ScriptFunction.class) : callSiteType.dropParameterTypes(0, 1));
+        MethodType toType = needsCallee(fromType) ? callSiteType.changeParameterType(0, ScriptFunction.class) : callSiteType.dropParameterTypes(0, 1);
         toType = toType.changeReturnType(fromType.returnType());
 
         final int toCount = toType.parameterCount();
@@ -436,7 +488,7 @@
     }
 
     @Override
-    CompiledFunction getBest(MethodType callSiteType) {
+    CompiledFunction getBest(final MethodType callSiteType) {
         synchronized(code) {
             final CompiledFunction existingBest = super.getBest(callSiteType);
             // TODO: what if callSiteType is vararg?
@@ -465,17 +517,28 @@
 
     /**
      * Return a script function data based on a function id, either this function if
-     * the id matches or a nested function based on functionId.
+     * the id matches or a nested function based on functionId. This goes down into
+     * nested functions until all leaves are exhausted.
+     *
      * @param functionId function id
      * @return script function data or null if invalid id
      */
     public RecompilableScriptFunctionData getScriptFunctionData(final int functionId) {
-        if(functionId == functionNodeId) {
+        if (functionId == functionNodeId) {
             return this;
         }
-        if(nestedFunctions == null) {
-            return null;
+        RecompilableScriptFunctionData data;
+
+        data = nestedFunctions == null ? null : nestedFunctions.get(functionId);
+        if (data != null) {
+            return data;
         }
-        return nestedFunctions.get(functionId);
+        for (final RecompilableScriptFunctionData ndata : nestedFunctions.values()) {
+            data = ndata.getScriptFunctionData(functionId);
+            if (data != null) {
+                return data;
+            }
+        }
+        return null;
     }
 }
--- a/src/jdk/nashorn/internal/runtime/ScriptFunction.java	Mon Mar 17 15:05:18 2014 +0100
+++ b/src/jdk/nashorn/internal/runtime/ScriptFunction.java	Thu Mar 20 16:16:42 2014 +0100
@@ -198,7 +198,7 @@
         return data.needsWrappedThis();
     }
 
-    private static boolean needsWrappedThis(Object fn) {
+    private static boolean needsWrappedThis(final Object fn) {
         return fn instanceof ScriptFunction ? ((ScriptFunction)fn).needsWrappedThis() : false;
     }
 
--- a/src/jdk/nashorn/internal/runtime/ScriptObject.java	Mon Mar 17 15:05:18 2014 +0100
+++ b/src/jdk/nashorn/internal/runtime/ScriptObject.java	Thu Mar 20 16:16:42 2014 +0100
@@ -163,6 +163,9 @@
     /** Method handle for getting the array data */
     public static final Call GET_ARRAY          = virtualCall(MethodHandles.lookup(), ScriptObject.class, "getArray", ArrayData.class);
 
+    /** Method handle for getting the property map - debugging purposes */
+    public static final Call GET_MAP            = virtualCall(MethodHandles.lookup(), ScriptObject.class, "getMap", PropertyMap.class);
+
     /** Method handle for setting the array data */
     public static final Call SET_ARRAY          = virtualCall(MethodHandles.lookup(), ScriptObject.class, "setArray", void.class, ArrayData.class);
 
@@ -175,6 +178,9 @@
     /** Method handle for getting the proto of a ScriptObject */
     public static final Call GET_PROTO          = virtualCallNoLookup(ScriptObject.class, "getProto", ScriptObject.class);
 
+    /** Method handle for getting the proto of a ScriptObject */
+    public static final Call GET_PROTO_DEPTH    = virtualCallNoLookup(ScriptObject.class, "getProto", ScriptObject.class, int.class);
+
     /** Method handle for setting the proto of a ScriptObject */
     public static final Call SET_PROTO          = virtualCallNoLookup(ScriptObject.class, "setProto", void.class, ScriptObject.class);
 
@@ -1213,6 +1219,20 @@
     }
 
     /**
+     * Get the proto of a specific depth
+     * @param n depth
+     * @return proto at given depth
+     */
+    public final ScriptObject getProto(final int n) {
+        assert n > 0;
+        ScriptObject p = getProto();
+        for (int i = n; i-- > 0;) {
+            p = p.getProto();
+        }
+        return p;
+    }
+
+    /**
      * Set the __proto__ of an object.
      * @param newProto new __proto__ to set.
      */
@@ -1901,9 +1921,9 @@
                 UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
 
         mh = find.getGetter(returnType, programPoint);
+
         //we never need a guard if noGuard is set
-        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 boolean noGuard = OBJECT_FIELDS_ONLY && NashornCallSiteDescriptor.isFastScope(desc) && !property.canChangeType();
         MethodHandle guard;
         final Class<? extends Throwable> exception;
         if (noGuard) {
@@ -1919,7 +1939,8 @@
         if (mh == null) {
             mh = Lookup.emptyGetter(returnType);
         } else {
-            assert mh.type().returnType().equals(returnType) : "returntype mismatch for getter " + mh.type().returnType() + " != " + returnType;
+             assert mh.type().returnType().equals(returnType) : "returntype mismatch for getter " + mh.type().returnType() + " != " + returnType;
+
              if (find.isSelf()) {
                 return new GuardedInvocation(mh, guard, null, exception);
              }
--- a/src/jdk/nashorn/internal/runtime/ScriptRuntime.java	Mon Mar 17 15:05:18 2014 +0100
+++ b/src/jdk/nashorn/internal/runtime/ScriptRuntime.java	Thu Mar 20 16:16:42 2014 +0100
@@ -269,7 +269,7 @@
         private final int length;
         private int index;
 
-        RangeIterator(int length) {
+        RangeIterator(final int length) {
             this.length = length;
         }
 
@@ -427,7 +427,7 @@
             }
 
             // checking for xVal == -0.0 and yVal == +0.0 or vice versa
-            if (xVal == 0.0 && (Double.doubleToLongBits(xVal) != Double.doubleToLongBits(yVal))) {
+            if (xVal == 0.0 && Double.doubleToLongBits(xVal) != Double.doubleToLongBits(yVal)) {
                 return false;
             }
 
@@ -438,7 +438,7 @@
             return x.equals(y);
         }
 
-        return (x == y);
+        return x == y;
     }
 
     /**
@@ -510,7 +510,7 @@
         final boolean xIsUndefined = x == UNDEFINED;
         final boolean yIsUndefined = y == UNDEFINED;
 
-        if ((xIsNumber && yIsUndefined) || (xIsUndefined && yIsNumber) || (xIsUndefined && yIsUndefined)) {
+        if (xIsNumber && yIsUndefined || xIsUndefined && yIsNumber || xIsUndefined && yIsUndefined) {
             return Double.NaN;
         }
 
@@ -713,8 +713,8 @@
             return x == y;
         }
 
-        if ((xType == JSType.UNDEFINED && yType == JSType.NULL) ||
-            (xType == JSType.NULL && yType == JSType.UNDEFINED)) {
+        if (xType == JSType.UNDEFINED && yType == JSType.NULL ||
+            xType == JSType.NULL && yType == JSType.UNDEFINED) {
             return true;
         }
 
@@ -735,11 +735,11 @@
         }
 
         if ((xType == JSType.STRING || xType == JSType.NUMBER) &&
-             (y instanceof ScriptObject))  {
+             y instanceof ScriptObject)  {
             return EQ(x, JSType.toPrimitive(y));
         }
 
-        if ((x instanceof ScriptObject) &&
+        if (x instanceof ScriptObject &&
             (yType == JSType.STRING || yType == JSType.NUMBER)) {
             return EQ(JSType.toPrimitive(x), y);
         }
@@ -876,7 +876,7 @@
      */
     public static boolean LT(final Object x, final Object y) {
         final Object value = lessThan(x, y, true);
-        return (value == UNDEFINED) ? false : (Boolean)value;
+        return value == UNDEFINED ? false : (Boolean)value;
     }
 
     /**
@@ -889,7 +889,7 @@
      */
     public static boolean GT(final Object x, final Object y) {
         final Object value = lessThan(y, x, false);
-        return (value == UNDEFINED) ? false : (Boolean)value;
+        return value == UNDEFINED ? false : (Boolean)value;
     }
 
     /**
@@ -902,7 +902,7 @@
      */
     public static boolean LE(final Object x, final Object y) {
         final Object value = lessThan(y, x, false);
-        return (!(Boolean.TRUE.equals(value) || value == UNDEFINED));
+        return !(Boolean.TRUE.equals(value) || value == UNDEFINED);
     }
 
     /**
@@ -915,7 +915,7 @@
      */
     public static boolean GE(final Object x, final Object y) {
         final Object value = lessThan(x, y, true);
-        return (!(Boolean.TRUE.equals(value) || value == UNDEFINED));
+        return !(Boolean.TRUE.equals(value) || value == UNDEFINED);
     }
 
     /** ECMA 11.8.5 The Abstract Relational Comparison Algorithm */
@@ -933,7 +933,7 @@
 
         if (JSType.of(px) == JSType.STRING && JSType.of(py) == JSType.STRING) {
             // May be String or ConsString
-            return (px.toString()).compareTo(py.toString()) < 0;
+            return px.toString().compareTo(py.toString()) < 0;
         }
 
         final double nx = JSType.toNumber(px);
--- a/src/jdk/nashorn/internal/runtime/arrays/ByteBufferArrayData.java	Mon Mar 17 15:05:18 2014 +0100
+++ b/src/jdk/nashorn/internal/runtime/arrays/ByteBufferArrayData.java	Thu Mar 20 16:16:42 2014 +0100
@@ -60,6 +60,7 @@
      *
      * @return property descriptor for element
      */
+    @Override
     public PropertyDescriptor getDescriptor(final GlobalObject global, final int index) {
         // make the index properties not configurable
         return global.newDataDescriptor(getObject(index), false, true, true);
@@ -197,7 +198,7 @@
         throw unsupported("convert");
     }
 
-    private UnsupportedOperationException unsupported(final String method) {
+    private static UnsupportedOperationException unsupported(final String method) {
         return new UnsupportedOperationException(method);
     }
 }
--- a/src/jdk/nashorn/internal/runtime/linker/NashornCallSiteDescriptor.java	Mon Mar 17 15:05:18 2014 +0100
+++ b/src/jdk/nashorn/internal/runtime/linker/NashornCallSiteDescriptor.java	Thu Mar 20 16:16:42 2014 +0100
@@ -86,7 +86,7 @@
     /**
      * 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;
+    public static final int MAX_PROGRAM_POINT_VALUE = (1 << 32 - CALLSITE_PROGRAM_POINT_SHIFT) - 1;
 
     /**
      * Flag mask to get the program point flags
@@ -107,6 +107,22 @@
     private final MethodType methodType;
     private final int flags;
 
+    public static String toString(final int flags) {
+        final StringBuilder sb = new StringBuilder();
+        if ((flags & CALLSITE_SCOPE) != 0) {
+            if ((flags & CALLSITE_FAST_SCOPE) != 0) {
+                sb.append("fastscope ");
+            } else {
+                assert (flags & CALLSITE_FAST_SCOPE) == 0 : "can't be fastscope without scope";
+                sb.append("scope ");
+            }
+        }
+        if ((flags & CALLSITE_STRICT) != 0) {
+            sb.append("strict ");
+        }
+        return sb.length() == 0 ? "" : " " + sb.toString().trim();
+    }
+
     /**
      * Retrieves a Nashorn call site descriptor with the specified values. Since call site descriptors are immutable
      * this method is at liberty to retrieve canonicalized instances (although it is not guaranteed it will do so).