8010652: Eliminate non-child references in Block/FunctionNode, and make few node types immutable
authorattila
Sat, 23 Mar 2013 00:58:39 +0100
changeset 16530 201d682e75f4
parent 16529 fb3208bbd5dc
child 16531 8f1b0de50d07
8010652: Eliminate non-child references in Block/FunctionNode, and make few node types immutable Reviewed-by: jlaskey, lagergren
nashorn/make/project.properties
nashorn/src/jdk/nashorn/internal/codegen/Attr.java
nashorn/src/jdk/nashorn/internal/codegen/ClassEmitter.java
nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java
nashorn/src/jdk/nashorn/internal/codegen/CompilationPhase.java
nashorn/src/jdk/nashorn/internal/codegen/Compiler.java
nashorn/src/jdk/nashorn/internal/codegen/FinalizeTypes.java
nashorn/src/jdk/nashorn/internal/codegen/FoldConstants.java
nashorn/src/jdk/nashorn/internal/codegen/FunctionSignature.java
nashorn/src/jdk/nashorn/internal/codegen/Lower.java
nashorn/src/jdk/nashorn/internal/codegen/MethodEmitter.java
nashorn/src/jdk/nashorn/internal/codegen/Splitter.java
nashorn/src/jdk/nashorn/internal/codegen/WeighNodes.java
nashorn/src/jdk/nashorn/internal/ir/AccessNode.java
nashorn/src/jdk/nashorn/internal/ir/Assignment.java
nashorn/src/jdk/nashorn/internal/ir/BaseNode.java
nashorn/src/jdk/nashorn/internal/ir/BinaryNode.java
nashorn/src/jdk/nashorn/internal/ir/Block.java
nashorn/src/jdk/nashorn/internal/ir/BreakNode.java
nashorn/src/jdk/nashorn/internal/ir/CallNode.java
nashorn/src/jdk/nashorn/internal/ir/CaseNode.java
nashorn/src/jdk/nashorn/internal/ir/CatchNode.java
nashorn/src/jdk/nashorn/internal/ir/ContinueNode.java
nashorn/src/jdk/nashorn/internal/ir/DoWhileNode.java
nashorn/src/jdk/nashorn/internal/ir/EmptyNode.java
nashorn/src/jdk/nashorn/internal/ir/ExecuteNode.java
nashorn/src/jdk/nashorn/internal/ir/ForNode.java
nashorn/src/jdk/nashorn/internal/ir/FunctionNode.java
nashorn/src/jdk/nashorn/internal/ir/IdentNode.java
nashorn/src/jdk/nashorn/internal/ir/IfNode.java
nashorn/src/jdk/nashorn/internal/ir/IndexNode.java
nashorn/src/jdk/nashorn/internal/ir/LabelNode.java
nashorn/src/jdk/nashorn/internal/ir/LexicalContext.java
nashorn/src/jdk/nashorn/internal/ir/LineNumberNode.java
nashorn/src/jdk/nashorn/internal/ir/LiteralNode.java
nashorn/src/jdk/nashorn/internal/ir/Location.java
nashorn/src/jdk/nashorn/internal/ir/Node.java
nashorn/src/jdk/nashorn/internal/ir/ObjectNode.java
nashorn/src/jdk/nashorn/internal/ir/PropertyNode.java
nashorn/src/jdk/nashorn/internal/ir/ReferenceNode.java
nashorn/src/jdk/nashorn/internal/ir/ReturnNode.java
nashorn/src/jdk/nashorn/internal/ir/RuntimeNode.java
nashorn/src/jdk/nashorn/internal/ir/SplitNode.java
nashorn/src/jdk/nashorn/internal/ir/SwitchNode.java
nashorn/src/jdk/nashorn/internal/ir/Symbol.java
nashorn/src/jdk/nashorn/internal/ir/TernaryNode.java
nashorn/src/jdk/nashorn/internal/ir/ThrowNode.java
nashorn/src/jdk/nashorn/internal/ir/TryNode.java
nashorn/src/jdk/nashorn/internal/ir/TypeOverride.java
nashorn/src/jdk/nashorn/internal/ir/UnaryNode.java
nashorn/src/jdk/nashorn/internal/ir/VarNode.java
nashorn/src/jdk/nashorn/internal/ir/WhileNode.java
nashorn/src/jdk/nashorn/internal/ir/WithNode.java
nashorn/src/jdk/nashorn/internal/ir/debug/JSONWriter.java
nashorn/src/jdk/nashorn/internal/ir/debug/PrintVisitor.java
nashorn/src/jdk/nashorn/internal/ir/visitor/NodeOperatorVisitor.java
nashorn/src/jdk/nashorn/internal/ir/visitor/NodeVisitor.java
nashorn/src/jdk/nashorn/internal/parser/Parser.java
nashorn/src/jdk/nashorn/internal/runtime/Context.java
nashorn/src/jdk/nashorn/internal/runtime/resources/Messages.properties
nashorn/test/script/basic/JDK-8006755.js
nashorn/test/script/basic/NASHORN-837.js
nashorn/test/src/jdk/nashorn/internal/codegen/CompilerTest.java
--- a/nashorn/make/project.properties	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/make/project.properties	Sat Mar 23 00:58:39 2013 +0100
@@ -210,7 +210,7 @@
 # add '-Dtest.js.outofprocess' to run each test in a new sub-process
 run.test.jvmargs.main=-server -Xmx${run.test.xmx} -XX:-TieredCompilation -esa -ea -Dnashorn.debug=true -Dfile.encoding=UTF-8
 #-XX:+HeapDumpOnOutOfMemoryError -XX:-UseCompressedKlassPointers -XX:+PrintHeapAtGC -XX:ClassMetaspaceSize=300M  
-run.test.jvmargs.octane.main=-Xms${run.test.xms} ${run.test.jvmargs}
+run.test.jvmargs.octane.main=-Xms${run.test.xms} ${run.test.jvmargs.main}
 
 run.test.jvmsecurityargs=-Xverify:all -Djava.security.properties=${basedir}/make/java.security.override -Djava.security.manager -Djava.security.policy=${basedir}/build/nashorn.policy
 
--- a/nashorn/src/jdk/nashorn/internal/codegen/Attr.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Attr.java	Sat Mar 23 00:58:39 2013 +0100
@@ -37,14 +37,16 @@
 import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL;
 import static jdk.nashorn.internal.ir.Symbol.IS_LET;
 import static jdk.nashorn.internal.ir.Symbol.IS_PARAM;
+import static jdk.nashorn.internal.ir.Symbol.IS_SCOPE;
 import static jdk.nashorn.internal.ir.Symbol.IS_THIS;
 import static jdk.nashorn.internal.ir.Symbol.IS_VAR;
+import static jdk.nashorn.internal.ir.Symbol.KINDMASK;
 
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Iterator;
-import java.util.LinkedList;
 import java.util.List;
+import java.util.ListIterator;
 import java.util.Set;
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.AccessNode;
@@ -52,19 +54,19 @@
 import jdk.nashorn.internal.ir.Block;
 import jdk.nashorn.internal.ir.CallNode;
 import jdk.nashorn.internal.ir.CallNode.EvalArgs;
-import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
 import jdk.nashorn.internal.ir.CaseNode;
 import jdk.nashorn.internal.ir.CatchNode;
 import jdk.nashorn.internal.ir.ForNode;
 import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
 import jdk.nashorn.internal.ir.IdentNode;
 import jdk.nashorn.internal.ir.IndexNode;
+import jdk.nashorn.internal.ir.LexicalContext;
 import jdk.nashorn.internal.ir.LiteralNode;
 import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
 import jdk.nashorn.internal.ir.Node;
 import jdk.nashorn.internal.ir.ObjectNode;
 import jdk.nashorn.internal.ir.PropertyNode;
-import jdk.nashorn.internal.ir.ReferenceNode;
 import jdk.nashorn.internal.ir.ReturnNode;
 import jdk.nashorn.internal.ir.RuntimeNode;
 import jdk.nashorn.internal.ir.RuntimeNode.Request;
@@ -117,6 +119,8 @@
      */
     private Set<String> localUses;
 
+    private final LexicalContext lexicalContext = new LexicalContext();
+
     private static final DebugLogger LOG   = new DebugLogger("attr");
     private static final boolean     DEBUG = LOG.isEnabled();
 
@@ -137,14 +141,15 @@
     }
 
     @Override
-    public Node leave(final AccessNode accessNode) {
+    public Node leaveAccessNode(final AccessNode accessNode) {
         newTemporary(Type.OBJECT, accessNode);  //While Object type is assigned here, Access Specialization in FinalizeTypes may narrow this
         end(accessNode);
         return accessNode;
     }
 
     @Override
-    public Node enter(final Block block) {
+    public Node enterBlock(final Block block) {
+        lexicalContext.push(block);
         start(block);
 
         final Set<String> savedLocalDefs = localDefs;
@@ -160,9 +165,7 @@
             localDefs = new HashSet<>(savedLocalDefs);
             localUses = new HashSet<>(savedLocalUses);
 
-            for (final Node statement : block.getStatements()) {
-                statement.accept(this);
-            }
+            block.visitStatements(this);
         } finally {
             localDefs = savedLocalDefs;
             localUses = savedLocalUses;
@@ -172,11 +175,12 @@
 
         end(block);
 
+        lexicalContext.pop(block);
         return null;
     }
 
     @Override
-    public Node enter(final CallNode callNode) {
+    public Node enterCallNode(final CallNode callNode) {
         start(callNode);
 
         callNode.getFunction().accept(this);
@@ -197,8 +201,7 @@
             evalArgs.setThis(thisNode);
         }
 
-        newTemporary(Type.OBJECT, callNode); // object type here, access specialization in FinalizeTypes may narrow it later
-        newType(callNode.getFunction().getSymbol(), Type.OBJECT);
+        newTemporary(callNode.getType(), callNode); // access specialization in FinalizeTypes may narrow it further later
 
         end(callNode);
 
@@ -206,29 +209,106 @@
     }
 
     @Override
-    public Node enter(final CatchNode catchNode) {
+    public Node enterCatchNode(final CatchNode catchNode) {
         final IdentNode exception = catchNode.getException();
         final Block     block     = getCurrentBlock();
 
         start(catchNode);
 
         // define block-local exception variable
-        final Symbol def = block.defineSymbol(exception.getName(), IS_VAR | IS_LET, exception);
+        final Symbol def = defineSymbol(block, exception.getName(), IS_VAR | IS_LET, exception);
         newType(def, Type.OBJECT);
         addLocalDef(exception.getName());
 
         return catchNode;
     }
 
+    /**
+     * Declare the definition of a new symbol.
+     *
+     * @param name         Name of symbol.
+     * @param symbolFlags  Symbol flags.
+     * @param node         Defining Node.
+     *
+     * @return Symbol for given name or null for redefinition.
+     */
+    private Symbol defineSymbol(final Block block, final String name, final int symbolFlags, final Node node) {
+        int    flags  = symbolFlags;
+        Symbol symbol = findSymbol(block, name); // Locate symbol.
+
+        if ((flags & KINDMASK) == IS_GLOBAL) {
+            flags |= IS_SCOPE;
+        }
+
+        final FunctionNode function = lexicalContext.getFunction(block);
+        if (symbol != null) {
+            // Symbol was already defined. Check if it needs to be redefined.
+            if ((flags & KINDMASK) == IS_PARAM) {
+                if (!isLocal(function, symbol)) {
+                    // Not defined in this function. Create a new definition.
+                    symbol = null;
+                } else if (symbol.isParam()) {
+                    // Duplicate parameter. Null return will force an error.
+                    assert false : "duplicate parameter";
+                    return null;
+                }
+            } else if ((flags & KINDMASK) == IS_VAR) {
+                if ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET) {
+                    assert !((flags & IS_LET) == IS_LET && symbol.getBlock() == block) : "duplicate let variable in block";
+                    // Always create a new definition.
+                    symbol = null;
+                } else {
+                    // Not defined in this function. Create a new definition.
+                    if (!isLocal(function, symbol) || symbol.less(IS_VAR)) {
+                        symbol = null;
+                    }
+                }
+            }
+        }
+
+        if (symbol == null) {
+            // If not found, then create a new one.
+            Block symbolBlock;
+
+            // Determine where to create it.
+            if ((flags & Symbol.KINDMASK) == IS_VAR && ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET)) {
+                symbolBlock = block;
+            } else {
+                symbolBlock = function;
+            }
+
+            // Create and add to appropriate block.
+            symbol = new Symbol(name, flags, node, symbolBlock);
+            symbolBlock.putSymbol(name, symbol);
+
+            if ((flags & Symbol.KINDMASK) != IS_GLOBAL) {
+                symbolBlock.getFrame().addSymbol(symbol);
+                symbol.setNeedsSlot(true);
+            }
+        } else if (symbol.less(flags)) {
+            symbol.setFlags(flags);
+        }
+
+        if (node != null) {
+            node.setSymbol(symbol);
+        }
+
+        return symbol;
+    }
+
     @Override
-    public Node enter(final FunctionNode functionNode) {
+    public Node enterFunctionNode(final FunctionNode functionNode) {
         start(functionNode, false);
         if (functionNode.isLazy()) {
-            LOG.info("LAZY: " + functionNode.getName());
+            LOG.info("LAZY: " + functionNode.getName() + " => Promoting to OBJECT");
+            newTemporary(lexicalContext.getCurrentFunction(), Type.OBJECT, functionNode);
+            functionNode.setReturnType(Type.OBJECT);
             end(functionNode);
             return null;
         }
 
+        lexicalContext.push(functionNode);
+
         clearLocalDefs();
         clearLocalUses();
 
@@ -244,24 +324,36 @@
         initScope(functionNode);
         initReturn(functionNode);
 
-        // Add all nested functions as symbols in this function
-        for (final FunctionNode nestedFunction : functionNode.getFunctions()) {
+        // Add all nested declared functions as symbols in this function
+        for (final FunctionNode nestedFunction : functionNode.getDeclaredFunctions()) {
             final IdentNode ident = nestedFunction.getIdent();
-            if (ident != null && nestedFunction.isStatement()) {
-                final Symbol functionSymbol = functionNode.defineSymbol(ident.getName(), IS_VAR, nestedFunction);
+            if (ident != null) {
+                assert nestedFunction.isDeclared();
+                final Symbol functionSymbol = defineSymbol(functionNode, ident.getName(), IS_VAR, nestedFunction);
                 newType(functionSymbol, Type.typeFor(ScriptFunction.class));
             }
         }
 
-        if (functionNode.isScript()) {
+        if (functionNode.isProgram()) {
             initFromPropertyMap(functionNode);
         }
 
         // Add function name as local symbol
-        if (!functionNode.isStatement() && !functionNode.isAnonymous() && !functionNode.isScript()) {
-            final Symbol selfSymbol = functionNode.defineSymbol(functionNode.getIdent().getName(), IS_VAR, functionNode);
-            newType(selfSymbol, Type.OBJECT);
-            selfSymbol.setNode(functionNode);
+        if (!functionNode.isDeclared() && !functionNode.isProgram()) {
+            if(functionNode.getSymbol() != null) {
+                // a temporary left over from an earlier pass when the function was lazy
+                assert functionNode.getSymbol().isTemp();
+                // remove it
+                functionNode.setSymbol(null);
+            }
+            final Symbol selfSymbol;
+            if(functionNode.isAnonymous()) {
+                selfSymbol = newTemporary(functionNode, Type.OBJECT, functionNode);
+            } else {
+                selfSymbol = defineSymbol(functionNode, functionNode.getIdent().getName(), IS_VAR, functionNode);
+                newType(selfSymbol, Type.OBJECT);
+                selfSymbol.setNode(functionNode);
+            }
         }
 
         /*
@@ -282,32 +374,26 @@
          */
 
         final List<Symbol> declaredSymbols = new ArrayList<>();
-        for (final VarNode decl : functionNode.getDeclarations()) {
-            final IdentNode ident = decl.getName();
-            // any declared symbols that aren't visited need to be typed as well, hence the list
-            declaredSymbols.add(functionNode.defineSymbol(ident.getName(), IS_VAR, new IdentNode(ident)));
-        }
-
-        // Every nested function needs a definition in the outer function with its name. Add these.
-        for (final FunctionNode nestedFunction : functionNode.getFunctions()) {
-            final VarNode varNode = nestedFunction.getFunctionVarNode();
-            if (varNode != null) {
-                varNode.accept(this);
-                assert varNode.isFunctionVarNode() : varNode + " should be function var node";
+        // This visitor will assign symbol to all declared variables, except function declarations (which are taken care
+        // in a separate step above) and "var" declarations in for loop initializers.
+        functionNode.accept(new NodeOperatorVisitor() {
+            @Override
+            public Node enterFunctionNode(FunctionNode nestedFn) {
+                // Don't descend into nested functions
+                return nestedFn == functionNode ? nestedFn : null;
             }
-        }
-
-        for (final Node statement : functionNode.getStatements()) {
-            if (statement instanceof VarNode && ((VarNode)statement).isFunctionVarNode()) {
-                continue; //var nodes have already been processed, skip or they will generate additional defs/uses and false "can be undefined"
+            @Override
+            public Node enterVarNode(VarNode varNode) {
+                if(varNode.isStatement() && !varNode.isFunctionDeclaration()) {
+                    final IdentNode ident = varNode.getName();
+                    // any declared symbols that aren't visited need to be typed as well, hence the list
+                    declaredSymbols.add(defineSymbol(functionNode, ident.getName(), IS_VAR, new IdentNode(ident)));
+                }
+                return null;
             }
-            statement.accept(this);
-        }
+        });
 
-        for (final FunctionNode nestedFunction : functionNode.getFunctions()) {
-            LOG.info("Going into nested function " + functionNode.getName() + " -> " + nestedFunction.getName());
-            nestedFunction.accept(this);
-        }
+        visitFunctionStatements(functionNode);
 
         //unknown parameters are promoted to object type.
         finalizeParameters(functionNode);
@@ -343,10 +429,19 @@
         functionNode.setState(CompilationState.ATTR);
 
         end(functionNode, false);
+        lexicalContext.pop(functionNode);
 
         return null;
     }
 
+    private void visitFunctionStatements(final FunctionNode functionNode) {
+        final List<Node> newStatements = new ArrayList<>(functionNode.getStatements());
+        for(ListIterator<Node> stmts = newStatements.listIterator(); stmts.hasNext();) {
+            stmts.set(stmts.next().accept(this));
+        }
+        functionNode.setStatements(newStatements);
+    }
+
     @Override
     public Node leaveCONVERT(final UnaryNode unaryNode) {
         assert false : "There should be no convert operators in IR during Attribution";
@@ -355,7 +450,7 @@
     }
 
     @Override
-    public Node enter(final IdentNode identNode) {
+    public Node enterIdentNode(final IdentNode identNode) {
         final String name = identNode.getName();
 
         start(identNode);
@@ -372,7 +467,7 @@
         final Block  block     = getCurrentBlock();
         final Symbol oldSymbol = identNode.getSymbol();
 
-        Symbol symbol = block.findSymbol(name);
+        Symbol symbol = findSymbol(block, name);
 
         //If an existing symbol with the name is found, use that otherwise, declare a new one
         if (symbol != null) {
@@ -396,22 +491,13 @@
             }
 
             identNode.setSymbol(symbol);
-            if (!getCurrentFunctionNode().isLocal(symbol)) {
-                // non-local: we need to put symbol in scope (if it isn't already)
-                if (!symbol.isScope()) {
-                    final List<Block> lookupBlocks = findLookupBlocksHelper(getCurrentFunctionNode(), symbol.findFunction());
-                    for (final Block lookupBlock : lookupBlocks) {
-                        final Symbol refSymbol = lookupBlock.findSymbol(name);
-                        if (refSymbol != null) { // See NASHORN-837, function declaration in lexical scope: try {} catch (x){ function f() { use(x) } } f()
-                            LOG.finest("Found a ref symbol that must be scope " + refSymbol);
-                            refSymbol.setIsScope();
-                        }
-                    }
-                }
+            // non-local: we need to put symbol in scope (if it isn't already)
+            if (!isLocal(getCurrentFunctionNode(), symbol) && !symbol.isScope()) {
+                symbol.setIsScope();
             }
         } else {
             LOG.info("No symbol exists. Declare undefined: " + symbol);
-            symbol = block.useSymbol(name, identNode);
+            symbol = useSymbol(block, name, identNode);
             // we have never seen this before, it can be undefined
             newType(symbol, Type.OBJECT); // TODO unknown -we have explicit casts anyway?
             symbol.setCanBeUndefined();
@@ -420,9 +506,10 @@
 
         assert symbol != null;
         if(symbol.isGlobal()) {
-            getCurrentFunctionNode().setUsesGlobalSymbol();
+            setUsesGlobalSymbol();
         } else if(symbol.isScope()) {
-            getCurrentFunctionNode().setUsesScopeSymbol(symbol);
+            final Iterator<Block> blocks = lexicalContext.getBlocks();
+            blocks.next().setUsesScopeSymbol(symbol, blocks);
         }
 
         if (symbol != oldSymbol && !identNode.isInitializedHere()) {
@@ -435,15 +522,68 @@
         return null;
     }
 
+    /**
+     * Marks the current function as one using any global symbol. The function and all its parent functions will all be
+     * marked as needing parent scope.
+     * @see #needsParentScope()
+     */
+    private void setUsesGlobalSymbol() {
+        for(final Iterator<FunctionNode> fns = lexicalContext.getFunctions(); fns.hasNext();) {
+            fns.next().setUsesAncestorScope();
+        }
+    }
+
+    /**
+     * Declare the use of a symbol in a block.
+     *
+     * @param block block in which the symbol is used
+     * @param name Name of symbol.
+     * @param node Using node
+     *
+     * @return Symbol for given name.
+     */
+    private Symbol useSymbol(final Block block, final String name, final Node node) {
+        Symbol symbol = findSymbol(block, name);
+
+        if (symbol == null) {
+            // If not found, declare as a free var.
+            symbol = defineSymbol(block, name, IS_GLOBAL, node);
+        } else {
+            node.setSymbol(symbol);
+        }
+
+        return symbol;
+    }
+
+
+    /**
+     * Search for symbol in the lexical context starting from the given block.
+     * @param name Symbol name.
+     * @return Found symbol or null if not found.
+     */
+    private Symbol findSymbol(final Block block, final String name) {
+        // Search up block chain to locate symbol.
+
+        for(final Iterator<Block> blocks = lexicalContext.getBlocks(block); blocks.hasNext();) {
+            // Find name.
+            final Symbol symbol = blocks.next().getExistingSymbol(name);
+            // If found then we are good.
+            if(symbol != null) {
+                return symbol;
+            }
+        }
+        return null;
+    }
+
     @Override
-    public Node leave(final IndexNode indexNode) {
+    public Node leaveIndexNode(final IndexNode indexNode) {
         newTemporary(Type.OBJECT, indexNode); //TORO
         return indexNode;
     }
 
     @SuppressWarnings("rawtypes")
     @Override
-    public Node enter(final LiteralNode literalNode) {
+    public Node enterLiteralNode(final LiteralNode literalNode) {
         try {
             start(literalNode);
             assert !literalNode.isTokenType(TokenType.THIS) : "tokentype for " + literalNode + " is this"; //guard against old dead code case. literal nodes should never inherit tokens
@@ -472,14 +612,14 @@
     }
 
     @Override
-    public Node leave(final ObjectNode objectNode) {
+    public Node leaveObjectNode(final ObjectNode objectNode) {
         newTemporary(Type.OBJECT, objectNode);
         end(objectNode);
         return objectNode;
     }
 
     @Override
-    public Node enter(final PropertyNode propertyNode) {
+    public Node enterPropertyNode(final PropertyNode propertyNode) {
         // assign a pseudo symbol to property name, see NASHORN-710
         propertyNode.setSymbol(new Symbol(propertyNode.getKeyName(), 0, Type.OBJECT));
         end(propertyNode);
@@ -487,31 +627,7 @@
     }
 
     @Override
-    public Node enter(final ReferenceNode referenceNode) {
-        final FunctionNode functionNode = referenceNode.getReference();
-        if (functionNode != null) {
-            functionNode.addReferencingParentBlock(getCurrentBlock());
-        }
-        return referenceNode;
-    }
-
-    @Override
-    public Node leave(final ReferenceNode referenceNode) {
-        newTemporary(Type.OBJECT, referenceNode); //reference node type is always an object, i.e. the scriptFunction. the function return type varies though
-
-        final FunctionNode functionNode = referenceNode.getReference();
-        //assert !functionNode.getType().isUnknown() || functionNode.isLazy() : functionNode.getType();
-        if (functionNode.isLazy()) {
-            LOG.info("Lazy function node call reference: " + functionNode.getName() + " => Promoting to OBJECT");
-            functionNode.setReturnType(Type.OBJECT);
-        }
-        end(referenceNode);
-
-        return referenceNode;
-    }
-
-    @Override
-    public Node leave(final ReturnNode returnNode) {
+    public Node leaveReturnNode(final ReturnNode returnNode) {
         final Node expr = returnNode.getExpression();
 
         if (expr != null) {
@@ -530,7 +646,7 @@
     }
 
     @Override
-    public Node leave(final SwitchNode switchNode) {
+    public Node leaveSwitchNode(final SwitchNode switchNode) {
         Type type = Type.UNKNOWN;
 
         for (final CaseNode caseNode : switchNode.getCases()) {
@@ -567,7 +683,7 @@
     }
 
     @Override
-    public Node leave(final TryNode tryNode) {
+    public Node leaveTryNode(final TryNode tryNode) {
         tryNode.setException(exceptionSymbol());
 
         if (tryNode.getFinallyBody() != null) {
@@ -580,13 +696,13 @@
     }
 
     @Override
-    public Node enter(final VarNode varNode) {
+    public Node enterVarNode(final VarNode varNode) {
         start(varNode);
 
         final IdentNode ident = varNode.getName();
         final String    name  = ident.getName();
 
-        final Symbol symbol = getCurrentBlock().defineSymbol(name, IS_VAR, ident);
+        final Symbol symbol = defineSymbol(getCurrentBlock(), name, IS_VAR, ident);
         assert symbol != null;
 
         LOG.info("VarNode " + varNode + " set symbol " + symbol);
@@ -598,23 +714,15 @@
             symbol.setCanBeUndefined();
         }
 
-        if (varNode.getInit() != null) {
-            varNode.getInit().accept(this);
-        }
-
         return varNode;
     }
 
     @Override
-    public Node leave(final VarNode varNode) {
+    public Node leaveVarNode(final VarNode varNode) {
         final Node      init  = varNode.getInit();
         final IdentNode ident = varNode.getName();
         final String    name  = ident.getName();
 
-        if (init != null) {
-            addLocalDef(name);
-        }
-
         if (init == null) {
             // var x; with no init will be treated like a use of x by
             // visit(IdentNode) unless we remove the name
@@ -623,8 +731,10 @@
             return varNode;
         }
 
+        addLocalDef(name);
+
         final Symbol  symbol   = varNode.getSymbol();
-        final boolean isScript = symbol.getBlock().getFunction().isScript(); //see NASHORN-56
+        final boolean isScript = lexicalContext.getFunction(symbol.getBlock()).isProgram(); //see NASHORN-56
         if ((init.getType().isNumeric() || init.getType().isBoolean()) && !isScript) {
             // Forbid integers as local vars for now as we have no way to treat them as undefined
             newType(symbol, init.getType());
@@ -718,11 +828,9 @@
         runtimeNode = new RuntimeNode(unaryNode, request, args);
         assert runtimeNode.getSymbol() == unaryNode.getSymbol(); //clone constructor should do this
 
-        runtimeNode.accept(this);
-        return runtimeNode;
+        return leaveRuntimeNode(runtimeNode);
     }
 
-
     @Override
     public Node leaveNEW(final UnaryNode unaryNode) {
         newTemporary(Type.OBJECT, unaryNode);
@@ -755,7 +863,7 @@
         runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args);
         assert runtimeNode.getSymbol() == unaryNode.getSymbol();
 
-        runtimeNode.accept(this);
+        runtimeNode = (RuntimeNode)leaveRuntimeNode(runtimeNode);
 
         end(unaryNode);
 
@@ -763,7 +871,7 @@
     }
 
     @Override
-    public Node leave(final RuntimeNode runtimeNode) {
+    public Node leaveRuntimeNode(final RuntimeNode runtimeNode) {
         newTemporary(runtimeNode.getRequest().getReturnType(), runtimeNode);
         return runtimeNode;
     }
@@ -823,12 +931,12 @@
             final IdentNode ident = (IdentNode)lhs;
             final String    name  = ident.getName();
 
-            Symbol symbol = getCurrentBlock().findSymbol(name);
+            Symbol symbol = findSymbol(getCurrentBlock(), name);
 
             if (symbol == null) {
-                symbol = block.defineSymbol(name, IS_GLOBAL, ident);
+                symbol = defineSymbol(block, name, IS_GLOBAL, ident);
                 binaryNode.setSymbol(symbol);
-            } else if (!getCurrentFunctionNode().isLocal(symbol)) {
+            } else if (!isLocal(getCurrentFunctionNode(), symbol)) {
                 symbol.setIsScope();
             }
 
@@ -838,6 +946,12 @@
         return binaryNode;
     }
 
+    private boolean isLocal(FunctionNode function, Symbol symbol) {
+        final Block block = symbol.getBlock();
+        // some temp symbols have no block, so can be assumed local
+        return block == null || lexicalContext.getFunction(block) == function;
+    }
+
     @Override
     public Node enterASSIGN(final BinaryNode binaryNode) {
         return enterAssignmentNode(binaryNode);
@@ -995,7 +1109,7 @@
         return leaveBinaryArithmetic(binaryNode);
     }
 
-    private Node leaveCmp(final BinaryNode binaryNode, final RuntimeNode.Request request) {
+    private Node leaveCmp(final BinaryNode binaryNode) {
         final Node lhs = binaryNode.lhs();
         final Node rhs = binaryNode.rhs();
 
@@ -1033,37 +1147,38 @@
 
     @Override
     public Node leaveEQ(final BinaryNode binaryNode) {
-        return leaveCmp(binaryNode, Request.EQ);
+        return leaveCmp(binaryNode);
     }
 
     @Override
     public Node leaveEQ_STRICT(final BinaryNode binaryNode) {
-        return leaveCmp(binaryNode, Request.EQ_STRICT);
+        return leaveCmp(binaryNode);
     }
 
     @Override
     public Node leaveGE(final BinaryNode binaryNode) {
-        return leaveCmp(binaryNode, Request.GE);
+        return leaveCmp(binaryNode);
     }
 
     @Override
     public Node leaveGT(final BinaryNode binaryNode) {
-        return leaveCmp(binaryNode, Request.GT);
+        return leaveCmp(binaryNode);
     }
 
     @Override
     public Node leaveIN(final BinaryNode binaryNode) {
-        try {
-            return new RuntimeNode(binaryNode, Request.IN).accept(this);
-        } finally {
-            end(binaryNode);
-        }
+        return leaveBinaryRuntimeOperator(binaryNode, Request.IN);
     }
 
     @Override
     public Node leaveINSTANCEOF(final BinaryNode binaryNode) {
+        return leaveBinaryRuntimeOperator(binaryNode, Request.INSTANCEOF);
+    }
+
+    private Node leaveBinaryRuntimeOperator(final BinaryNode binaryNode, final Request request) {
         try {
-            return new RuntimeNode(binaryNode, Request.INSTANCEOF).accept(this);
+            // Don't do a full RuntimeNode.accept, as we don't want to double-visit the binary node operands
+            return leaveRuntimeNode(new RuntimeNode(binaryNode, request));
         } finally {
             end(binaryNode);
         }
@@ -1071,12 +1186,12 @@
 
     @Override
     public Node leaveLE(final BinaryNode binaryNode) {
-        return leaveCmp(binaryNode, Request.LE);
+        return leaveCmp(binaryNode);
     }
 
     @Override
     public Node leaveLT(final BinaryNode binaryNode) {
-        return leaveCmp(binaryNode, Request.LT);
+        return leaveCmp(binaryNode);
     }
 
     @Override
@@ -1091,12 +1206,12 @@
 
     @Override
     public Node leaveNE(final BinaryNode binaryNode) {
-        return leaveCmp(binaryNode, Request.NE);
+        return leaveCmp(binaryNode);
     }
 
     @Override
     public Node leaveNE_STRICT(final BinaryNode binaryNode) {
-        return leaveCmp(binaryNode, Request.NE_STRICT);
+        return leaveCmp(binaryNode);
     }
 
     @Override
@@ -1127,9 +1242,9 @@
     }
 
     @Override
-    public Node leave(final ForNode forNode) {
+    public Node leaveForNode(final ForNode forNode) {
         if (forNode.isForIn()) {
-            forNode.setIterator(newInternal(getCurrentFunctionNode(), getCurrentFunctionNode().uniqueName(ITERATOR_PREFIX.tag()), Type.OBJECT)); //NASHORN-73
+            forNode.setIterator(newInternal(getCurrentFunctionNode().uniqueName(ITERATOR_PREFIX.tag()), Type.OBJECT)); //NASHORN-73
             /*
              * Iterators return objects, so we need to widen the scope of the
              * init variable if it, for example, has been assigned double type
@@ -1144,7 +1259,7 @@
     }
 
     @Override
-    public Node leave(final TernaryNode ternaryNode) {
+    public Node leaveTernaryNode(final TernaryNode ternaryNode) {
         final Node lhs  = ternaryNode.rhs();
         final Node rhs  = ternaryNode.third();
 
@@ -1159,24 +1274,24 @@
         return ternaryNode;
     }
 
-    private static void initThis(final FunctionNode functionNode) {
-        final Symbol thisSymbol = functionNode.defineSymbol(THIS.tag(), IS_PARAM | IS_THIS, null);
+    private void initThis(final FunctionNode functionNode) {
+        final Symbol thisSymbol = defineSymbol(functionNode, THIS.tag(), IS_PARAM | IS_THIS, null);
         newType(thisSymbol, Type.OBJECT);
         thisSymbol.setNeedsSlot(true);
         functionNode.getThisNode().setSymbol(thisSymbol);
         LOG.info("Initialized scope symbol: " + thisSymbol);
     }
 
-    private static void initScope(final FunctionNode functionNode) {
-        final Symbol scopeSymbol = functionNode.defineSymbol(SCOPE.tag(), IS_VAR | IS_INTERNAL, null);
+    private void initScope(final FunctionNode functionNode) {
+        final Symbol scopeSymbol = defineSymbol(functionNode, SCOPE.tag(), IS_VAR | IS_INTERNAL, null);
         newType(scopeSymbol, Type.typeFor(ScriptObject.class));
         scopeSymbol.setNeedsSlot(true);
         functionNode.getScopeNode().setSymbol(scopeSymbol);
         LOG.info("Initialized scope symbol: " + scopeSymbol);
     }
 
-    private static void initReturn(final FunctionNode functionNode) {
-        final Symbol returnSymbol = functionNode.defineSymbol(SCRIPT_RETURN.tag(), IS_VAR | IS_INTERNAL, null);
+    private void initReturn(final FunctionNode functionNode) {
+        final Symbol returnSymbol = defineSymbol(functionNode, SCRIPT_RETURN.tag(), IS_VAR | IS_INTERNAL, null);
         newType(returnSymbol, Type.OBJECT);
         returnSymbol.setNeedsSlot(true);
         functionNode.getResultNode().setSymbol(returnSymbol);
@@ -1186,7 +1301,7 @@
 
     private void initVarArg(final FunctionNode functionNode) {
         if (functionNode.isVarArg()) {
-            final Symbol varArgsSymbol = functionNode.defineSymbol(VARARGS.tag(), IS_PARAM | IS_INTERNAL, null);
+            final Symbol varArgsSymbol = defineSymbol(functionNode, VARARGS.tag(), IS_PARAM | IS_INTERNAL, null);
             varArgsSymbol.setTypeOverride(Type.OBJECT_ARRAY);
             varArgsSymbol.setNeedsSlot(true);
             functionNode.getVarArgsNode().setSymbol(varArgsSymbol);
@@ -1194,7 +1309,7 @@
 
             if (functionNode.needsArguments()) {
                 final String    argumentsName   = functionNode.getArgumentsNode().getName();
-                final Symbol    argumentsSymbol = functionNode.defineSymbol(argumentsName, IS_VAR | IS_INTERNAL, null);
+                final Symbol    argumentsSymbol = defineSymbol(functionNode, argumentsName, IS_VAR | IS_INTERNAL, null);
                 newType(argumentsSymbol, Type.typeFor(ScriptObject.class));
                 argumentsSymbol.setNeedsSlot(true);
                 functionNode.getArgumentsNode().setSymbol(argumentsSymbol);
@@ -1204,9 +1319,9 @@
         }
     }
 
-    private static void initCallee(final FunctionNode functionNode) {
+    private void initCallee(final FunctionNode functionNode) {
         assert functionNode.getCalleeNode() != null : functionNode + " has no callee";
-        final Symbol calleeSymbol = functionNode.defineSymbol(CALLEE.tag(), IS_PARAM | IS_INTERNAL, null);
+        final Symbol calleeSymbol = defineSymbol(functionNode, CALLEE.tag(), IS_PARAM | IS_INTERNAL, null);
         newType(calleeSymbol, Type.typeFor(ScriptFunction.class));
         calleeSymbol.setNeedsSlot(true);
         functionNode.getCalleeNode().setSymbol(calleeSymbol);
@@ -1226,7 +1341,7 @@
 
         for (final IdentNode param : functionNode.getParameters()) {
             addLocalDef(param.getName());
-            final Symbol paramSymbol = functionNode.defineSymbol(param.getName(), IS_PARAM, param);
+            final Symbol paramSymbol = defineSymbol(functionNode, param.getName(), IS_PARAM, param);
             if (paramSymbol != null) {
                 final Type callSiteParamType = functionNode.getSpecializedType(param);
                 if (callSiteParamType != null) {
@@ -1279,15 +1394,15 @@
      * Move any properties from a global map into the scope of this method
      * @param functionNode the function node for which to init scope vars
      */
-    private static void initFromPropertyMap(final FunctionNode functionNode) {
+    private void initFromPropertyMap(final FunctionNode functionNode) {
         // For a script, add scope symbols as defined in the property map
-        assert functionNode.isScript();
+        assert functionNode.isProgram();
 
         final PropertyMap map = Context.getGlobalMap();
 
         for (final Property property : map.getProperties()) {
             final String key    = property.getKey();
-            final Symbol symbol = functionNode.defineSymbol(key, IS_GLOBAL, null);
+            final Symbol symbol = defineSymbol(functionNode, key, IS_GLOBAL, null);
             newType(symbol, Type.OBJECT);
             LOG.info("Added global symbol from property map " + symbol);
         }
@@ -1354,7 +1469,7 @@
     private static void ensureAssignmentSlots(final FunctionNode functionNode, final Node assignmentDest) {
         assignmentDest.accept(new NodeVisitor() {
             @Override
-            public Node leave(final IndexNode indexNode) {
+            public Node leaveIndexNode(final IndexNode indexNode) {
                 final Node index = indexNode.getIndex();
                 index.getSymbol().setNeedsSlot(!index.getSymbol().isConstant());
                 return indexNode;
@@ -1399,7 +1514,7 @@
                 }
 
                 @Override
-                public Node enter(final FunctionNode node) {
+                public Node enterFunctionNode(final FunctionNode node) {
                     return node.isLazy() ? null : node;
                 }
 
@@ -1419,7 +1534,7 @@
                  */
                 @SuppressWarnings("fallthrough")
                 @Override
-                public Node leave(final BinaryNode binaryNode) {
+                public Node leaveBinaryNode(final BinaryNode binaryNode) {
                     final Type widest = Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType());
                     switch (binaryNode.tokenType()) {
                     default:
@@ -1477,22 +1592,6 @@
         return binaryNode;
     }
 
-    private static List<Block> findLookupBlocksHelper(final FunctionNode currentFunction, final FunctionNode topFunction) {
-        if (currentFunction.findParentFunction() == topFunction) {
-            final List<Block> blocks = new LinkedList<>();
-
-            blocks.add(currentFunction.getParent());
-            blocks.addAll(currentFunction.getReferencingParentBlocks());
-            return blocks;
-        }
-        /*
-         * assumption: all parent blocks of an inner function will always be in the same outer function;
-         * therefore we can simply skip through intermediate functions.
-         * @see FunctionNode#addReferencingParentBlock(Block)
-         */
-        return findLookupBlocksHelper(currentFunction.findParentFunction(), topFunction);
-    }
-
     private static boolean isFunctionExpressionSelfReference(final Symbol symbol) {
         if (symbol.isVar() && symbol.getNode() == symbol.getBlock() && symbol.getNode() instanceof FunctionNode) {
             return ((FunctionNode)symbol.getNode()).getIdent().getName().equals(symbol.getName());
@@ -1509,16 +1608,12 @@
         return newTemporary(getCurrentFunctionNode(), type, node);
     }
 
-    private Symbol newInternal(final FunctionNode functionNode, final String name, final Type type) {
-        final Symbol iter = getCurrentFunctionNode().defineSymbol(name, IS_VAR | IS_INTERNAL, null);
+    private Symbol newInternal(final String name, final Type type) {
+        final Symbol iter = defineSymbol(getCurrentFunctionNode(), name, IS_VAR | IS_INTERNAL, null);
         iter.setType(type); // NASHORN-73
         return iter;
     }
 
-    private Symbol newInternal(final String name, final Type type) {
-        return newInternal(getCurrentFunctionNode(), name, type);
-    }
-
     private static void newType(final Symbol symbol, final Type type) {
         final Type oldType = symbol.getSymbolType();
         symbol.setType(type);
@@ -1577,13 +1672,13 @@
             }
 
             @Override
-            public Node enter(final Block block) {
+            public Node enterBlock(final Block block) {
                 toObject(block);
                 return block;
             }
 
             @Override
-            public Node enter(final FunctionNode node) {
+            public Node enterFunctionNode(final FunctionNode node) {
                 toObject(node);
                 if (node.isLazy()) {
                     return null;
--- a/nashorn/src/jdk/nashorn/internal/codegen/ClassEmitter.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/codegen/ClassEmitter.java	Sat Mar 23 00:58:39 2013 +0100
@@ -195,6 +195,14 @@
     }
 
     /**
+     * Returns the name of the compile unit class name.
+     * @return the name of the compile unit class name.
+     */
+    String getUnitClassName() {
+        return unitClassName;
+    }
+
+    /**
      * Convert a binary name to a package/class name.
      *
      * @param name Binary name.
@@ -244,7 +252,7 @@
             // $getMap - get the ith entry from the constants table and cast to PropertyMap.
             final MethodEmitter getMapMethod = method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), GET_MAP.tag(), PropertyMap.class, int.class);
             getMapMethod.begin();
-            getMapMethod.loadConstants(unitClassName)
+            getMapMethod.loadConstants()
                         .load(Type.INT, 0)
                         .arrayload()
                         .checkcast(PropertyMap.class)
@@ -254,7 +262,7 @@
             // $setMap - overwrite an existing map.
             final MethodEmitter setMapMethod = method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), SET_MAP.tag(), void.class, int.class, PropertyMap.class);
             setMapMethod.begin();
-            setMapMethod.loadConstants(unitClassName)
+            setMapMethod.loadConstants()
                         .load(Type.INT, 0)
                         .load(Type.OBJECT, 1)
                         .arraystore();
--- a/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Sat Mar 23 00:58:39 2013 +0100
@@ -76,18 +76,18 @@
 import jdk.nashorn.internal.ir.ExecuteNode;
 import jdk.nashorn.internal.ir.ForNode;
 import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
 import jdk.nashorn.internal.ir.IdentNode;
 import jdk.nashorn.internal.ir.IfNode;
 import jdk.nashorn.internal.ir.IndexNode;
+import jdk.nashorn.internal.ir.LexicalContext;
 import jdk.nashorn.internal.ir.LineNumberNode;
 import jdk.nashorn.internal.ir.LiteralNode;
-import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
 import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
 import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit;
 import jdk.nashorn.internal.ir.Node;
 import jdk.nashorn.internal.ir.ObjectNode;
 import jdk.nashorn.internal.ir.PropertyNode;
-import jdk.nashorn.internal.ir.ReferenceNode;
 import jdk.nashorn.internal.ir.ReturnNode;
 import jdk.nashorn.internal.ir.RuntimeNode;
 import jdk.nashorn.internal.ir.RuntimeNode.Request;
@@ -107,6 +107,7 @@
 import jdk.nashorn.internal.parser.Lexer.RegexToken;
 import jdk.nashorn.internal.parser.TokenType;
 import jdk.nashorn.internal.runtime.Context;
+import jdk.nashorn.internal.runtime.DebugLogger;
 import jdk.nashorn.internal.runtime.ECMAException;
 import jdk.nashorn.internal.runtime.Property;
 import jdk.nashorn.internal.runtime.PropertyMap;
@@ -156,12 +157,20 @@
     /** How many regexp fields have been emitted */
     private int regexFieldCount;
 
+    /** Used for temporary signaling between enterCallNode and enterFunctionNode to handle the special case of calling
+     * a just-defined anonymous function expression. */
+    private boolean functionNodeIsCallee;
+
     /** Map of shared scope call sites */
     private final Map<SharedScopeCall, SharedScopeCall> scopeCalls = new HashMap<>();
 
+    private final LexicalContext lexicalContext = new LexicalContext();
+
     /** When should we stop caching regexp expressions in fields to limit bytecode size? */
     private static final int MAX_REGEX_FIELDS = 2 * 1024;
 
+    private static final DebugLogger LOG   = new DebugLogger("codegen", "nashorn.codegen.debug");
+
     /**
      * Constructor.
      *
@@ -210,7 +219,7 @@
             final int flags = CALLSITE_SCOPE | getCallSiteFlags();
             method.loadScope();
 
-            if (symbol.isFastScope(getCurrentFunctionNode())) {
+            if (isFastScope(symbol)) {
                 // Only generate shared scope getter for fast-scope symbols so we know we can dial in correct scope.
                 if (symbol.getUseCount() > SharedScopeCall.FAST_SCOPE_GET_THRESHOLD) {
                     return loadSharedScopeVar(identNode.getType(), symbol, flags);
@@ -221,8 +230,28 @@
         }
     }
 
+    /**
+     * Check if this symbol can be accessed directly with a putfield or getfield or dynamic load
+     *
+     * @param function function to check for fast scope
+     * @return true if fast scope
+     */
+    private boolean isFastScope(final Symbol symbol) {
+        if (!symbol.isScope() || !symbol.getBlock().needsScope()) {
+            return false;
+        }
+        // Allow fast scope access if no function contains with or eval
+        for(final Iterator<FunctionNode> it = lexicalContext.getFunctions(getCurrentFunctionNode()); it.hasNext();) {
+            final FunctionNode func = it.next();
+            if (func.hasWith() || func.hasEval()) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     private MethodEmitter loadSharedScopeVar(final Type valueType, final Symbol symbol, final int flags) {
-        method.load(symbol.isFastScope(getCurrentFunctionNode()) ? getScopeProtoDepth(getCurrentBlock(), symbol) : -1);
+        method.load(isFastScope(symbol) ? getScopeProtoDepth(getCurrentBlock(), symbol) : -1);
         final SharedScopeCall scopeCall = getScopeGet(valueType, symbol, flags | CALLSITE_FAST_SCOPE);
         scopeCall.generateInvoke(method);
         return method;
@@ -240,30 +269,18 @@
         return method;
     }
 
-    private static int getScopeProtoDepth(final Block currentBlock, final Symbol symbol) {
-        if (currentBlock == symbol.getBlock()) {
-            return 0;
-        }
-
-        final int   delta       = currentBlock.needsScope() ? 1 : 0;
-        final Block parentBlock = currentBlock.getParent();
-
-        if (parentBlock != null) {
-            final int result = getScopeProtoDepth(parentBlock, symbol);
-            if (result != -1) {
-                return delta + result;
+    private int getScopeProtoDepth(final Block startingBlock, final Symbol symbol) {
+        int depth = 0;
+        final Block definingBlock = symbol.getBlock();
+        for(final Iterator<Block> blocks = lexicalContext.getBlocks(startingBlock); blocks.hasNext();) {
+            final Block currentBlock = blocks.next();
+            if (currentBlock == definingBlock) {
+                return depth;
+            }
+            if (currentBlock.needsScope()) {
+                ++depth;
             }
         }
-
-        if (currentBlock instanceof FunctionNode) {
-            for (final Block lookupBlock : ((FunctionNode)currentBlock).getReferencingParentBlocks()) {
-                final int result = getScopeProtoDepth(lookupBlock, symbol);
-                if (result != -1) {
-                    return delta + result;
-                }
-            }
-        }
-
         return -1;
     }
 
@@ -313,13 +330,13 @@
 
         node.accept(new NodeVisitor(getCurrentCompileUnit(), method) {
             @Override
-            public Node enter(final IdentNode identNode) {
+            public Node enterIdentNode(final IdentNode identNode) {
                 loadIdent(identNode);
                 return null;
             }
 
             @Override
-            public Node enter(final AccessNode accessNode) {
+            public Node enterAccessNode(final AccessNode accessNode) {
                 if (!baseAlreadyOnStack) {
                     load(accessNode.getBase()).convert(Type.OBJECT);
                 }
@@ -329,7 +346,7 @@
             }
 
             @Override
-            public Node enter(final IndexNode indexNode) {
+            public Node enterIndexNode(final IndexNode indexNode) {
                 if (!baseAlreadyOnStack) {
                     load(indexNode.getBase()).convert(Type.OBJECT);
                     load(indexNode.getIndex());
@@ -339,6 +356,14 @@
             }
 
             @Override
+            public Node enterFunctionNode(FunctionNode functionNode) {
+                // function nodes will always leave a constructed function object on stack, no need to load the symbol
+                // separately as in enterDefault()
+                functionNode.accept(codegen);
+                return null;
+            }
+
+            @Override
             public Node enterDefault(final Node otherNode) {
                 otherNode.accept(codegen); // generate code for whatever we are looking at.
                 method.load(symbol); // load the final symbol to the stack (or nop if no slot, then result is already there)
@@ -350,7 +375,7 @@
     }
 
     @Override
-    public Node enter(final AccessNode accessNode) {
+    public Node enterAccessNode(final AccessNode accessNode) {
         if (accessNode.testResolved()) {
             return null;
         }
@@ -422,10 +447,11 @@
     }
 
     @Override
-    public Node enter(final Block block) {
+    public Node enterBlock(final Block block) {
         if (block.testResolved()) {
             return null;
         }
+        lexicalContext.push(block);
 
         method.label(block.getEntryLabel());
         initLocals(block);
@@ -434,14 +460,14 @@
     }
 
     @Override
-    public Node leave(final Block block) {
+    public Node leaveBlock(final Block block) {
         method.label(block.getBreakLabel());
         symbolInfo(block);
 
         if (block.needsScope()) {
             popBlockScope(block);
         }
-
+        lexicalContext.pop(block);
         return block;
     }
 
@@ -467,7 +493,7 @@
     }
 
     @Override
-    public Node enter(final BreakNode breakNode) {
+    public Node enterBreakNode(final BreakNode breakNode) {
         if (breakNode.testResolved()) {
             return null;
         }
@@ -515,14 +541,13 @@
     }
 
     @Override
-    public Node enter(final CallNode callNode) {
+    public Node enterCallNode(final CallNode callNode) {
         if (callNode.testResolved()) {
             return null;
         }
 
         final List<Node>   args            = callNode.getArgs();
         final Node         function        = callNode.getFunction();
-        final FunctionNode currentFunction = getCurrentFunctionNode();
         final Block        currentBlock    = getCurrentBlock();
 
         function.accept(new NodeVisitor(getCurrentCompileUnit(), method) {
@@ -531,7 +556,7 @@
                 final Symbol symbol = identNode.getSymbol();
                 int    scopeCallFlags = flags;
                 method.loadScope();
-                if (symbol.isFastScope(currentFunction)) {
+                if (isFastScope(symbol)) {
                     method.load(getScopeProtoDepth(currentBlock, symbol));
                     scopeCallFlags |= CALLSITE_FAST_SCOPE;
                 } else {
@@ -593,7 +618,7 @@
             }
 
             @Override
-            public Node enter(final IdentNode node) {
+            public Node enterIdentNode(final IdentNode node) {
                 final Symbol symbol = node.getSymbol();
 
                 if (symbol.isScope()) {
@@ -606,7 +631,7 @@
                     if (callNode.isEval()) {
                         evalCall(node, flags);
                     } else if (useCount <= SharedScopeCall.FAST_SCOPE_CALL_THRESHOLD
-                            || (!symbol.isFastScope(currentFunction) && useCount <= SharedScopeCall.SLOW_SCOPE_CALL_THRESHOLD)
+                            || (!isFastScope(symbol) && useCount <= SharedScopeCall.SLOW_SCOPE_CALL_THRESHOLD)
                             || callNode.inWithBlock()) {
                         scopeCall(node, flags);
                     } else {
@@ -621,7 +646,7 @@
             }
 
             @Override
-            public Node enter(final AccessNode node) {
+            public Node enterAccessNode(final AccessNode node) {
                 load(node.getBase());
                 method.convert(Type.OBJECT);
                 method.dup();
@@ -634,8 +659,7 @@
             }
 
             @Override
-            public Node enter(final ReferenceNode node) {
-                final FunctionNode callee   = node.getReference();
+            public Node enterFunctionNode(final FunctionNode callee) {
                 final boolean      isVarArg = callee.isVarArg();
                 final int          argCount = isVarArg ? -1 : callee.getParameters().size();
 
@@ -653,12 +677,13 @@
                 loadArgs(args, signature, isVarArg, argCount);
                 method.invokestatic(callee.getCompileUnit().getUnitClassName(), callee.getName(), signature);
                 assert method.peekType().equals(callee.getReturnType()) : method.peekType() + " != " + callee.getReturnType();
-
+                functionNodeIsCallee = true;
+                callee.accept(CodeGenerator.this);
                 return null;
             }
 
             @Override
-            public Node enter(final IndexNode node) {
+            public Node enterIndexNode(final IndexNode node) {
                 load(node.getBase());
                 method.convert(Type.OBJECT);
                 method.dup();
@@ -694,7 +719,7 @@
     }
 
     @Override
-    public Node enter(final ContinueNode continueNode) {
+    public Node enterContinueNode(final ContinueNode continueNode) {
         if (continueNode.testResolved()) {
             return null;
         }
@@ -709,17 +734,17 @@
     }
 
     @Override
-    public Node enter(final DoWhileNode doWhileNode) {
-        return enter((WhileNode)doWhileNode);
+    public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
+        return enterWhileNode(doWhileNode);
     }
 
     @Override
-    public Node enter(final EmptyNode emptyNode) {
+    public Node enterEmptyNode(final EmptyNode emptyNode) {
         return null;
     }
 
     @Override
-    public Node enter(final ExecuteNode executeNode) {
+    public Node enterExecuteNode(final ExecuteNode executeNode) {
         if (executeNode.testResolved()) {
             return null;
         }
@@ -731,7 +756,7 @@
     }
 
     @Override
-    public Node enter(final ForNode forNode) {
+    public Node enterForNode(final ForNode forNode) {
         if (forNode.testResolved()) {
             return null;
         }
@@ -813,7 +838,7 @@
      * @param block block with local vars.
      */
     private void initLocals(final Block block) {
-        final FunctionNode function       = block.getFunction();
+        final FunctionNode function       = lexicalContext.getFunction(block);
         final boolean      isFunctionNode = block == function;
 
         /*
@@ -915,7 +940,7 @@
             foc.makeObject(method);
 
             // runScript(): merge scope into global
-            if (isFunctionNode && function.isScript()) {
+            if (isFunctionNode && function.isProgram()) {
                 method.invoke(ScriptRuntime.MERGE_SCOPE);
             }
 
@@ -958,19 +983,29 @@
     }
 
     @Override
-    public Node enter(final FunctionNode functionNode) {
-        if (functionNode.isLazy()) {
-            return null;
-        }
+    public Node enterFunctionNode(final FunctionNode functionNode) {
+        final boolean isCallee = functionNodeIsCallee;
+        functionNodeIsCallee = false;
 
         if (functionNode.testResolved()) {
             return null;
         }
 
+        if(!(isCallee || functionNode == compiler.getFunctionNode())) {
+            newFunctionObject(functionNode);
+        }
+
+        if (functionNode.isLazy()) {
+            return null;
+        }
+
+        LOG.info("=== BEGIN " + functionNode.getName());
+        lexicalContext.push(functionNode);
+
         setCurrentCompileUnit(functionNode.getCompileUnit());
         assert getCurrentCompileUnit() != null;
 
-        method = getCurrentCompileUnit().getClassEmitter().method(functionNode);
+        setCurrentMethodEmitter(getCurrentCompileUnit().getClassEmitter().method(functionNode));
         functionNode.setMethodEmitter(method);
         // Mark end for variable tables.
         method.begin();
@@ -983,7 +1018,7 @@
     }
 
     @Override
-    public Node leave(final FunctionNode functionNode) {
+    public Node leaveFunctionNode(final FunctionNode functionNode) {
         // Mark end for variable tables.
         method.label(functionNode.getBreakLabel());
 
@@ -1001,16 +1036,18 @@
             throw e;
         }
 
+        lexicalContext.pop(functionNode);
+        LOG.info("=== END " + functionNode.getName());
         return functionNode;
     }
 
     @Override
-    public Node enter(final IdentNode identNode) {
+    public Node enterIdentNode(final IdentNode identNode) {
         return null;
     }
 
     @Override
-    public Node enter(final IfNode ifNode) {
+    public Node enterIfNode(final IfNode ifNode) {
         if (ifNode.testResolved()) {
             return null;
         }
@@ -1049,7 +1086,7 @@
     }
 
     @Override
-    public Node enter(final IndexNode indexNode) {
+    public Node enterIndexNode(final IndexNode indexNode) {
         if (indexNode.testResolved()) {
             return null;
         }
@@ -1060,7 +1097,7 @@
     }
 
     @Override
-    public Node enter(final LineNumberNode lineNumberNode) {
+    public Node enterLineNumberNode(final LineNumberNode lineNumberNode) {
         if (lineNumberNode.testResolved()) {
             return null;
         }
@@ -1068,7 +1105,6 @@
         final Label label = new Label("line:" + lineNumberNode.getLineNumber() + " (" + getCurrentFunctionNode().getName() + ")");
         method.label(label);
         method.lineNumber(lineNumberNode.getLineNumber(), label);
-
         return null;
     }
 
@@ -1106,7 +1142,7 @@
                     final String name      = getCurrentFunctionNode().uniqueName(SPLIT_PREFIX.tag());
                     final String signature = methodDescriptor(type, Object.class, ScriptFunction.class, ScriptObject.class, type);
 
-                    method = getCurrentCompileUnit().getClassEmitter().method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), name, signature);
+                    setCurrentMethodEmitter(getCurrentCompileUnit().getClassEmitter().method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), name, signature));
                     method.setFunctionNode(getCurrentFunctionNode());
                     method.begin();
 
@@ -1212,7 +1248,7 @@
             method.invokestatic(unitClassName, methodName, methodDescriptor(cls, int.class));
             classEmitter.needGetConstantMethod(cls);
         } else {
-            method.loadConstants(unitClassName).load(index).arrayload();
+            method.loadConstants().load(index).arrayload();
             if (cls != Object.class) {
                 method.checkcast(cls);
             }
@@ -1292,14 +1328,14 @@
 
     @SuppressWarnings("rawtypes")
     @Override
-    public Node enter(final LiteralNode literalNode) {
+    public Node enterLiteralNode(final LiteralNode literalNode) {
         assert literalNode.getSymbol() != null : literalNode + " has no symbol";
         load(literalNode).store(literalNode.getSymbol());
         return null;
     }
 
     @Override
-    public Node enter(final ObjectNode objectNode) {
+    public Node enterObjectNode(final ObjectNode objectNode) {
         if (objectNode.testResolved()) {
             return null;
         }
@@ -1372,10 +1408,10 @@
         }
 
         for (final Node element : elements) {
-            final PropertyNode  propertyNode = (PropertyNode)element;
-            final Object        key          = propertyNode.getKey();
-            final ReferenceNode getter       = (ReferenceNode)propertyNode.getGetter();
-            final ReferenceNode setter       = (ReferenceNode)propertyNode.getSetter();
+            final PropertyNode propertyNode = (PropertyNode)element;
+            final Object       key          = propertyNode.getKey();
+            final FunctionNode getter       = (FunctionNode)propertyNode.getGetter();
+            final FunctionNode setter       = (FunctionNode)propertyNode.getSetter();
 
             if (getter == null && setter == null) {
                 continue;
@@ -1404,18 +1440,7 @@
     }
 
     @Override
-    public Node enter(final ReferenceNode referenceNode) {
-        if (referenceNode.testResolved()) {
-            return null;
-        }
-
-        newFunctionObject(referenceNode.getReference());
-
-        return null;
-    }
-
-    @Override
-    public Node enter(final ReturnNode returnNode) {
+    public Node enterReturnNode(final ReturnNode returnNode) {
         if (returnNode.testResolved()) {
             return null;
         }
@@ -1556,7 +1581,7 @@
     }
 
     @Override
-    public Node enter(final RuntimeNode runtimeNode) {
+    public Node enterRuntimeNode(final RuntimeNode runtimeNode) {
         if (runtimeNode.testResolved()) {
             return null;
         }
@@ -1637,7 +1662,7 @@
     }
 
     @Override
-    public Node enter(final SplitNode splitNode) {
+    public Node enterSplitNode(final SplitNode splitNode) {
         if (splitNode.testResolved()) {
             return null;
         }
@@ -1706,7 +1731,7 @@
     }
 
     @Override
-    public Node leave(final SplitNode splitNode) {
+    public Node leaveSplitNode(final SplitNode splitNode) {
         try {
             // Wrap up this method.
             method.loadResult();
@@ -1763,7 +1788,7 @@
     }
 
     @Override
-    public Node enter(final SwitchNode switchNode) {
+    public Node enterSwitchNode(final SwitchNode switchNode) {
         if (switchNode.testResolved()) {
             return null;
         }
@@ -1895,7 +1920,7 @@
     }
 
     @Override
-    public Node enter(final ThrowNode throwNode) {
+    public Node enterThrowNode(final ThrowNode throwNode) {
         if (throwNode.testResolved()) {
             return null;
         }
@@ -1922,7 +1947,7 @@
     }
 
     @Override
-    public Node enter(final TryNode tryNode) {
+    public Node enterTryNode(final TryNode tryNode) {
         if (tryNode.testResolved()) {
             return null;
         }
@@ -1955,7 +1980,7 @@
             setCurrentBlock(catchBlock);
 
             try {
-                enter(catchBlock);
+                enterBlock(catchBlock);
 
                 final CatchNode catchNode          = (CatchNode)catchBlocks.get(i).getStatements().get(0);
                 final IdentNode exception          = catchNode.getException();
@@ -1966,6 +1991,7 @@
                     // Generate catch body (inlined finally) and rethrow exception
                     catchBody.accept(this);
                     method.load(symbol).athrow();
+                    lexicalContext.pop(catchBlock);
                     continue;
                 }
 
@@ -2012,7 +2038,7 @@
                     }
                 }
 
-                leave(catchBlock);
+                leaveBlock(catchBlock);
             } finally {
                 setCurrentBlock(saveBlock);
             }
@@ -2027,7 +2053,7 @@
     }
 
     @Override
-    public Node enter(final VarNode varNode) {
+    public Node enterVarNode(final VarNode varNode) {
         final Node init = varNode.getInit();
 
         if (varNode.testResolved() || init == null) {
@@ -2049,7 +2075,7 @@
             int flags = CALLSITE_SCOPE | getCallSiteFlags();
             final IdentNode identNode = varNode.getName();
             final Type type = identNode.getType();
-            if (varSymbol.isFastScope(getCurrentFunctionNode())) {
+            if (isFastScope(varSymbol)) {
                 storeFastScopeVar(type, varSymbol, flags);
             } else {
                 method.dynamicSet(type, identNode.getName(), flags);
@@ -2065,7 +2091,7 @@
     }
 
     @Override
-    public Node enter(final WhileNode whileNode) {
+    public Node enterWhileNode(final WhileNode whileNode) {
         if (whileNode.testResolved()) {
             return null;
         }
@@ -2098,7 +2124,7 @@
     }
 
     @Override
-    public Node enter(final WithNode withNode) {
+    public Node enterWithNode(final WithNode withNode) {
         if (withNode.testResolved()) {
             return null;
         }
@@ -2864,7 +2890,7 @@
      * Ternary visits.
      */
     @Override
-    public Node enter(final TernaryNode ternaryNode) {
+    public Node enterTernaryNode(final TernaryNode ternaryNode) {
         if (ternaryNode.testResolved()) {
             return null;
         }
@@ -3060,7 +3086,7 @@
 
             target.accept(new NodeVisitor(getCurrentCompileUnit(), method) {
                 @Override
-                public Node enter(final IdentNode node) {
+                public Node enterIdentNode(final IdentNode node) {
                     if (targetSymbol.isScope()) {
                         method.load(scopeSymbol);
                         depth++;
@@ -3083,13 +3109,13 @@
                 }
 
                 @Override
-                public Node enter(final AccessNode node) {
+                public Node enterAccessNode(final AccessNode node) {
                     enterBaseNode();
                     return null;
                 }
 
                 @Override
-                public Node enter(final IndexNode node) {
+                public Node enterIndexNode(final IndexNode node) {
                     enterBaseNode();
 
                     final Node index = node.getIndex();
@@ -3155,8 +3181,6 @@
         }
 
         private void epilogue() {
-            final FunctionNode currentFunction = getCurrentFunctionNode();
-
             /**
              * Take the original target args from the stack and use them
              * together with the value to be stored to emit the store code
@@ -3174,7 +3198,7 @@
                 }
 
                 @Override
-                public Node enter(final UnaryNode node) {
+                public Node enterUnaryNode(final UnaryNode node) {
                     if(node.tokenType() == TokenType.CONVERT && node.getSymbol() != null) {
                         method.convert(node.rhs().getType());
                     }
@@ -3182,11 +3206,11 @@
                 }
 
                 @Override
-                public Node enter(final IdentNode node) {
+                public Node enterIdentNode(final IdentNode node) {
                     final Symbol symbol = node.getSymbol();
                     assert symbol != null;
                     if (symbol.isScope()) {
-                        if (symbol.isFastScope(currentFunction)) {
+                        if (isFastScope(symbol)) {
                             storeFastScopeVar(node.getType(), symbol, CALLSITE_SCOPE | getCallSiteFlags());
                         } else {
                             method.dynamicSet(node.getType(), node.getName(), CALLSITE_SCOPE | getCallSiteFlags());
@@ -3199,13 +3223,13 @@
                 }
 
                 @Override
-                public Node enter(final AccessNode node) {
+                public Node enterAccessNode(final AccessNode node) {
                     method.dynamicSet(node.getProperty().getType(), node.getProperty().getName(), getCallSiteFlags());
                     return null;
                 }
 
                 @Override
-                public Node enter(final IndexNode node) {
+                public Node enterIndexNode(final IndexNode node) {
                     method.dynamicSetIndex(getCallSiteFlags());
                     return null;
                 }
--- a/nashorn/src/jdk/nashorn/internal/codegen/CompilationPhase.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CompilationPhase.java	Sat Mar 23 00:58:39 2013 +0100
@@ -14,16 +14,16 @@
 import java.util.EnumSet;
 import java.util.HashSet;
 import java.util.Set;
-
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.CallNode;
 import jdk.nashorn.internal.ir.FunctionNode;
 import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
+import jdk.nashorn.internal.ir.LexicalContext;
 import jdk.nashorn.internal.ir.Node;
-import jdk.nashorn.internal.ir.ReferenceNode;
-import jdk.nashorn.internal.ir.visitor.NodeVisitor;
 import jdk.nashorn.internal.ir.debug.ASTWriter;
 import jdk.nashorn.internal.ir.debug.PrintVisitor;
+import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
+import jdk.nashorn.internal.ir.visitor.NodeVisitor;
 import jdk.nashorn.internal.runtime.ECMAErrors;
 import jdk.nashorn.internal.runtime.ScriptEnvironment;
 import jdk.nashorn.internal.runtime.Timing;
@@ -65,17 +65,17 @@
             outermostFunctionNode.accept(new NodeVisitor() {
                 // self references are done with invokestatic and thus cannot have trampolines - never lazy
                 @Override
-                public Node enter(final CallNode node) {
+                public Node enterCallNode(final CallNode node) {
                     final Node callee = node.getFunction();
-                    if (callee instanceof ReferenceNode) {
-                        neverLazy.add(((ReferenceNode)callee).getReference());
+                    if (callee instanceof FunctionNode) {
+                        neverLazy.add(((FunctionNode)callee));
                         return null;
                     }
                     return node;
                 }
 
                 @Override
-                public Node enter(final FunctionNode node) {
+                public Node enterFunctionNode(final FunctionNode node) {
                     if (node == outermostFunctionNode) {
                         return node;
                     }
@@ -94,15 +94,24 @@
                 lazy.remove(node);
             }
 
-            for (final FunctionNode node : lazy) {
-                Compiler.LOG.fine("Marking " + node.getName() + " as lazy");
-                node.setIsLazy(true);
-                final FunctionNode parent = node.findParentFunction();
-                if (parent != null) {
-                    Compiler.LOG.fine("Marking " + parent.getName() + " as having lazy children - it needs scope for all variables");
-                    parent.setHasLazyChildren();
+            outermostFunctionNode.accept(new NodeOperatorVisitor() {
+                private final LexicalContext lexicalContext = new LexicalContext();
+                @Override
+                public Node enterFunctionNode(FunctionNode functionNode) {
+                    lexicalContext.push(functionNode);
+                    if(lazy.contains(functionNode)) {
+                        Compiler.LOG.fine("Marking " + functionNode.getName() + " as lazy");
+                        functionNode.setIsLazy(true);
+                        lexicalContext.getParentFunction(functionNode).setHasLazyChildren();
+                    }
+                    return functionNode;
                 }
-            }
+                @Override
+                public Node leaveFunctionNode(FunctionNode functionNode) {
+                    lexicalContext.pop(functionNode);
+                    return functionNode;
+                }
+            });
         }
 
         @Override
@@ -241,6 +250,16 @@
                 final CodeGenerator codegen = new CodeGenerator(compiler);
                 fn.accept(codegen);
                 codegen.generateScopeCalls();
+                fn.accept(new NodeOperatorVisitor() {
+                    @Override
+                    public Node enterFunctionNode(FunctionNode functionNode) {
+                        if(functionNode.isLazy()) {
+                            functionNode.resetResolved();
+                            return null;
+                        }
+                        return fn;
+                    }
+                });
 
             } catch (final VerifyError e) {
                 if (env._verify_code || env._print_code) {
--- a/nashorn/src/jdk/nashorn/internal/codegen/Compiler.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Compiler.java	Sat Mar 23 00:58:39 2013 +0100
@@ -46,7 +46,6 @@
 import java.util.Map.Entry;
 import java.util.Set;
 import java.util.logging.Level;
-
 import jdk.internal.dynalink.support.NameCodec;
 import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
 import jdk.nashorn.internal.codegen.types.Type;
@@ -383,7 +382,7 @@
 
         functionNode.accept(new NodeVisitor() {
             @Override
-            public Node enter(final FunctionNode node) {
+            public Node enterFunctionNode(final FunctionNode node) {
                 if (node.isLazy()) {
                     return null;
                 }
--- a/nashorn/src/jdk/nashorn/internal/codegen/FinalizeTypes.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/codegen/FinalizeTypes.java	Sat Mar 23 00:58:39 2013 +0100
@@ -34,20 +34,20 @@
 import jdk.nashorn.internal.ir.Block;
 import jdk.nashorn.internal.ir.CallNode;
 import jdk.nashorn.internal.ir.CallNode.EvalArgs;
-import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
 import jdk.nashorn.internal.ir.CaseNode;
 import jdk.nashorn.internal.ir.CatchNode;
 import jdk.nashorn.internal.ir.DoWhileNode;
 import jdk.nashorn.internal.ir.ExecuteNode;
 import jdk.nashorn.internal.ir.ForNode;
 import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
 import jdk.nashorn.internal.ir.IdentNode;
 import jdk.nashorn.internal.ir.IfNode;
 import jdk.nashorn.internal.ir.IndexNode;
+import jdk.nashorn.internal.ir.LexicalContext;
 import jdk.nashorn.internal.ir.LiteralNode;
 import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
 import jdk.nashorn.internal.ir.Node;
-import jdk.nashorn.internal.ir.ReferenceNode;
 import jdk.nashorn.internal.ir.ReturnNode;
 import jdk.nashorn.internal.ir.RuntimeNode;
 import jdk.nashorn.internal.ir.RuntimeNode.Request;
@@ -85,11 +85,13 @@
 
     private static final DebugLogger LOG = new DebugLogger("finalize");
 
+    private final LexicalContext lexicalContext = new LexicalContext();
+
     FinalizeTypes() {
     }
 
     @Override
-    public Node leave(final CallNode callNode) {
+    public Node leaveCallNode(final CallNode callNode) {
         final EvalArgs evalArgs = callNode.getEvalArgs();
         if (evalArgs != null) {
             evalArgs.setCode(evalArgs.getCode().accept(this));
@@ -97,15 +99,14 @@
 
         // AccessSpecializer - call return type may change the access for this location
         final Node function = callNode.getFunction();
-        if (function instanceof ReferenceNode) {
-            setTypeOverride(callNode, ((ReferenceNode)function).getReference().getType());
+        if (function instanceof FunctionNode) {
+            return setTypeOverride(callNode, ((FunctionNode)function).getReturnType());
         }
         return callNode;
     }
 
     private Node leaveUnary(final UnaryNode unaryNode) {
-        unaryNode.setRHS(convert(unaryNode.rhs(), unaryNode.getType()));
-        return unaryNode;
+        return unaryNode.setRHS(convert(unaryNode.rhs(), unaryNode.getType()));
     }
 
     @Override
@@ -126,8 +127,7 @@
 
     @Override
     public Node leaveDECINC(final UnaryNode unaryNode) {
-        specialize(unaryNode);
-        return unaryNode;
+        return specialize(unaryNode).node;
     }
 
     @Override
@@ -159,9 +159,7 @@
             }
         }
 
-        binaryNode.setLHS(convert(lhs, type));
-        binaryNode.setRHS(convert(rhs, type));
-        return binaryNode;
+        return binaryNode.setLHS(convert(lhs, type)).setRHS(convert(rhs, type));
     }
 
     @Override
@@ -171,12 +169,13 @@
 
     @Override
     public Node leaveASSIGN(final BinaryNode binaryNode) {
-        Type destType = specialize(binaryNode);
+        final SpecializedNode specialized = specialize(binaryNode);
+        final BinaryNode specBinaryNode = (BinaryNode)specialized.node;
+        Type destType = specialized.type;
         if (destType == null) {
-            destType = binaryNode.getType();
+            destType = specBinaryNode.getType();
         }
-        binaryNode.setRHS(convert(binaryNode.rhs(), destType));
-        return binaryNode;
+        return specBinaryNode.setRHS(convert(specBinaryNode.rhs(), destType));
     }
 
     @Override
@@ -255,21 +254,21 @@
     @Override
     public Node leaveCOMMALEFT(final BinaryNode binaryNode) {
         assert binaryNode.getSymbol() != null;
-        binaryNode.setRHS(discard(binaryNode.rhs()));
+        final BinaryNode newBinaryNode = (BinaryNode)binaryNode.setRHS(discard(binaryNode.rhs()));
         // AccessSpecializer - the type of lhs, which is the remaining value of this node may have changed
         // in that case, update the node type as well
-        propagateType(binaryNode, binaryNode.lhs().getType());
-        return binaryNode;
+        propagateType(newBinaryNode, newBinaryNode.lhs().getType());
+        return newBinaryNode;
     }
 
     @Override
     public Node leaveCOMMARIGHT(final BinaryNode binaryNode) {
         assert binaryNode.getSymbol() != null;
-        binaryNode.setLHS(discard(binaryNode.lhs()));
+        final BinaryNode newBinaryNode = binaryNode.setLHS(discard(binaryNode.lhs()));
         // AccessSpecializer - the type of rhs, which is the remaining value of this node may have changed
         // in that case, update the node type as well
-        propagateType(binaryNode, binaryNode.rhs().getType());
-        return binaryNode;
+        propagateType(newBinaryNode, newBinaryNode.rhs().getType());
+        return newBinaryNode;
     }
 
     @Override
@@ -355,13 +354,20 @@
     }
 
     @Override
-    public Node enter(final Block block) {
+    public Node enterBlock(final Block block) {
+        lexicalContext.push(block);
         updateSymbols(block);
         return block;
     }
 
     @Override
-    public Node leave(final CatchNode catchNode) {
+    public Node leaveBlock(Block block) {
+        lexicalContext.pop(block);
+        return super.leaveBlock(block);
+    }
+
+    @Override
+    public Node leaveCatchNode(final CatchNode catchNode) {
         final Node exceptionCondition = catchNode.getExceptionCondition();
         if (exceptionCondition != null) {
             catchNode.setExceptionCondition(convert(exceptionCondition, Type.BOOLEAN));
@@ -370,23 +376,23 @@
     }
 
     @Override
-    public Node enter(final DoWhileNode doWhileNode) {
-        return enter((WhileNode)doWhileNode);
+    public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
+        return enterWhileNode(doWhileNode);
     }
 
     @Override
-    public Node leave(final DoWhileNode doWhileNode) {
-        return leave((WhileNode)doWhileNode);
+    public Node leaveDoWhileNode(final DoWhileNode doWhileNode) {
+        return leaveWhileNode(doWhileNode);
     }
 
     @Override
-    public Node leave(final ExecuteNode executeNode) {
+    public Node leaveExecuteNode(final ExecuteNode executeNode) {
         executeNode.setExpression(discard(executeNode.getExpression()));
         return executeNode;
     }
 
     @Override
-    public Node leave(final ForNode forNode) {
+    public Node leaveForNode(final ForNode forNode) {
         final Node init   = forNode.getInit();
         final Node test   = forNode.getTest();
         final Node modify = forNode.getModify();
@@ -414,11 +420,12 @@
     }
 
     @Override
-    public Node enter(final FunctionNode functionNode) {
+    public Node enterFunctionNode(final FunctionNode functionNode) {
         if (functionNode.isLazy()) {
             return null;
         }
 
+        lexicalContext.push(functionNode);
         // If the function doesn't need a callee, we ensure its __callee__ symbol doesn't get a slot. We can't do
         // this earlier, as access to scoped variables, self symbol, etc. in previous phases can all trigger the
         // need for the callee.
@@ -439,14 +446,20 @@
     }
 
     @Override
-    public Node leave(final IfNode ifNode) {
+    public Node leaveFunctionNode(FunctionNode functionNode) {
+        lexicalContext.pop(functionNode);
+        return super.leaveFunctionNode(functionNode);
+    }
+
+    @Override
+    public Node leaveIfNode(final IfNode ifNode) {
         ifNode.setTest(convert(ifNode.getTest(), Type.BOOLEAN));
         return ifNode;
     }
 
     @SuppressWarnings("rawtypes")
     @Override
-    public Node enter(final LiteralNode literalNode) {
+    public Node enterLiteralNode(final LiteralNode literalNode) {
         if (literalNode instanceof ArrayLiteralNode) {
             final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode)literalNode;
             final Node[]           array            = arrayLiteralNode.getValue();
@@ -464,7 +477,7 @@
     }
 
     @Override
-    public Node leave(final ReturnNode returnNode) {
+    public Node leaveReturnNode(final ReturnNode returnNode) {
         final Node expr = returnNode.getExpression();
         if (expr != null) {
             returnNode.setExpression(convert(expr, getCurrentFunctionNode().getReturnType()));
@@ -473,7 +486,7 @@
     }
 
     @Override
-    public Node leave(final RuntimeNode runtimeNode) {
+    public Node leaveRuntimeNode(final RuntimeNode runtimeNode) {
         final List<Node> args = runtimeNode.getArgs();
         for (final Node arg : args) {
             assert !arg.getType().isUnknown();
@@ -482,7 +495,7 @@
     }
 
     @Override
-    public Node leave(final SwitchNode switchNode) {
+    public Node leaveSwitchNode(final SwitchNode switchNode) {
         final Node           expression  = switchNode.getExpression();
         final List<CaseNode> cases       = switchNode.getCases();
         final boolean        allInteger  = switchNode.getTag().getSymbolType().isInteger();
@@ -501,33 +514,34 @@
     }
 
     @Override
-    public Node leave(final TernaryNode ternaryNode) {
-        ternaryNode.setLHS(convert(ternaryNode.lhs(), Type.BOOLEAN));
-        return ternaryNode;
+    public Node leaveTernaryNode(final TernaryNode ternaryNode) {
+        return ternaryNode.setLHS(convert(ternaryNode.lhs(), Type.BOOLEAN));
     }
 
     @Override
-    public Node leave(final ThrowNode throwNode) {
+    public Node leaveThrowNode(final ThrowNode throwNode) {
         throwNode.setExpression(convert(throwNode.getExpression(), Type.OBJECT));
         return throwNode;
     }
 
     @Override
-    public Node leave(final VarNode varNode) {
+    public Node leaveVarNode(final VarNode varNode) {
         final Node rhs = varNode.getInit();
         if (rhs != null) {
-            Type destType = specialize(varNode);
+            final SpecializedNode specialized = specialize(varNode);
+            final VarNode specVarNode = (VarNode)specialized.node;
+            Type destType = specialized.type;
             if (destType == null) {
-                destType = varNode.getType();
+                destType = specVarNode.getType();
             }
-            assert varNode.hasType() : varNode + " doesn't have a type";
-            varNode.setInit(convert(rhs, destType));
+            assert specVarNode.hasType() : specVarNode + " doesn't have a type";
+            return specVarNode.setInit(convert(rhs, destType));
         }
         return varNode;
     }
 
     @Override
-    public Node leave(final WhileNode whileNode) {
+    public Node leaveWhileNode(final WhileNode whileNode) {
         final Node test = whileNode.getTest();
         if (test != null) {
             whileNode.setTest(convert(test, Type.BOOLEAN));
@@ -536,7 +550,7 @@
     }
 
     @Override
-    public Node leave(final WithNode withNode) {
+    public Node leaveWithNode(final WithNode withNode) {
         withNode.setExpression(convert(withNode.getExpression(), Type.OBJECT));
         return withNode;
     }
@@ -555,14 +569,14 @@
      * that scope and slot information is correct for every symbol
      * @param block block for which to to finalize type info.
      */
-    private static void updateSymbols(final Block block) {
+    private void updateSymbols(final Block block) {
         if (!block.needsScope()) {
             return; // nothing to do
         }
 
-        assert !(block instanceof FunctionNode) || block.getFunction() == block;
+        final FunctionNode functionNode = lexicalContext.getFunction(block);
+        assert !(block instanceof FunctionNode) || functionNode == block;
 
-        final FunctionNode functionNode   = block.getFunction();
         final List<Symbol> symbols        = block.getFrame().getSymbols();
         final boolean      allVarsInScope = functionNode.allVarsInScope();
         final boolean      isVarArg       = functionNode.isVarArg();
@@ -631,10 +645,7 @@
             break;
         }
 
-        binaryNode.setLHS(convert(lhs, widest));
-        binaryNode.setRHS(convert(rhs, widest));
-
-        return binaryNode;
+        return binaryNode.setLHS(convert(lhs, widest)).setRHS(convert(rhs, widest));
     }
 
     /**
@@ -656,9 +667,7 @@
     }
 
     private Node leaveBinary(final BinaryNode binaryNode, final Type lhsType, final Type rhsType) {
-        binaryNode.setLHS(convert(binaryNode.lhs(), lhsType));
-        binaryNode.setRHS(convert(binaryNode.rhs(), rhsType));
-        return binaryNode;
+        return binaryNode.setLHS(convert(binaryNode.lhs(), lhsType)).setRHS(convert(binaryNode.rhs(), rhsType));
     }
 
     /**
@@ -679,7 +688,7 @@
             }
 
             @Override
-            public Node enter(final IdentNode identNode) {
+            public Node enterIdentNode(final IdentNode identNode) {
                 if (!exclude.contains(identNode)) {
                     setCanBePrimitive(identNode.getSymbol());
                 }
@@ -687,26 +696,36 @@
             }
 
             @Override
-            public Node enter(final AccessNode accessNode) {
+            public Node enterAccessNode(final AccessNode accessNode) {
                 setCanBePrimitive(accessNode.getProperty().getSymbol());
                 return null;
             }
 
             @Override
-            public Node enter(final IndexNode indexNode) {
+            public Node enterIndexNode(final IndexNode indexNode) {
                 exclude.add(indexNode.getBase()); //prevent array base node to be flagged as primitive, but k in a[k++] is fine
                 return indexNode;
             }
         });
     }
 
-    private static Type specialize(final Assignment<?> assignment) {
+    private static class SpecializedNode {
+        final Node node;
+        final Type type;
+
+        SpecializedNode(Node node, Type type) {
+            this.node = node;
+            this.type = type;
+        }
+    }
+
+    private static <T extends Node> SpecializedNode specialize(final Assignment<T> assignment) {
         final Node node = ((Node)assignment);
-        final Node lhs = assignment.getAssignmentDest();
+        final T lhs = assignment.getAssignmentDest();
         final Node rhs = assignment.getAssignmentSource();
 
         if (!canHaveCallSiteType(lhs)) {
-            return null;
+            return new SpecializedNode(node, null);
         }
 
         final Type to;
@@ -718,13 +737,13 @@
 
         if (!isSupportedCallSiteType(to)) {
             //meaningless to specialize to boolean or object
-            return null;
+            return new SpecializedNode(node, null);
         }
 
-        setTypeOverride(lhs, to);
-        propagateType(node, to);
+        final Node newNode = assignment.setAssignmentDest(setTypeOverride(lhs, to));
+        propagateType(newNode, to);
 
-        return to;
+        return new SpecializedNode(newNode, to);
     }
 
 
@@ -736,7 +755,7 @@
      * @return true if node can have a callsite type
      */
     private static boolean canHaveCallSiteType(final Node node) {
-        return node instanceof TypeOverride && ((TypeOverride)node).canHaveCallSiteType();
+        return node instanceof TypeOverride && ((TypeOverride<?>)node).canHaveCallSiteType();
     }
 
     /**
@@ -762,7 +781,8 @@
      * @param node    node for which to change type
      * @param to      new type
      */
-    private static void setTypeOverride(final Node node, final Type to) {
+    @SuppressWarnings("unchecked")
+    private static <T extends Node> T setTypeOverride(final T node, final Type to) {
         final Type from = node.getType();
         if (!node.getType().equals(to)) {
             LOG.info("Changing call override type for '" + node + "' from " + node.getType() + " to " + to);
@@ -771,7 +791,7 @@
             }
         }
         LOG.info("Type override for lhs in '" + node + "' => " + to);
-        ((TypeOverride)node).setType(to);
+        return ((TypeOverride<T>)node).setType(to);
     }
 
     /**
@@ -816,8 +836,8 @@
             }
         } else {
             if (canHaveCallSiteType(node) && isSupportedCallSiteType(to)) {
-                setTypeOverride(node, to);
-                return resultNode;
+                assert node instanceof TypeOverride;
+                return setTypeOverride(node, to);
             }
             resultNode = new UnaryNode(node.getSource(), Token.recast(node.getToken(), TokenType.CONVERT), node);
         }
--- a/nashorn/src/jdk/nashorn/internal/codegen/FoldConstants.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/codegen/FoldConstants.java	Sat Mar 23 00:58:39 2013 +0100
@@ -31,12 +31,12 @@
 import jdk.nashorn.internal.ir.EmptyNode;
 import jdk.nashorn.internal.ir.ExecuteNode;
 import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
 import jdk.nashorn.internal.ir.IfNode;
 import jdk.nashorn.internal.ir.LiteralNode;
 import jdk.nashorn.internal.ir.Node;
 import jdk.nashorn.internal.ir.TernaryNode;
 import jdk.nashorn.internal.ir.UnaryNode;
-import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
 import jdk.nashorn.internal.runtime.DebugLogger;
 import jdk.nashorn.internal.runtime.JSType;
@@ -54,7 +54,7 @@
     }
 
     @Override
-    public Node leave(final UnaryNode unaryNode) {
+    public Node leaveUnaryNode(final UnaryNode unaryNode) {
         final LiteralNode<?> literalNode = new UnaryNodeConstantEvaluator(unaryNode).eval();
         if (literalNode != null) {
             LOG.info("Unary constant folded " + unaryNode + " to " + literalNode);
@@ -64,7 +64,7 @@
     }
 
     @Override
-    public Node leave(final BinaryNode binaryNode) {
+    public Node leaveBinaryNode(final BinaryNode binaryNode) {
         final LiteralNode<?> literalNode = new BinaryNodeConstantEvaluator(binaryNode).eval();
         if (literalNode != null) {
             LOG.info("Binary constant folded " + binaryNode + " to " + literalNode);
@@ -74,7 +74,7 @@
     }
 
     @Override
-    public Node enter(final FunctionNode functionNode) {
+    public Node enterFunctionNode(final FunctionNode functionNode) {
         if (functionNode.isLazy()) {
             return null;
         }
@@ -82,13 +82,13 @@
     }
 
     @Override
-    public Node leave(final FunctionNode functionNode) {
+    public Node leaveFunctionNode(final FunctionNode functionNode) {
         functionNode.setState(CompilationState.CONSTANT_FOLDED);
         return functionNode;
     }
 
     @Override
-    public Node leave(final IfNode ifNode) {
+    public Node leaveIfNode(final IfNode ifNode) {
         final Node test = ifNode.getTest();
         if (test instanceof LiteralNode) {
             final Block shortCut = ((LiteralNode<?>)test).isTrue() ? ifNode.getPass() : ifNode.getFail();
@@ -101,7 +101,7 @@
     }
 
     @Override
-    public Node leave(final TernaryNode ternaryNode) {
+    public Node leaveTernaryNode(final TernaryNode ternaryNode) {
         final Node test = ternaryNode.lhs();
         if (test instanceof LiteralNode) {
             return ((LiteralNode<?>)test).isTrue() ? ternaryNode.rhs() : ternaryNode.third();
--- a/nashorn/src/jdk/nashorn/internal/codegen/FunctionSignature.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/codegen/FunctionSignature.java	Sat Mar 23 00:58:39 2013 +0100
@@ -155,7 +155,7 @@
             true,
             functionNode.needsCallee(),
             functionNode.getReturnType(),
-            (functionNode.isVarArg() && !functionNode.isScript()) ?
+            (functionNode.isVarArg() && !functionNode.isProgram()) ?
                 null :
                 functionNode.getParameters());
     }
--- a/nashorn/src/jdk/nashorn/internal/codegen/Lower.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Lower.java	Sat Mar 23 00:58:39 2013 +0100
@@ -37,8 +37,8 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Deque;
+import java.util.Iterator;
 import java.util.List;
-import jdk.nashorn.internal.ir.AccessNode;
 import jdk.nashorn.internal.ir.BaseNode;
 import jdk.nashorn.internal.ir.BinaryNode;
 import jdk.nashorn.internal.ir.Block;
@@ -52,11 +52,12 @@
 import jdk.nashorn.internal.ir.ExecuteNode;
 import jdk.nashorn.internal.ir.ForNode;
 import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
 import jdk.nashorn.internal.ir.IdentNode;
 import jdk.nashorn.internal.ir.IfNode;
-import jdk.nashorn.internal.ir.IndexNode;
 import jdk.nashorn.internal.ir.LabelNode;
 import jdk.nashorn.internal.ir.LabeledNode;
+import jdk.nashorn.internal.ir.LexicalContext;
 import jdk.nashorn.internal.ir.LineNumberNode;
 import jdk.nashorn.internal.ir.LiteralNode;
 import jdk.nashorn.internal.ir.Node;
@@ -69,7 +70,6 @@
 import jdk.nashorn.internal.ir.VarNode;
 import jdk.nashorn.internal.ir.WhileNode;
 import jdk.nashorn.internal.ir.WithNode;
-import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
 import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
 import jdk.nashorn.internal.parser.Token;
@@ -103,6 +103,8 @@
 
     private List<Node> statements;
 
+    private LexicalContext lexicalContext = new LexicalContext();
+
     /**
      * Constructor.
      *
@@ -114,14 +116,15 @@
     }
 
     @Override
-    public Node enter(final Block block) {
+    public Node enterBlock(final Block block) {
         final Node       savedLastStatement = lastStatement;
         final List<Node> savedStatements    = statements;
-
+        lexicalContext.push(block);
         try {
             this.statements = new ArrayList<>();
+            NodeVisitor visitor = this;
             for (final Node statement : block.getStatements()) {
-                statement.accept(this);
+                statement.accept(visitor);
                 /*
                  * This is slightly unsound, for example if we have a loop with
                  * a guarded statement like if (x) continue in the body and the
@@ -133,7 +136,7 @@
                  */
                 if (lastStatement != null && lastStatement.isTerminal()) {
                     copyTerminal(block, lastStatement);
-                    break;
+                    visitor = new DeadCodeVarDeclarationVisitor();
                 }
             }
             block.setStatements(statements);
@@ -141,18 +144,19 @@
         } finally {
             this.statements = savedStatements;
             this.lastStatement = savedLastStatement;
+            lexicalContext.pop(block);
         }
 
         return null;
     }
 
     @Override
-    public Node enter(final BreakNode breakNode) {
+    public Node enterBreakNode(final BreakNode breakNode) {
         return enterBreakOrContinue(breakNode);
     }
 
     @Override
-    public Node enter(final CallNode callNode) {
+    public Node enterCallNode(final CallNode callNode) {
         final Node function = markerFunction(callNode.getFunction());
         callNode.setFunction(function);
         checkEval(callNode); //check if this is an eval call and store the information
@@ -160,44 +164,44 @@
     }
 
     @Override
-    public Node leave(final CaseNode caseNode) {
+    public Node leaveCaseNode(final CaseNode caseNode) {
         caseNode.copyTerminalFlags(caseNode.getBody());
         return caseNode;
     }
 
     @Override
-    public Node leave(final CatchNode catchNode) {
+    public Node leaveCatchNode(final CatchNode catchNode) {
         catchNode.copyTerminalFlags(catchNode.getBody());
         addStatement(catchNode);
         return catchNode;
     }
 
     @Override
-    public Node enter(final ContinueNode continueNode) {
+    public Node enterContinueNode(final ContinueNode continueNode) {
         return enterBreakOrContinue(continueNode);
     }
 
     @Override
-    public Node enter(final DoWhileNode doWhileNode) {
-        return enter((WhileNode)doWhileNode);
+    public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
+        return enterWhileNode(doWhileNode);
     }
 
     @Override
-    public Node leave(final DoWhileNode doWhileNode) {
-        return leave((WhileNode)doWhileNode);
+    public Node leaveDoWhileNode(final DoWhileNode doWhileNode) {
+        return leaveWhileNode(doWhileNode);
     }
 
     @Override
-    public Node enter(final EmptyNode emptyNode) {
+    public Node enterEmptyNode(final EmptyNode emptyNode) {
         return null;
     }
 
     @Override
-    public Node leave(final ExecuteNode executeNode) {
+    public Node leaveExecuteNode(final ExecuteNode executeNode) {
         final Node expr = executeNode.getExpression();
 
-        if (getCurrentFunctionNode().isScript()) {
-            if (!(expr instanceof Block)) {
+        if (getCurrentFunctionNode().isProgram()) {
+            if (!(expr instanceof Block) || expr instanceof FunctionNode) { // it's not a block, but can be a function
                 if (!isInternalExpression(expr) && !isEvalResultAssignment(expr)) {
                     executeNode.setExpression(new BinaryNode(executeNode.getSource(), Token.recast(executeNode.getToken(), TokenType.ASSIGN),
                             getCurrentFunctionNode().getResultNode(),
@@ -213,13 +217,13 @@
     }
 
     @Override
-    public Node enter(final ForNode forNode) {
+    public Node enterForNode(final ForNode forNode) {
         nest(forNode);
         return forNode;
     }
 
     @Override
-    public Node leave(final ForNode forNode) {
+    public Node leaveForNode(final ForNode forNode) {
         final Node  test = forNode.getTest();
         final Block body = forNode.getBody();
 
@@ -247,18 +251,16 @@
     }
 
     @Override
-    public Node enter(final FunctionNode functionNode) {
+    public Node enterFunctionNode(final FunctionNode functionNode) {
         LOG.info("START FunctionNode: " + functionNode.getName());
 
         if (functionNode.isLazy()) {
             LOG.info("LAZY: " + functionNode.getName());
             return null;
         }
-
+        lexicalContext.push(functionNode);
         initFunctionNode(functionNode);
 
-        Node initialEvalResult = LiteralNode.newInstance(functionNode, ScriptRuntime.UNDEFINED);
-
         nest(functionNode);
 
         /*
@@ -272,60 +274,40 @@
         statements    = new ArrayList<>();
         lastStatement = null;
 
-        // for initial eval result is the last declared function
-        for (final FunctionNode nestedFunction : functionNode.getFunctions()) {
-            final IdentNode ident = nestedFunction.getIdent();
-            if (ident != null && nestedFunction.isStatement()) {
-                initialEvalResult = new IdentNode(ident);
-            }
-        }
-
         if (functionNode.needsSelfSymbol()) {
             //function needs to start with var funcIdent = __callee_;
             statements.add(functionNode.getSelfSymbolInit().accept(this));
         }
 
+        NodeVisitor visitor = this;
         try {
-            // Every nested function needs a definition in the outer function with its name. Add these.
-            for (final FunctionNode nestedFunction : functionNode.getFunctions()) {
-                final VarNode varNode = nestedFunction.getFunctionVarNode();
-                if (varNode != null) {
-                    final LineNumberNode lineNumberNode = nestedFunction.getFunctionVarLineNumberNode();
-                    if (lineNumberNode != null) {
-                        lineNumberNode.accept(this);
-                    }
-                    varNode.accept(this);
-                    varNode.setIsFunctionVarNode();
+            //do the statements - this fills the block with code
+            boolean needsInitialEvalResult = functionNode.isProgram();
+            for (final Node statement : functionNode.getStatements()) {
+                // If this function is a program, then insert an assignment to the initial eval result after all
+                // function declarations.
+                if(needsInitialEvalResult && !(statement instanceof LineNumberNode || (statement instanceof VarNode && ((VarNode)statement).isFunctionDeclaration()))) {
+                    addInitialEvalResult(functionNode);
+                    needsInitialEvalResult = false;
                 }
-            }
-
-            if (functionNode.isScript()) {
-                new ExecuteNode(functionNode.getSource(), functionNode.getFirstToken(), functionNode.getFinish(), initialEvalResult).accept(this);
-            }
-
-            //do the statements - this fills the block with code
-            for (final Node statement : functionNode.getStatements()) {
-                statement.accept(this);
+                statement.accept(visitor);
                 //If there are unused terminated endpoints in the function, we need
                 // to add a "return undefined" in those places for correct semantics
                 LOG.info("Checking lastStatement="+lastStatement+" for terminal flags");
                 if (lastStatement != null && lastStatement.hasTerminalFlags()) {
                     copyTerminal(functionNode, lastStatement);
-                    break;
+                    assert !needsInitialEvalResult;
+                    visitor = new DeadCodeVarDeclarationVisitor();
                 }
             }
-
+            if(needsInitialEvalResult) {
+                addInitialEvalResult(functionNode);
+            }
             functionNode.setStatements(statements);
 
             if (!functionNode.isTerminal()) {
                 guaranteeReturn(functionNode);
             }
-
-            //lower all nested functions
-            for (final FunctionNode nestedFunction : functionNode.getFunctions()) {
-                nestedFunction.accept(this);
-            }
-
         } finally {
             statements    = savedStatements;
             lastStatement = savedLastStatement;
@@ -333,19 +315,67 @@
 
         LOG.info("END FunctionNode: " + functionNode.getName());
         unnest(functionNode);
+        lexicalContext.pop(functionNode);
 
         functionNode.setState(CompilationState.LOWERED);
 
         return null;
     }
 
+    /**
+     * This visitor is used to go over statements after a terminal statement. Those statements are dead code, but the
+     * var declarations in them still have the effect of declaring a local variable on the function level. Therefore,
+     * they aren't really dead code and must be preserved. Note that they're only preserved as no-op declarations; their
+     * initializers are wiped out as those are, in fact, dead code.
+     */
+    private class DeadCodeVarDeclarationVisitor extends NodeOperatorVisitor {
+        DeadCodeVarDeclarationVisitor() {
+        }
+
+        @Override
+        public Node enterVarNode(VarNode varNode) {
+            // Can't ever see a function declaration, as this visitor is only ever used after a terminal statement was
+            // encountered, and all function declarations precede any terminal statements.
+            assert !varNode.isFunctionDeclaration();
+            if(varNode.getInit() == null) {
+                // No initializer, just pass it to Lower.
+                return varNode.accept(Lower.this);
+            }
+            // Wipe out the initializer and then pass it to Lower.
+            return varNode.setInit(null).accept(Lower.this);
+        }
+    }
+
+    private void addInitialEvalResult(final FunctionNode functionNode) {
+        new ExecuteNode(functionNode.getSource(), functionNode.getFirstToken(), functionNode.getFinish(),
+                getInitialEvalResult(functionNode)).accept(this);
+    }
+
+    /**
+     * Result of initial result of evaluating a particular program, which is either the last function it declares, or
+     * undefined if it doesn't declare any functions.
+     * @param program
+     * @return the initial result of evaluating the program
+     */
+    private static Node getInitialEvalResult(final FunctionNode program) {
+        IdentNode lastFnName = null;
+        for (final FunctionNode fn : program.getDeclaredFunctions()) {
+            assert fn.isDeclared();
+            final IdentNode fnName = fn.getIdent();
+            if(fnName != null) {
+                lastFnName = fnName;
+            }
+        }
+        return lastFnName != null ? new IdentNode(lastFnName) : LiteralNode.newInstance(program, ScriptRuntime.UNDEFINED);
+    }
+
     @Override
-    public Node enter(final IfNode ifNode) {
+    public Node enterIfNode(final IfNode ifNode) {
         return nest(ifNode);
     }
 
     @Override
-    public Node leave(final IfNode ifNode) {
+    public Node leaveIfNode(final IfNode ifNode) {
         final Node pass = ifNode.getPass();
         final Node fail = ifNode.getFail();
 
@@ -360,7 +390,7 @@
     }
 
     @Override
-    public Node enter(LabelNode labelNode) {
+    public Node enterLabelNode(LabelNode labelNode) {
         final Block body = labelNode.getBody();
         body.accept(this);
         copyTerminal(labelNode, body);
@@ -369,13 +399,13 @@
     }
 
     @Override
-    public Node enter(final LineNumberNode lineNumberNode) {
+    public Node enterLineNumberNode(final LineNumberNode lineNumberNode) {
         addStatement(lineNumberNode, false); // don't put it in lastStatement cache
         return null;
     }
 
     @Override
-    public Node enter(final ReturnNode returnNode) {
+    public Node enterReturnNode(final ReturnNode returnNode) {
         final TryNode tryNode = returnNode.getTryChain();
         final Node    expr    = returnNode.getExpression();
 
@@ -413,19 +443,19 @@
     }
 
     @Override
-    public Node leave(final ReturnNode returnNode) {
+    public Node leaveReturnNode(final ReturnNode returnNode) {
         addStatement(returnNode); //ReturnNodes are always terminal, marked as such in constructor
         return returnNode;
     }
 
     @Override
-    public Node enter(final SwitchNode switchNode) {
+    public Node enterSwitchNode(final SwitchNode switchNode) {
         nest(switchNode);
         return switchNode;
     }
 
     @Override
-    public Node leave(final SwitchNode switchNode) {
+    public Node leaveSwitchNode(final SwitchNode switchNode) {
         unnest(switchNode);
 
         final List<CaseNode> cases       = switchNode.getCases();
@@ -446,13 +476,13 @@
     }
 
     @Override
-    public Node leave(final ThrowNode throwNode) {
+    public Node leaveThrowNode(final ThrowNode throwNode) {
         addStatement(throwNode); //ThrowNodes are always terminal, marked as such in constructor
         return throwNode;
     }
 
     @Override
-    public Node enter(final TryNode tryNode) {
+    public Node enterTryNode(final TryNode tryNode) {
         final Block  finallyBody = tryNode.getFinallyBody();
         final long   token       = tryNode.getToken();
         final int    finish      = tryNode.getFinish();
@@ -538,26 +568,19 @@
 
             // set outer tryNode's body to innerTryNode
             final Block outerBody;
-            outerBody = new Block(source, token, finish, tryNode.getBody().getParent(), getCurrentFunctionNode());
+            outerBody = new Block(source, token, finish);
             outerBody.setStatements(new ArrayList<Node>(Arrays.asList(innerTryNode)));
             tryNode.setBody(outerBody);
             tryNode.setCatchBlocks(null);
-
-            // now before we go on, we have to fix the block parents
-            // (we repair the block tree after the insertion so that all references are intact)
-            innerTryNode.getBody().setParent(tryNode.getBody());
-            for (final Block block : innerTryNode.getCatchBlocks()) {
-                block.setParent(tryNode.getBody());
-            }
         }
 
         // create a catch-all that inlines finally and rethrows
 
-        final Block catchBlock      = new Block(source, token, finish, getCurrentBlock(), getCurrentFunctionNode());
+        final Block catchBlock      = new Block(source, token, finish);
         //this catch block should get define symbol
 
-        final Block catchBody       = new Block(source, token, finish, catchBlock, getCurrentFunctionNode());
-        final Node  catchAllFinally = finallyBody.clone();
+        final Block catchBody       = new Block(source, token, finish);
+        final Node  catchAllFinally = finallyBody.copy();
 
         catchBody.addStatement(new ExecuteNode(source, finallyBody.getToken(), finallyBody.getFinish(), catchAllFinally));
         setTerminal(catchBody, true);
@@ -584,7 +607,7 @@
     }
 
     @Override
-    public Node leave(final TryNode tryNode) {
+    public Node leaveTryNode(final TryNode tryNode) {
         final Block finallyBody   = tryNode.getFinallyBody();
 
         boolean allTerminal = tryNode.getBody().isTerminal() && (finallyBody == null || finallyBody.isTerminal());
@@ -608,18 +631,18 @@
     }
 
     @Override
-    public Node leave(final VarNode varNode) {
+    public Node leaveVarNode(final VarNode varNode) {
         addStatement(varNode);
         return varNode;
     }
 
     @Override
-    public Node enter(final WhileNode whileNode) {
+    public Node enterWhileNode(final WhileNode whileNode) {
         return nest(whileNode);
     }
 
     @Override
-    public Node leave(final WhileNode whileNode) {
+    public Node leaveWhileNode(final WhileNode whileNode) {
         final Node test = whileNode.getTest();
 
         if (test == null) {
@@ -653,7 +676,7 @@
     }
 
     @Override
-    public Node leave(final WithNode withNode) {
+    public Node leaveWithNode(final WithNode withNode) {
         if (withNode.getBody().isTerminal()) {
             setTerminal(withNode,  true);
         }
@@ -682,28 +705,10 @@
      */
     private static Node markerFunction(final Node function) {
         if (function instanceof IdentNode) {
-            return new IdentNode((IdentNode)function) {
-                @Override
-                public boolean isFunction() {
-                    return true;
-                }
-            };
-        } else if (function instanceof AccessNode) {
-            return new AccessNode((AccessNode)function) {
-                @Override
-                public boolean isFunction() {
-                    return true;
-                }
-            };
-        } else if (function instanceof IndexNode) {
-            return new IndexNode((IndexNode)function) {
-                @Override
-                public boolean isFunction() {
-                    return true;
-                }
-            };
+            return ((IdentNode)function).setIsFunction();
+        } else if (function instanceof BaseNode) {
+            return ((BaseNode)function).setIsFunction();
         }
-
         return function;
     }
 
@@ -746,7 +751,7 @@
             if (args.size() >= 1 && EVAL.tag().equals(callee.getName())) {
                 final CallNode.EvalArgs evalArgs =
                     new CallNode.EvalArgs(
-                        args.get(0).clone().accept(this), //clone as we use this for the "is eval case". original evaluated separately for "is not eval case"
+                        args.get(0).copy().accept(this), //clone as we use this for the "is eval case". original evaluated separately for "is not eval case"
                         getCurrentFunctionNode().getThisNode(),
                         evalLocation(callee),
                         getCurrentFunctionNode().isStrictMode());
@@ -773,13 +778,13 @@
 
         loopBody.accept(new NodeVisitor() {
             @Override
-            public Node leave(final BreakNode node) {
+            public Node leaveBreakNode(final BreakNode node) {
                 escapes.add(node);
                 return node;
             }
 
             @Override
-            public Node leave(final ContinueNode node) {
+            public Node leaveContinueNode(final ContinueNode node) {
                 // all inner loops have been popped.
                 if (nesting.contains(node.getTargetNode())) {
                     escapes.add(node);
@@ -794,7 +799,7 @@
     private void guaranteeReturn(final FunctionNode functionNode) {
         Node resultNode;
 
-        if (functionNode.isScript()) {
+        if (functionNode.isProgram()) {
             resultNode = functionNode.getResultNode(); // the eval result, symbol assigned in Attr
         } else {
             if (lastStatement != null && lastStatement.isTerminal() || lastStatement instanceof ReturnNode) {
@@ -859,18 +864,15 @@
      * @return true if try block is inside the target, false otherwise.
      */
     private boolean isNestedTry(final TryNode tryNode, final Block target) {
-        for (Block current = getCurrentBlock(); current != target; current = current.getParent()) {
-            if (tryNode.getBody() == current) {
+        for(Iterator<Block> blocks = lexicalContext.getBlocks(getCurrentBlock()); blocks.hasNext();) {
+            final Block block = blocks.next();
+            if(block == target) {
+                return false;
+            }
+            if(tryNode.isChildBlock(block)) {
                 return true;
             }
-
-            for (final Block catchBlock : tryNode.getCatchBlocks()) {
-                if (catchBlock == current) {
-                    return true;
-                }
-            }
         }
-
         return false;
     }
 
@@ -899,7 +901,7 @@
                 continue;
             }
 
-            finallyBody = (Block)finallyBody.clone();
+            finallyBody = (Block)finallyBody.copy();
             final boolean hasTerminalFlags = finallyBody.hasTerminalFlags();
 
             new ExecuteNode(finallyBody.getSource(), finallyBody.getToken(), finallyBody.getFinish(), finallyBody).accept(this);
@@ -974,6 +976,3 @@
     }
 
 }
-
-
-
--- a/nashorn/src/jdk/nashorn/internal/codegen/MethodEmitter.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/codegen/MethodEmitter.java	Sat Mar 23 00:58:39 2013 +0100
@@ -651,11 +651,10 @@
 
     /**
      * Load the constants array
-     * @param unitClassName name of the compile unit from which to load constants
      * @return this method emitter
      */
-    MethodEmitter loadConstants(final String unitClassName) {
-        getStatic(unitClassName, CONSTANTS.tag(), CONSTANTS.descriptor());
+    MethodEmitter loadConstants() {
+        getStatic(classEmitter.getUnitClassName(), CONSTANTS.tag(), CONSTANTS.descriptor());
         assert peekType().isArray() : peekType();
         return this;
     }
--- a/nashorn/src/jdk/nashorn/internal/codegen/Splitter.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Splitter.java	Sat Mar 23 00:58:39 2013 +0100
@@ -41,6 +41,7 @@
 import jdk.nashorn.internal.ir.FunctionNode;
 import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
 import jdk.nashorn.internal.ir.LabelNode;
+import jdk.nashorn.internal.ir.LexicalContext;
 import jdk.nashorn.internal.ir.LiteralNode;
 import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
 import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit;
@@ -49,6 +50,7 @@
 import jdk.nashorn.internal.ir.SplitNode;
 import jdk.nashorn.internal.ir.SwitchNode;
 import jdk.nashorn.internal.ir.WhileNode;
+import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
 import jdk.nashorn.internal.runtime.DebugLogger;
 import jdk.nashorn.internal.runtime.Source;
@@ -70,6 +72,8 @@
     /** Cache for calculated block weights. */
     private final Map<Node, Long> weightCache = new HashMap<>();
 
+    private final LexicalContext lexicalContext = new LexicalContext();
+
     /** Weight threshold for when to start a split. */
     public static final long SPLIT_THRESHOLD = Options.getIntProperty("nashorn.compiler.splitter.threshold", 32 * 1024);
 
@@ -112,7 +116,7 @@
             }
 
             if (weight >= SPLIT_THRESHOLD) {
-                weight = splitBlock(functionNode);
+                weight = splitBlock(functionNode, functionNode);
             }
 
             if (functionNode.isSplit()) {
@@ -132,9 +136,20 @@
         }
 
         // Recursively split nested functions
-        for (final FunctionNode function : functionNode.getFunctions()) {
-            new Splitter(compiler, function, outermostCompileUnit).split();
-        }
+        functionNode.accept(new NodeOperatorVisitor() {
+            @Override
+            public Node enterFunctionNode(FunctionNode function) {
+                if(function == functionNode) {
+                    // Don't process outermost function (it was already processed) but descend into it to find nested
+                    // functions.
+                    return function;
+                }
+                // Process a nested function
+                new Splitter(compiler, function, outermostCompileUnit).split();
+                // Don't descend into a a nested function; Splitter.split() has taken care of nested-in-nested functions.
+                return null;
+            }
+        });
 
         functionNode.setState(CompilationState.SPLIT);
     }
@@ -155,7 +170,7 @@
      *
      * @return new weight for the resulting block.
      */
-    private long splitBlock(final Block block) {
+    private long splitBlock(final Block block, final FunctionNode function) {
         functionNode.setIsSplit();
 
         final List<Node> splits = new ArrayList<>();
@@ -167,7 +182,7 @@
 
             if (statementsWeight + weight >= SPLIT_THRESHOLD || statement.isTerminal()) {
                 if (!statements.isEmpty()) {
-                    splits.add(createBlockSplitNode(block, statements, statementsWeight));
+                    splits.add(createBlockSplitNode(block, function, statements, statementsWeight));
                     statements = new ArrayList<>();
                     statementsWeight = 0;
                 }
@@ -183,7 +198,7 @@
         }
 
         if (!statements.isEmpty()) {
-            splits.add(createBlockSplitNode(block, statements, statementsWeight));
+            splits.add(createBlockSplitNode(block, function, statements, statementsWeight));
         }
 
         block.setStatements(splits);
@@ -199,13 +214,13 @@
      *
      * @return New split node.
      */
-    private SplitNode createBlockSplitNode(final Block parent, final List<Node> statements, final long weight) {
+    private SplitNode createBlockSplitNode(final Block parent, final FunctionNode function, final List<Node> statements, final long weight) {
         final Source source = parent.getSource();
         final long   token  = parent.getToken();
         final int    finish = parent.getFinish();
-        final String name   = parent.getFunction().uniqueName(SPLIT_PREFIX.tag());
+        final String name   = function.uniqueName(SPLIT_PREFIX.tag());
 
-        final Block newBlock = new Block(source, token, finish, parent, functionNode);
+        final Block newBlock = new Block(source, token, finish);
         newBlock.setFrame(new Frame(parent.getFrame()));
         newBlock.setStatements(statements);
 
@@ -217,15 +232,17 @@
     }
 
     @Override
-    public Node enter(final Block block) {
+    public Node enterBlock(final Block block) {
         if (block.isCatchBlock()) {
             return null;
         }
+        lexicalContext.push(block);
 
         final long weight = WeighNodes.weigh(block, weightCache);
 
         if (weight < SPLIT_THRESHOLD) {
             weightCache.put(block, weight);
+            lexicalContext.pop(block);
             return null;
         }
 
@@ -233,23 +250,24 @@
     }
 
     @Override
-    public Node leave(final Block block) {
+    public Node leaveBlock(final Block block) {
         assert !block.isCatchBlock();
 
         // Block was heavier than SLIT_THRESHOLD in enter, but a sub-block may have
         // been split already, so weigh again before splitting.
         long weight = WeighNodes.weigh(block, weightCache);
         if (weight >= SPLIT_THRESHOLD) {
-            weight = splitBlock(block);
+            weight = splitBlock(block, lexicalContext.getFunction(block));
         }
         weightCache.put(block, weight);
 
+        lexicalContext.pop(block);
         return block;
     }
 
     @SuppressWarnings("rawtypes")
     @Override
-    public Node leave(final LiteralNode literal) {
+    public Node leaveLiteralNode(final LiteralNode literal) {
         long weight = WeighNodes.weigh(literal);
 
         if (weight < SPLIT_THRESHOLD) {
@@ -294,17 +312,12 @@
     }
 
     @Override
-    public Node enter(final FunctionNode node) {
-        if (node.isLazy()) {
-            return null;
+    public Node enterFunctionNode(final FunctionNode node) {
+        if(node == functionNode && !node.isLazy()) {
+            lexicalContext.push(node);
+            node.visitStatements(this);
+            lexicalContext.pop(node);
         }
-
-        final List<Node> statements = node.getStatements();
-
-        for (final Node statement : statements) {
-            statement.accept(this);
-        }
-
         return null;
     }
 
@@ -321,38 +334,38 @@
         }
 
         @Override
-        public Node enter(final LabelNode labelNode) {
+        public Node enterLabelNode(final LabelNode labelNode) {
             registerJumpTarget(labelNode.getBreakNode());
             registerJumpTarget(labelNode.getContinueNode());
             return labelNode;
         }
 
         @Override
-        public Node enter(final WhileNode whileNode) {
+        public Node enterWhileNode(final WhileNode whileNode) {
             registerJumpTarget(whileNode);
             return whileNode;
         }
 
         @Override
-        public Node enter(final DoWhileNode doWhileNode) {
+        public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
             registerJumpTarget(doWhileNode);
             return doWhileNode;
         }
 
         @Override
-        public Node enter(final ForNode forNode) {
+        public Node enterForNode(final ForNode forNode) {
             registerJumpTarget(forNode);
             return forNode;
         }
 
         @Override
-        public Node enter(final SwitchNode switchNode) {
+        public Node enterSwitchNode(final SwitchNode switchNode) {
             registerJumpTarget(switchNode);
             return switchNode;
         }
 
         @Override
-        public Node enter(final ReturnNode returnNode) {
+        public Node enterReturnNode(final ReturnNode returnNode) {
             for (final SplitNode split : splitStack) {
                 split.setHasReturn(true);
             }
@@ -360,25 +373,25 @@
         }
 
         @Override
-        public Node enter(final ContinueNode continueNode) {
+        public Node enterContinueNode(final ContinueNode continueNode) {
             searchJumpTarget(continueNode.getTargetNode(), continueNode.getTargetLabel());
             return continueNode;
         }
 
         @Override
-        public Node enter(final BreakNode breakNode) {
+        public Node enterBreakNode(final BreakNode breakNode) {
             searchJumpTarget(breakNode.getTargetNode(), breakNode.getTargetLabel());
             return breakNode;
         }
 
         @Override
-        public Node enter(final SplitNode splitNode) {
+        public Node enterSplitNode(final SplitNode splitNode) {
             splitStack.addFirst(splitNode);
             return splitNode;
         }
 
         @Override
-        public Node leave(final SplitNode splitNode) {
+        public Node leaveSplitNode(final SplitNode splitNode) {
             assert splitNode == splitStack.peekFirst();
             splitStack.removeFirst();
             return splitNode;
--- a/nashorn/src/jdk/nashorn/internal/codegen/WeighNodes.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/codegen/WeighNodes.java	Sat Mar 23 00:58:39 2013 +0100
@@ -47,7 +47,6 @@
 import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit;
 import jdk.nashorn.internal.ir.Node;
 import jdk.nashorn.internal.ir.PropertyNode;
-import jdk.nashorn.internal.ir.ReferenceNode;
 import jdk.nashorn.internal.ir.ReturnNode;
 import jdk.nashorn.internal.ir.RuntimeNode;
 import jdk.nashorn.internal.ir.SplitNode;
@@ -80,7 +79,7 @@
     private static final long LITERAL_WEIGHT   = 10;
     private static final long LOOP_WEIGHT      = 4;
     private static final long NEW_WEIGHT       = 6;
-    private static final long REFERENCE_WEIGHT = 20;
+    private static final long FUNC_EXPR_WEIGHT = 20;
     private static final long RETURN_WEIGHT    = 2;
     private static final long SPLIT_WEIGHT     = 40;
     private static final long SWITCH_WEIGHT    = 8;
@@ -94,36 +93,37 @@
     /** Optional cache for weight of block nodes. */
     private final Map<Node, Long> weightCache;
 
-    /*
+    private final FunctionNode topFunction;
+
+    /**
      * Constructor
      *
      * @param weightCache cache of already calculated block weights
      */
-    private WeighNodes(final Map<Node, Long> weightCache) {
+    private WeighNodes(FunctionNode topFunction, final Map<Node, Long> weightCache) {
         super(null, null);
+        this.topFunction = topFunction;
         this.weightCache = weightCache;
     }
 
     static long weigh(final Node node) {
-        final WeighNodes weighNodes = new WeighNodes(null);
-        node.accept(weighNodes);
-        return weighNodes.weight;
+        return weigh(node, null);
     }
 
     static long weigh(final Node node, final Map<Node, Long> weightCache) {
-        final WeighNodes weighNodes = new WeighNodes(weightCache);
+        final WeighNodes weighNodes = new WeighNodes(node instanceof FunctionNode ? (FunctionNode)node : null, weightCache);
         node.accept(weighNodes);
         return weighNodes.weight;
     }
 
     @Override
-    public Node leave(final AccessNode accessNode) {
+    public Node leaveAccessNode(final AccessNode accessNode) {
         weight += ACCESS_WEIGHT;
         return accessNode;
     }
 
     @Override
-    public Node enter(final Block block) {
+    public Node enterBlock(final Block block) {
         if (weightCache != null && weightCache.containsKey(block)) {
             weight += weightCache.get(block);
             return null;
@@ -133,78 +133,79 @@
     }
 
     @Override
-    public Node leave(final BreakNode breakNode) {
+    public Node leaveBreakNode(final BreakNode breakNode) {
         weight += BREAK_WEIGHT;
         return breakNode;
     }
 
     @Override
-    public Node leave(final CallNode callNode) {
+    public Node leaveCallNode(final CallNode callNode) {
         weight += CALL_WEIGHT;
         return callNode;
     }
 
     @Override
-    public Node leave(final CatchNode catchNode) {
+    public Node leaveCatchNode(final CatchNode catchNode) {
         weight += CATCH_WEIGHT;
         return catchNode;
     }
 
     @Override
-    public Node leave(final ContinueNode continueNode) {
+    public Node leaveContinueNode(final ContinueNode continueNode) {
         weight += CONTINUE_WEIGHT;
         return continueNode;
     }
 
     @Override
-    public Node leave(final DoWhileNode doWhileNode) {
+    public Node leaveDoWhileNode(final DoWhileNode doWhileNode) {
         weight += LOOP_WEIGHT;
         return doWhileNode;
     }
 
     @Override
-    public Node leave(final ExecuteNode executeNode) {
+    public Node leaveExecuteNode(final ExecuteNode executeNode) {
         return executeNode;
     }
 
     @Override
-    public Node leave(final ForNode forNode) {
+    public Node leaveForNode(final ForNode forNode) {
         weight += LOOP_WEIGHT;
         return forNode;
     }
 
     @Override
-    public Node enter(final FunctionNode functionNode) {
-        final List<Node> statements = functionNode.getStatements();
-
-        for (final Node statement : statements) {
-            statement.accept(this);
+    public Node enterFunctionNode(final FunctionNode functionNode) {
+        if(functionNode == topFunction) {
+            // the function being weighted; descend into its statements
+            functionNode.visitStatements(this);
+        } else {
+            // just a reference to inner function from outer function
+            weight += FUNC_EXPR_WEIGHT;
         }
-
         return null;
     }
 
     @Override
-    public Node leave(final IdentNode identNode) {
+    public Node leaveIdentNode(final IdentNode identNode) {
         weight += ACCESS_WEIGHT + identNode.getName().length() * 2;
         return identNode;
     }
 
     @Override
-    public Node leave(final IfNode ifNode) {
+    public Node leaveIfNode(final IfNode ifNode) {
         weight += IF_WEIGHT;
         return ifNode;
     }
 
     @Override
-    public Node leave(final IndexNode indexNode) {
+    public Node leaveIndexNode(final IndexNode indexNode) {
         weight += ACCESS_WEIGHT;
         return indexNode;
     }
 
     @SuppressWarnings("rawtypes")
     @Override
-    public Node enter(final LiteralNode literalNode) {
+    public Node enterLiteralNode(final LiteralNode literalNode) {
         weight += LITERAL_WEIGHT;
 
         if (literalNode instanceof ArrayLiteralNode) {
@@ -230,67 +231,61 @@
     }
 
     @Override
-    public Node leave(final PropertyNode propertyNode) {
+    public Node leavePropertyNode(final PropertyNode propertyNode) {
         weight += LITERAL_WEIGHT;
         return propertyNode;
     }
 
     @Override
-    public Node leave(final ReferenceNode referenceNode) {
-        weight += REFERENCE_WEIGHT;
-        return referenceNode;
-    }
-
-    @Override
-    public Node leave(final ReturnNode returnNode) {
+    public Node leaveReturnNode(final ReturnNode returnNode) {
         weight += RETURN_WEIGHT;
         return returnNode;
     }
 
     @Override
-    public Node leave(final RuntimeNode runtimeNode) {
+    public Node leaveRuntimeNode(final RuntimeNode runtimeNode) {
         weight += CALL_WEIGHT;
         return runtimeNode;
     }
 
     @Override
-    public Node enter(final SplitNode splitNode) {
+    public Node enterSplitNode(final SplitNode splitNode) {
         weight += SPLIT_WEIGHT;
         return null;
     }
 
     @Override
-    public Node leave(final SwitchNode switchNode) {
+    public Node leaveSwitchNode(final SwitchNode switchNode) {
         weight += SWITCH_WEIGHT;
         return switchNode;
     }
 
     @Override
-    public Node leave(final ThrowNode throwNode) {
+    public Node leaveThrowNode(final ThrowNode throwNode) {
         weight += THROW_WEIGHT;
         return throwNode;
     }
 
     @Override
-    public Node leave(final TryNode tryNode) {
+    public Node leaveTryNode(final TryNode tryNode) {
         weight += THROW_WEIGHT;
         return tryNode;
     }
 
     @Override
-    public Node leave(final VarNode varNode) {
+    public Node leaveVarNode(final VarNode varNode) {
         weight += VAR_WEIGHT;
         return varNode;
     }
 
     @Override
-    public Node leave(final WhileNode whileNode) {
+    public Node leaveWhileNode(final WhileNode whileNode) {
         weight += LOOP_WEIGHT;
         return whileNode;
     }
 
     @Override
-    public Node leave(final WithNode withNode) {
+    public Node leaveWithNode(final WithNode withNode) {
         weight += WITH_WEIGHT;
         return withNode;
     }
--- a/nashorn/src/jdk/nashorn/internal/ir/AccessNode.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/AccessNode.java	Sat Mar 23 00:58:39 2013 +0100
@@ -36,7 +36,7 @@
  * IR representation of a property access (period operator.)
  *
  */
-public class AccessNode extends BaseNode implements TypeOverride {
+public class AccessNode extends BaseNode implements TypeOverride<AccessNode> {
     /** Property ident. */
     private IdentNode property;
 
@@ -56,9 +56,7 @@
         super(source, token, finish, base);
 
         this.start    = base.getStart();
-        this.property = property;
-
-        this.property.setIsPropertyName();
+        this.property = property.setIsPropertyName();
     }
 
     /**
@@ -106,10 +104,10 @@
      */
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
+        if (visitor.enterAccessNode(this) != null) {
             base = base.accept(visitor);
             property = (IdentNode)property.accept(visitor);
-            return visitor.leave(this);
+            return visitor.leaveAccessNode(this);
         }
 
         return this;
@@ -150,13 +148,14 @@
     }
 
     @Override
-    public void setType(final Type type) {
+    public AccessNode setType(final Type type) {
         if (DEBUG_FIELDS && !Type.areEquivalent(getSymbol().getSymbolType(), type)) {
             ObjectClassGenerator.LOG.info(getClass().getName() + " " + this + " => " + type + " instead of " + getType());
         }
-        property.setType(type);
+        property = property.setType(type);
         getSymbol().setTypeOverride(type); //always a temp so this is fine.
         hasCallSiteType = true;
+        return this;
     }
 
     @Override
--- a/nashorn/src/jdk/nashorn/internal/ir/Assignment.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/Assignment.java	Sat Mar 23 00:58:39 2013 +0100
@@ -46,4 +46,11 @@
      * @return get the assignment source node
      */
     public Node getAssignmentSource();
+
+    /**
+     * Set assignment destination node.
+     * @param n the assignment destination node.
+     * @return a node equivalent to this one except for the requested change.
+     */
+    public Node setAssignmentDest(D n);
 }
--- a/nashorn/src/jdk/nashorn/internal/ir/BaseNode.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/BaseNode.java	Sat Mar 23 00:58:39 2013 +0100
@@ -38,6 +38,8 @@
     /** Base Node. */
     protected Node base;
 
+    private boolean function;
+
     /**
      * Constructor
      *
@@ -96,6 +98,15 @@
 
     @Override
     public boolean isFunction() {
-        return false;
+        return function;
+    }
+
+    /**
+     * Mark this node as being the callee operand of a {@link CallNode}.
+     * @return a base node identical to this one in all aspects except with its function flag set.
+     */
+    public BaseNode setIsFunction() {
+        function = true;
+        return this;
     }
 }
--- a/nashorn/src/jdk/nashorn/internal/ir/BinaryNode.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/BinaryNode.java	Sat Mar 23 00:58:39 2013 +0100
@@ -35,7 +35,7 @@
  */
 public class BinaryNode extends UnaryNode {
     /** Left hand side argument. */
-    protected Node lhs;
+    private Node lhs;
 
     /**
      * Constructor
@@ -140,6 +140,11 @@
     }
 
     @Override
+    public Node setAssignmentDest(Node n) {
+        return setLHS(n);
+    }
+
+    @Override
     public Node getAssignmentSource() {
         return rhs();
     }
@@ -163,10 +168,9 @@
      */
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
-            lhs = lhs.accept(visitor);
-            rhs = rhs.accept(visitor);
-            return visitor.leave(this);
+        if (visitor.enterBinaryNode(this) != null) {
+            // TODO: good cause for a separate visitMembers: we could delegate to UnaryNode.visitMembers
+            return visitor.leaveBinaryNode((BinaryNode)setLHS(lhs.accept(visitor)).setRHS(rhs().accept(visitor)));
         }
 
         return this;
@@ -229,8 +233,12 @@
     /**
      * Set the left hand side expression for this node
      * @param lhs new left hand side expression
+     * @return a node equivalent to this one except for the requested change.
      */
-    public void setLHS(final Node lhs) {
-        this.lhs = lhs;
+    public BinaryNode setLHS(final Node lhs) {
+        if(this.lhs == lhs) return this;
+        final BinaryNode n = (BinaryNode)clone();
+        n.lhs = lhs;
+        return n;
     }
 }
--- a/nashorn/src/jdk/nashorn/internal/ir/Block.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/Block.java	Sat Mar 23 00:58:39 2013 +0100
@@ -25,14 +25,6 @@
 
 package jdk.nashorn.internal.ir;
 
-import static jdk.nashorn.internal.ir.Symbol.IS_GLOBAL;
-import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL;
-import static jdk.nashorn.internal.ir.Symbol.IS_LET;
-import static jdk.nashorn.internal.ir.Symbol.IS_PARAM;
-import static jdk.nashorn.internal.ir.Symbol.IS_SCOPE;
-import static jdk.nashorn.internal.ir.Symbol.IS_VAR;
-import static jdk.nashorn.internal.ir.Symbol.KINDMASK;
-
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -40,9 +32,9 @@
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
+import java.util.ListIterator;
 import jdk.nashorn.internal.codegen.Frame;
 import jdk.nashorn.internal.codegen.Label;
-import jdk.nashorn.internal.ir.annotations.Reference;
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
 import jdk.nashorn.internal.runtime.Source;
 
@@ -51,14 +43,6 @@
  * basis for script body.
  */
 public class Block extends Node {
-    /** Parent context */
-    @Reference
-    private Block parent;
-
-    /** Owning function - a FunctionNode has itself as function */
-    @Reference
-    protected FunctionNode function;
-
     /** List of statements */
     protected List<Node> statements;
 
@@ -83,14 +67,10 @@
      * @param source   source code
      * @param token    token
      * @param finish   finish
-     * @param parent   reference to parent block
-     * @param function function node this block is in
      */
-    public Block(final Source source, final long token, final int finish, final Block parent, final FunctionNode function) {
+    public Block(final Source source, final long token, final int finish) {
         super(source, token, finish);
 
-        this.parent     = parent;
-        this.function   = function;
         this.statements = new ArrayList<>();
         this.symbols    = new HashMap<>();
         this.entryLabel = new Label("block_entry");
@@ -106,8 +86,6 @@
     protected Block(final Block block, final CopyState cs) {
         super(block);
 
-        this.parent     = block.parent;
-        this.function   = block.function;
         this.statements = new ArrayList<>();
         for (final Node statement : block.getStatements()) {
             statements.add(cs.existingOrCopy(statement));
@@ -122,55 +100,7 @@
 
     @Override
     protected Node copy(final CopyState cs) {
-        return fixBlockChain(new Block(this, cs));
-    }
-
-    /**
-     * Whenever a clone that contains a hierarchy of blocks is created,
-     * this function has to be called to ensure that the parents point
-     * to the correct parent blocks or two different ASTs would not
-     * be completely separated.
-     *
-     * @return the argument
-     */
-    static Block fixBlockChain(final Block root) {
-        root.accept(new NodeVisitor() {
-            private Block        parent   = root.getParent();
-            private final FunctionNode function = root.getFunction();
-
-            @Override
-            public Node enter(final Block block) {
-                assert block.getFunction() == function;
-                block.setParent(parent);
-                parent = block;
-
-                return block;
-            }
-
-            @Override
-            public Node leave(final Block block) {
-                parent = block.getParent();
-
-                return block;
-            }
-
-            @Override
-            public Node enter(final FunctionNode functionNode) {
-                assert functionNode.getFunction() == function;
-
-                return enter((Block)functionNode);
-            }
-
-            @Override
-            public Node leave(final FunctionNode functionNode) {
-                assert functionNode.getFunction() == function;
-
-                return leave((Block)functionNode);
-            }
-
-        });
-
-        return root;
+        return new Block(this, cs);
     }
 
     /**
@@ -188,17 +118,12 @@
     }
 
     /**
-     * Prepend a statement to the statement list
+     * Prepend statements to the statement list
      *
-     * @param statement Statement node to add
+     * @param prepended statement to add
      */
-    public void prependStatement(final Node statement) {
-        if (statement != null) {
-            final List<Node> newStatements = new ArrayList<>();
-            newStatements.add(statement);
-            newStatements.addAll(statements);
-            setStatements(newStatements);
-        }
+    public void prependStatements(final List<Node> prepended) {
+        statements.addAll(0, prepended);
     }
 
     /**
@@ -211,39 +136,6 @@
     }
 
     /**
-     * Add a new function to the function list.
-     *
-     * @param functionNode Function node to add.
-     */
-    public void addFunction(final FunctionNode functionNode) {
-        assert parent != null : "Parent context missing.";
-
-        parent.addFunction(functionNode);
-    }
-
-    /**
-     * Add a list of functions to the function list.
-     *
-     * @param functionNodes Function nodes to add.
-     */
-    public void addFunctions(final List<FunctionNode> functionNodes) {
-        assert parent != null : "Parent context missing.";
-
-        parent.addFunctions(functionNodes);
-    }
-
-    /**
-     * Set the function list to a new one
-     *
-     * @param functionNodes the nodes to set
-     */
-    public void setFunctions(final List<FunctionNode> functionNodes) {
-        assert parent != null : "Parent context missing.";
-
-        parent.setFunctions(functionNodes);
-    }
-
-    /**
      * Assist in IR navigation.
      *
      * @param visitor IR navigating visitor.
@@ -257,13 +149,9 @@
         try {
             // Ignore parent to avoid recursion.
 
-            if (visitor.enter(this) != null) {
-                for (int i = 0, count = statements.size(); i < count; i++) {
-                    final Node statement = statements.get(i);
-                    statements.set(i, statement.accept(visitor));
-                }
-
-                return visitor.leave(this);
+            if (visitor.enterBlock(this) != null) {
+                visitStatements(visitor);
+                return visitor.leaveBlock(this);
             }
         } finally {
             visitor.setCurrentBlock(saveBlock);
@@ -281,51 +169,13 @@
     }
 
     /**
-     * Search for symbol.
-     *
-     * @param name Symbol name.
-     *
-     * @return Found symbol or null if not found.
+     * Retrieves an existing symbol defined in the current block.
+     * @param name the name of the symbol
+     * @return an existing symbol with the specified name defined in the current block, or null if this block doesn't
+     * define a symbol with this name.
      */
-    public Symbol findSymbol(final String name) {
-        // Search up block chain to locate symbol.
-
-        for (Block block = this; block != null; block = block.getParent()) {
-            // Find name.
-            final Symbol symbol = block.symbols.get(name);
-            // If found then we are good.
-            if (symbol != null) {
-                return symbol;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Search for symbol in current function.
-     *
-     * @param name Symbol name.
-     *
-     * @return Found symbol or null if not found.
-     */
-    public Symbol findLocalSymbol(final String name) {
-        // Search up block chain to locate symbol.
-        for (Block block = this; block != null; block = block.getParent()) {
-            // Find name.
-            final Symbol symbol = block.symbols.get(name);
-            // If found then we are good.
-            if (symbol != null) {
-                return symbol;
-            }
-
-            // If searched function then we are done.
-            if (block == block.function) {
-                break;
-            }
-        }
-
-        // Not found.
-        return null;
+    public Symbol getExistingSymbol(final String name) {
+        return symbols.get(name);
     }
 
     /**
@@ -338,122 +188,6 @@
         return statements.size() == 1 && statements.get(0) instanceof CatchNode;
     }
 
-    /**
-     * Test to see if a symbol is local to the function.
-     *
-     * @param symbol Symbol to test.
-     * @return True if a local symbol.
-     */
-    public boolean isLocal(final Symbol symbol) {
-        // some temp symbols have no block, so can be assumed local
-        final Block block = symbol.getBlock();
-        return block == null || block.getFunction() == function;
-    }
-
-    /**
-     * Declare the definition of a new symbol.
-     *
-     * @param name         Name of symbol.
-     * @param symbolFlags  Symbol flags.
-     * @param node         Defining Node.
-     *
-     * @return Symbol for given name or null for redefinition.
-     */
-    public Symbol defineSymbol(final String name, final int symbolFlags, final Node node) {
-        int    flags  = symbolFlags;
-        Symbol symbol = findSymbol(name); // Locate symbol.
-
-        if ((flags & KINDMASK) == IS_GLOBAL) {
-            flags |= IS_SCOPE;
-        }
-
-        if (symbol != null) {
-            // Symbol was already defined. Check if it needs to be redefined.
-            if ((flags & KINDMASK) == IS_PARAM) {
-                if (!function.isLocal(symbol)) {
-                    // Not defined in this function. Create a new definition.
-                    symbol = null;
-                } else if (symbol.isParam()) {
-                    // Duplicate parameter. Null return will force an error.
-                    assert false : "duplicate parameter";
-                    return null;
-                }
-            } else if ((flags & KINDMASK) == IS_VAR) {
-                if ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & Symbol.IS_LET) == Symbol.IS_LET) {
-                    assert !((flags & IS_LET) == IS_LET && symbol.getBlock() == this) : "duplicate let variable in block";
-                    // Always create a new definition.
-                    symbol = null;
-                } else {
-                    // Not defined in this function. Create a new definition.
-                    if (!function.isLocal(symbol) || symbol.less(IS_VAR)) {
-                        symbol = null;
-                    }
-                }
-            }
-        }
-
-        if (symbol == null) {
-            // If not found, then create a new one.
-            Block symbolBlock;
-
-            // Determine where to create it.
-            if ((flags & Symbol.KINDMASK) == IS_VAR && ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET)) {
-                symbolBlock = this;
-            } else {
-                symbolBlock = getFunction();
-            }
-
-            // Create and add to appropriate block.
-            symbol = new Symbol(name, flags, node, symbolBlock);
-            symbolBlock.putSymbol(name, symbol);
-
-            if ((flags & Symbol.KINDMASK) != IS_GLOBAL) {
-                symbolBlock.getFrame().addSymbol(symbol);
-                symbol.setNeedsSlot(true);
-            }
-        } else if (symbol.less(flags)) {
-            symbol.setFlags(flags);
-        }
-
-        if (node != null) {
-            node.setSymbol(symbol);
-        }
-
-        return symbol;
-    }
-
-    /**
-     * Declare the use of a symbol.
-     *
-     * @param name Name of symbol.
-     * @param node Using node
-     *
-     * @return Symbol for given name.
-     */
-    public Symbol useSymbol(final String name, final Node node) {
-        Symbol symbol = findSymbol(name);
-
-        if (symbol == null) {
-            // If not found, declare as a free var.
-            symbol = defineSymbol(name, IS_GLOBAL, node);
-        } else {
-            node.setSymbol(symbol);
-        }
-
-        return symbol;
-    }
-
-    /**
-     * Add parent name to the builder.
-     *
-     * @param sb String bulder.
-     */
-    public void addParentName(final StringBuilder sb) {
-        if (parent != null) {
-            parent.addParentName(sb);
-        }
-    }
-
     @Override
     public void toString(final StringBuilder sb) {
         for (final Node statement : statements) {
@@ -512,16 +246,6 @@
     }
 
     /**
-     * Get the FunctionNode for this block, i.e. the function it
-     * belongs to
-     *
-     * @return the function node
-     */
-    public FunctionNode getFunction() {
-        return function;
-    }
-
-    /**
      * Reset the frame for this block
      *
      * @param frame  the new frame
@@ -531,24 +255,6 @@
     }
 
     /**
-     * Get the parent block
-     *
-     * @return parent block, or null if none exists
-     */
-    public Block getParent() {
-        return parent;
-    }
-
-    /**
-     * Set the parent block
-     *
-     * @param parent the new parent block
-     */
-    public void setParent(final Block parent) {
-        this.parent = parent;
-    }
-
-    /**
      * Get the list of statements in this block
      *
      * @return a list of statements
@@ -558,6 +264,15 @@
     }
 
     /**
+     * Applies the specified visitor to all statements in the block.
+     * @param visitor the visitor.
+     */
+    public void visitStatements(NodeVisitor visitor) {
+        for (ListIterator<Node> stmts = statements.listIterator(); stmts.hasNext();) {
+            stmts.set(stmts.next().accept(visitor));
+        }
+    }
+    /**
      * Reset the statement list for this block
      *
      * @param statements  new statement list
@@ -592,4 +307,29 @@
         needsScope = true;
     }
 
+    /**
+     * Marks this block as using a specified scoped symbol. The block and its parent blocks up to but not
+     * including the block defining the symbol will be marked as needing parent scope. The block defining the symbol
+     * will be marked as one that needs to have its own scope.
+     * @param symbol the symbol being used.
+     * @param ancestors the iterator over block's containing lexical context
+     */
+    public void setUsesScopeSymbol(final Symbol symbol, Iterator<Block> ancestors) {
+        if(symbol.getBlock() == this) {
+            setNeedsScope();
+        } else {
+            setUsesParentScopeSymbol(symbol, ancestors);
+        }
+    }
+
+    /**
+     * Invoked when this block uses a scope symbol defined in one of its ancestors.
+     * @param symbol the scope symbol being used
+     * @param ancestors iterator over ancestor blocks
+     */
+    void setUsesParentScopeSymbol(final Symbol symbol, Iterator<Block> ancestors) {
+        if(ancestors.hasNext()) {
+            ancestors.next().setUsesScopeSymbol(symbol, ancestors);
+        }
+    }
 }
--- a/nashorn/src/jdk/nashorn/internal/ir/BreakNode.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/BreakNode.java	Sat Mar 23 00:58:39 2013 +0100
@@ -64,8 +64,8 @@
      */
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
-            return visitor.leave(this);
+        if (visitor.enterBreakNode(this) != null) {
+            return visitor.leaveBreakNode(this);
         }
 
         return this;
--- a/nashorn/src/jdk/nashorn/internal/ir/CallNode.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/CallNode.java	Sat Mar 23 00:58:39 2013 +0100
@@ -37,7 +37,7 @@
  * IR representation for a function call.
  *
  */
-public class CallNode extends Node implements TypeOverride {
+public class CallNode extends Node implements TypeOverride<CallNode> {
 
     private Type type;
 
@@ -176,13 +176,13 @@
         if (hasCallSiteType()) {
             return type;
         }
-        assert !function.getType().isUnknown();
-        return function.getType();
+        return function instanceof FunctionNode ? ((FunctionNode)function).getReturnType() : Type.OBJECT;
     }
 
     @Override
-    public void setType(final Type type) {
+    public CallNode setType(final Type type) {
         this.type = type;
+        return this;
     }
 
     private boolean hasCallSiteType() {
@@ -208,14 +208,14 @@
      */
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
+        if (visitor.enterCallNode(this) != null) {
             function = function.accept(visitor);
 
             for (int i = 0, count = args.size(); i < count; i++) {
                 args.set(i, args.get(i).accept(visitor));
             }
 
-            return visitor.leave(this);
+            return visitor.leaveCallNode(this);
         }
 
         return this;
--- a/nashorn/src/jdk/nashorn/internal/ir/CaseNode.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/CaseNode.java	Sat Mar 23 00:58:39 2013 +0100
@@ -79,7 +79,7 @@
      */
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
+        if (visitor.enterCaseNode(this) != null) {
             if (test != null) {
                 test = test.accept(visitor);
             }
@@ -87,7 +87,7 @@
                 body = (Block)body.accept(visitor);
             }
 
-            return visitor.leave(this);
+            return visitor.leaveCaseNode(this);
         }
 
         return this;
--- a/nashorn/src/jdk/nashorn/internal/ir/CatchNode.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/CatchNode.java	Sat Mar 23 00:58:39 2013 +0100
@@ -84,7 +84,7 @@
      */
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
+        if (visitor.enterCatchNode(this) != null) {
             exception = (IdentNode)exception.accept(visitor);
 
             if (exceptionCondition != null) {
@@ -92,7 +92,7 @@
             }
 
             body = (Block)body.accept(visitor);
-            return visitor.leave(this);
+            return visitor.leaveCatchNode(this);
         }
 
         return this;
--- a/nashorn/src/jdk/nashorn/internal/ir/ContinueNode.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/ContinueNode.java	Sat Mar 23 00:58:39 2013 +0100
@@ -61,8 +61,8 @@
 
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
-            return visitor.leave(this);
+        if (visitor.enterContinueNode(this) != null) {
+            return visitor.leaveContinueNode(this);
         }
 
         return this;
--- a/nashorn/src/jdk/nashorn/internal/ir/DoWhileNode.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/DoWhileNode.java	Sat Mar 23 00:58:39 2013 +0100
@@ -63,11 +63,11 @@
 
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
+        if (visitor.enterDoWhileNode(this) != null) {
             body = (Block)body.accept(visitor);
             test = test.accept(visitor);
 
-            return visitor.leave(this);
+            return visitor.leaveDoWhileNode(this);
         }
 
         return this;
--- a/nashorn/src/jdk/nashorn/internal/ir/EmptyNode.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/EmptyNode.java	Sat Mar 23 00:58:39 2013 +0100
@@ -57,8 +57,8 @@
 
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
-            return visitor.leave(this);
+        if (visitor.enterEmptyNode(this) != null) {
+            return visitor.leaveEmptyNode(this);
         }
         return this;
     }
--- a/nashorn/src/jdk/nashorn/internal/ir/ExecuteNode.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/ExecuteNode.java	Sat Mar 23 00:58:39 2013 +0100
@@ -85,9 +85,9 @@
 
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
+        if (visitor.enterExecuteNode(this) != null) {
             setExpression(expression.accept(visitor));
-            return visitor.leave(this);
+            return visitor.leaveExecuteNode(this);
         }
 
         return this;
--- a/nashorn/src/jdk/nashorn/internal/ir/ForNode.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/ForNode.java	Sat Mar 23 00:58:39 2013 +0100
@@ -76,7 +76,7 @@
 
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
+        if (visitor.enterForNode(this) != null) {
             if (init != null) {
                 init = init.accept(visitor);
             }
@@ -91,7 +91,7 @@
 
             body = (Block)body.accept(visitor);
 
-            return visitor.leave(this);
+            return visitor.leaveForNode(this);
         }
 
         return this;
--- a/nashorn/src/jdk/nashorn/internal/ir/FunctionNode.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/FunctionNode.java	Sat Mar 23 00:58:39 2013 +0100
@@ -34,7 +34,7 @@
 import java.util.Collections;
 import java.util.EnumSet;
 import java.util.HashMap;
-import java.util.LinkedList;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Stack;
@@ -47,7 +47,7 @@
 import jdk.nashorn.internal.ir.annotations.Ignore;
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
 import jdk.nashorn.internal.parser.Parser;
-import jdk.nashorn.internal.runtime.Debug;
+import jdk.nashorn.internal.runtime.ScriptFunction;
 import jdk.nashorn.internal.runtime.Source;
 import jdk.nashorn.internal.runtime.UserAccessorProperty;
 import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
@@ -57,6 +57,8 @@
  */
 public class FunctionNode extends Block {
 
+    private static final Type FUNCTION_TYPE = Type.typeFor(ScriptFunction.class);
+
     /** Function kinds */
     public enum Kind {
         /** a normal function - nothing special */
@@ -112,9 +114,6 @@
     /** List of parameters. */
     private List<IdentNode> parameters;
 
-    /** List of nested functions. */
-    private List<FunctionNode> functions;
-
     /** First token of function. **/
     private long firstToken;
 
@@ -157,10 +156,6 @@
     /** Pending control list. */
     private final Stack<Node> controlStack;
 
-    /** Variable declarations in the function's scope */
-    @Ignore
-    private final List<VarNode> declarations;
-
     /** VarNode for this function statement */
     @Ignore //this is explicit code anyway and should not be traversed after lower
     private VarNode funcVarNode;
@@ -184,33 +179,35 @@
     private int flags;
 
     /** Is anonymous function flag. */
-    private static final int IS_ANONYMOUS                = 0b0000_0000_0000_0001;
-    /** Is statement flag */
-    private static final int IS_STATEMENT                = 0b0000_0000_0000_0010;
+    private static final int IS_ANONYMOUS                = 1 << 0;
+    /** Is the function created in a function declaration (as opposed to a function expression) */
+    private static final int IS_DECLARED                 = 1 << 1;
     /** is this a strict mode function? */
-    private static final int IS_STRICT_MODE              = 0b0000_0000_0000_0100;
+    private static final int IS_STRICT_MODE              = 1 << 2;
     /** Does the function use the "arguments" identifier ? */
-    private static final int USES_ARGUMENTS              = 0b0000_0000_0000_1000;
+    private static final int USES_ARGUMENTS              = 1 << 3;
     /** Are we lowered ? */
-    private static final int IS_LOWERED                  = 0b0000_0000_0001_0000;
+    private static final int IS_LOWERED                  = 1 << 4;
     /** Has this node been split because it was too large? */
-    private static final int IS_SPLIT                    = 0b0000_0000_0010_0000;
+    private static final int IS_SPLIT                    = 1 << 5;
     /** Does the function call eval? */
-    private static final int HAS_EVAL                    = 0b0000_0000_0100_0000;
+    private static final int HAS_EVAL                    = 1 << 6;
     /** Does the function contain a with block ? */
-    private static final int HAS_WITH                    = 0b0000_0000_1000_0000;
+    private static final int HAS_WITH                    = 1 << 7;
     /** Does a descendant function contain a with or eval? */
-    private static final int HAS_DESCENDANT_WITH_OR_EVAL = 0b0000_0001_0000_0000;
+    private static final int HAS_DESCENDANT_WITH_OR_EVAL = 1 << 8;
     /** Does the function define "arguments" identifier as a parameter of nested function name? */
-    private static final int DEFINES_ARGUMENTS           = 0b0000_0010_0000_0000;
+    private static final int DEFINES_ARGUMENTS           = 1 << 9;
     /** Does the function need a self symbol? */
-    private static final int NEEDS_SELF_SYMBOL           = 0b0000_0100_0000_0000;
+    private static final int NEEDS_SELF_SYMBOL           = 1 << 10;
     /** Does this function or any of its descendants use variables from an ancestor function's scope (incl. globals)? */
-    private static final int USES_ANCESTOR_SCOPE         = 0b0000_1000_0000_0000;
+    private static final int USES_ANCESTOR_SCOPE         = 1 << 11;
     /** Is this function lazily compiled? */
-    private static final int IS_LAZY                     = 0b0001_0000_0000_0000;
+    private static final int IS_LAZY                     = 1 << 12;
     /** Does this function have lazy, yet uncompiled children */
-    private static final int HAS_LAZY_CHILDREN           = 0b0010_0000_0000_0000;
+    private static final int HAS_LAZY_CHILDREN           = 1 << 13;
+    /** Does this function have lazy, yet uncompiled children */
+    private static final int IS_PROGRAM                   = 1 << 14;
 
     /** Does this function or any nested functions contain a with or an eval? */
     private static final int HAS_DEEP_WITH_OR_EVAL = HAS_EVAL | HAS_WITH | HAS_DESCENDANT_WITH_OR_EVAL;
@@ -226,56 +223,34 @@
     private Type returnType = Type.UNKNOWN;
 
     /**
-     * Used to keep track of a function's parent blocks.
-     * This is needed when a (finally body) block is cloned than contains inner functions.
-     * Does not include function.getParent().
-     */
-    @Ignore
-    private List<Block> referencingParentBlocks;
-
-    /**
      * Constructor
      *
      * @param source    the source
      * @param token     token
      * @param finish    finish
      * @param namespace the namespace
-     * @param parent    the parent block
      * @param ident     the identifier
      * @param name      the name of the function
      */
-    @SuppressWarnings("LeakingThisInConstructor")
-    public FunctionNode(final Source source, final long token, final int finish, final Namespace namespace, final Block parent, final IdentNode ident, final String name) {
-        super(source, token, finish, parent, null);
+    public FunctionNode(final Source source, final long token, final int finish, final Namespace namespace, final IdentNode ident, final String name) {
+        super(source, token, finish);
 
         this.ident             = ident;
         this.name              = name;
         this.kind              = Kind.NORMAL;
         this.parameters        = new ArrayList<>();
-        this.functions         = new ArrayList<>();
         this.firstToken        = token;
         this.lastToken         = token;
         this.namespace         = namespace;
         this.labelStack        = new Stack<>();
         this.controlStack      = new Stack<>();
-        this.declarations      = new ArrayList<>();
-        // my block -> function is this. We added @SuppressWarnings("LeakingThisInConstructor") as NetBeans identifies
-        // it as such a leak - this is a false positive as we're setting this into a field of the object being
-        // constructed, so it can't be seen from other threads.
-        this.function          = this;
         this.compilationState  = EnumSet.of(CompilationState.INITIALIZED);
         this.specializedTypes  = new HashMap<>();
     }
 
-    @SuppressWarnings("LeakingThisInConstructor")
     private FunctionNode(final FunctionNode functionNode, final CopyState cs) {
         super(functionNode, cs);
 
-        this.functions = new ArrayList<>();
-        for (final FunctionNode f : functionNode.getFunctions()) {
-            this.functions.add((FunctionNode)cs.existingOrCopy(f));
-        }
-
         this.ident = (IdentNode)cs.existingOrCopy(functionNode.ident);
         this.name  = functionNode.name;
         this.kind  = functionNode.kind;
@@ -296,22 +271,12 @@
         this.calleeNode        = (IdentNode)cs.existingOrCopy(functionNode.calleeNode);
         this.labelStack        = new Stack<>();
         this.controlStack      = new Stack<>();
-        this.declarations      = new ArrayList<>();
-
-        for (final VarNode decl : functionNode.getDeclarations()) {
-            declarations.add((VarNode) cs.existingOrCopy(decl)); //TODO same?
-        }
 
         this.flags = functionNode.flags;
 
         this.funcVarNode = (VarNode)cs.existingOrCopy(functionNode.funcVarNode);
         /** VarNode for this function statement */
 
-        // my block -> function is this. We added @SuppressWarnings("LeakingThisInConstructor") as NetBeans identifies
-        // it as such a leak - this is a false positive as we're setting this into a field of the object being
-        // constructed, so it can't be seen from other threads.
-        this.function = this;
-
         this.compilationState = EnumSet.copyOf(functionNode.compilationState);
         this.specializedTypes = new HashMap<>();
     }
@@ -319,21 +284,21 @@
     @Override
     protected Node copy(final CopyState cs) {
         // deep clone all parent blocks
-        return fixBlockChain(new FunctionNode(this, cs));
+        return new FunctionNode(this, cs);
     }
 
     @Override
     public Node accept(final NodeVisitor visitor) {
-        final FunctionNode saveFunctionNode = visitor.getCurrentFunctionNode();
-        final Block        saveBlock        = visitor.getCurrentBlock();
+        final FunctionNode  saveFunctionNode  = visitor.getCurrentFunctionNode();
+        final Block         saveBlock         = visitor.getCurrentBlock();
+        final MethodEmitter saveMethodEmitter = visitor.getCurrentMethodEmitter();
+        final CompileUnit   saveCompileUnit   = visitor.getCurrentCompileUnit();
 
         visitor.setCurrentFunctionNode(this);
-        visitor.setCurrentCompileUnit(getCompileUnit());
-        visitor.setCurrentMethodEmitter(getMethodEmitter());
         visitor.setCurrentBlock(this);
 
         try {
-            if (visitor.enter(this) != null) {
+            if (visitor.enterFunctionNode(this) != null) {
                 if (ident != null) {
                     ident = (IdentNode)ident.accept(visitor);
                 }
@@ -342,51 +307,25 @@
                     parameters.set(i, (IdentNode)parameters.get(i).accept(visitor));
                 }
 
-                for (int i = 0, count = functions.size(); i < count; i++) {
-                    functions.set(i, (FunctionNode)functions.get(i).accept(visitor));
-                }
-
                 for (int i = 0, count = statements.size(); i < count; i++) {
                     statements.set(i, statements.get(i).accept(visitor));
                 }
 
-                return visitor.leave(this);
+                return visitor.leaveFunctionNode(this);
             }
         } finally {
             visitor.setCurrentBlock(saveBlock);
             visitor.setCurrentFunctionNode(saveFunctionNode);
-            visitor.setCurrentCompileUnit(saveFunctionNode != null ? saveFunctionNode.getCompileUnit() : null);
-            visitor.setCurrentMethodEmitter(saveFunctionNode != null ? saveFunctionNode.getMethodEmitter() : null);
+            visitor.setCurrentCompileUnit(saveCompileUnit);
+            visitor.setCurrentMethodEmitter(saveMethodEmitter);
         }
 
         return this;
     }
 
-    /**
-     * Locate the parent function.
-     *
-     * @return Parent function.
-     */
-    public FunctionNode findParentFunction() {
-        return getParent() != null ? getParent().getFunction() : null;
-    }
-
-    /**
-     * Add parent name to the builder.
-     *
-     * @param sb String builder.
-     */
-    @Override
-    public void addParentName(final StringBuilder sb) {
-        if (!isScript()) {
-            sb.append(getName());
-            sb.append("$");
-        }
-    }
-
     @Override
     public boolean needsScope() {
-        return super.needsScope() || isScript();
+        return super.needsScope() || isProgram();
     }
 
     /**
@@ -544,12 +483,18 @@
     }
 
     /**
-     * Determine if script function.
-     *
-     * @return True if script function.
+     * Returns true if the function is the top-level program.
+     * @return True if this function node represents the top-level program.
      */
-    public boolean isScript() {
-        return getParent() == null;
+    public boolean isProgram() {
+        return (flags & IS_PROGRAM) != 0;
+    }
+
+    /**
+     * Marks the function as representing the top-level program.
+     */
+    public void setProgram() {
+        flags |= IS_PROGRAM;
     }
 
     /**
@@ -589,31 +534,31 @@
 
     /**
      * Flag this function as using the {@code with} keyword
+     * @param ancestors the iterator over functions in this functions's containing lexical context
      */
-    public void setHasWith() {
+    public void setHasWith(final Iterator<FunctionNode> ancestors) {
         if(!hasWith()) {
             this.flags |= HAS_WITH;
             // with requires scope in parents.
             // TODO: refine this. with should not force all variables in parents to be in scope, only those that are
             // actually referenced as identifiers by name
-            markParentForWithOrEval();
+            markParentForWithOrEval(ancestors);
         }
     }
 
-    private void markParentForWithOrEval() {
+    private void markParentForWithOrEval(final Iterator<FunctionNode> ancestors) {
         // If this is invoked, then either us or a descendant uses with or eval, meaning we must have our own scope.
         setNeedsScope();
 
-        final FunctionNode parentFunction = findParentFunction();
-        if(parentFunction != null) {
-            parentFunction.setDescendantHasWithOrEval();
+        if(ancestors.hasNext()) {
+            ancestors.next().setDescendantHasWithOrEval(ancestors);
         }
     }
 
-    private void setDescendantHasWithOrEval() {
+    private void setDescendantHasWithOrEval(final Iterator<FunctionNode> ancestors) {
         if((flags & HAS_DESCENDANT_WITH_OR_EVAL) == 0) {
             flags |= HAS_DESCENDANT_WITH_OR_EVAL;
-            markParentForWithOrEval();
+            markParentForWithOrEval(ancestors);
         }
     }
 
@@ -628,11 +573,12 @@
 
     /**
      * Flag this function as calling the {@code eval} function
+     * @param ancestors the iterator over functions in this functions's containing lexical context
      */
-    public void setHasEval() {
+    public void setHasEval(final Iterator<FunctionNode> ancestors) {
         if(!hasEval()) {
             this.flags |= HAS_EVAL;
-            markParentForWithOrEval();
+            markParentForWithOrEval(ancestors);
         }
     }
 
@@ -665,11 +611,34 @@
     }
 
     /**
-     * Get all nested functions
-     * @return list of nested functions in this function
+     * Returns a list of functions declared by this function. Only includes declared functions, and does not include any
+     * function expressions that might occur in its body.
+     * @return a list of functions declared by this function.
      */
-    public List<FunctionNode> getFunctions() {
-        return Collections.unmodifiableList(functions);
+    public List<FunctionNode> getDeclaredFunctions() {
+        // Note that the function does not have a dedicated list of declared functions, but rather relies on the
+        // invariant that all function declarations are at the beginning of the statement list as VarNode with a
+        // FunctionNode marked as statement with its variable initializer. Every VarNode is also preceded by a
+        // LineNumberNode. This invariant is established by the parser and has to be preserved in visitors.
+        final List<FunctionNode> fns = new ArrayList<>();
+        for (final Node stmt : statements) {
+            if(stmt instanceof LineNumberNode) {
+                continue;
+            } else if(stmt instanceof VarNode) {
+                final Node init = ((VarNode)stmt).getInit();
+                if(init instanceof FunctionNode) {
+                    final FunctionNode fn = (FunctionNode)init;
+                    if(fn.isDeclared()) {
+                        fns.add(fn);
+                        continue;
+                    }
+                }
+            }
+            // Node is neither a LineNumberNode, nor a function declaration VarNode. Since all function declarations are
+            // at the start of the function, we've reached the end of function declarations.
+            break;
+        }
+        return fns;
     }
 
     /**
@@ -801,7 +770,7 @@
     public boolean needsArguments() {
         // uses "arguments" or calls eval, but it does not redefine "arguments", and finally, it's not a script, since
         // for top-level script, "arguments" is picked up from Context by Global.init() instead.
-        return (flags & MAYBE_NEEDS_ARGUMENTS) != 0 && (flags & DEFINES_ARGUMENTS) == 0 && !isScript();
+        return (flags & MAYBE_NEEDS_ARGUMENTS) != 0 && (flags & DEFINES_ARGUMENTS) == 0 && !isProgram();
     }
 
     /**
@@ -820,7 +789,7 @@
      * @return true if the function needs parent scope.
      */
     public boolean needsParentScope() {
-        return (flags & NEEDS_PARENT_SCOPE) != 0 || isScript();
+        return (flags & NEEDS_PARENT_SCOPE) != 0 || isProgram();
     }
 
     /**
@@ -880,7 +849,7 @@
      * @return true if all variables should be in scope
      */
     public boolean allVarsInScope() {
-        return isScript() || (flags & HAS_ALL_VARS_IN_SCOPE) != 0;
+        return isProgram() || (flags & HAS_ALL_VARS_IN_SCOPE) != 0;
     }
 
     /**
@@ -989,19 +958,19 @@
     }
 
     /**
-     * Check if this function is a statement
-     * @return true if function is a statement
+     * Check if this function is created as a function declaration (as opposed to function expression)
+     * @return true if function is declared.
      */
-    public boolean isStatement() {
-        return (flags & IS_STATEMENT) != 0;
+    public boolean isDeclared() {
+        return (flags & IS_DECLARED) != 0;
     }
 
     /**
-     * Flag this function as a statement
+     * Flag this function as being created as a function declaration (as opposed to a function expression).
      * @see Parser
      */
-    public void setIsStatement() {
-        this.flags |= IS_STATEMENT;
+    public void setIsDeclared() {
+        this.flags |= IS_DECLARED;
     }
 
     /**
@@ -1049,35 +1018,16 @@
     }
 
     /**
-     * Marks this function as one using any global symbol. The function and all its parent functions will all be marked
-     * as needing parent scope.
-     * @see #needsParentScope()
+     * Marks this function as using any of its ancestors' scopes.
      */
-    public void setUsesGlobalSymbol() {
+    public void setUsesAncestorScope() {
         this.flags |= USES_ANCESTOR_SCOPE;
-        final FunctionNode parentFn = findParentFunction();
-        if(parentFn != null) {
-            parentFn.setUsesGlobalSymbol();
-        }
     }
 
-    /**
-     * Marks this function as using a specified scoped symbol. The function and its parent functions up to but not
-     * including the function defining the symbol will be marked as needing parent scope. The function defining the
-     * symbol will be marked as one that needs to have its own scope.
-     * @param symbol the symbol being used.
-     * @see #needsParentScope()
-     */
-    public void setUsesScopeSymbol(final Symbol symbol) {
-        if(symbol.getBlock() == this) {
-            setNeedsScope();
-        } else {
-            this.flags |= USES_ANCESTOR_SCOPE;
-            final FunctionNode parentFn = findParentFunction();
-            if (parentFn != null) {
-                parentFn.setUsesScopeSymbol(symbol);
-            }
-        }
+    @Override
+    void setUsesParentScopeSymbol(Symbol symbol, Iterator<Block> ancestors) {
+        setUsesAncestorScope();
+        super.setUsesParentScopeSymbol(symbol, ancestors);
     }
 
     /**
@@ -1152,7 +1102,7 @@
 
     @Override
     public Type getType() {
-        return getReturnType();
+        return FUNCTION_TYPE;
     }
 
     /**
@@ -1212,56 +1162,6 @@
     }
 
     /**
-     * Add a new function to the function list.
-     *
-     * @param functionNode Function node to add.
-     */
-    @Override
-    public void addFunction(final FunctionNode functionNode) {
-        assert functionNode != null;
-        functions.add(functionNode);
-    }
-
-    /**
-     * Add a list of functions to the function list.
-     *
-     * @param functionNodes  Function nodes to add.
-     */
-    @Override
-    public void addFunctions(final List<FunctionNode> functionNodes) {
-        functions.addAll(functionNodes);
-    }
-
-    /**
-     * Set a function list
-     *
-     * @param functionNodes to set
-     */
-    @Override
-    public void setFunctions(final List<FunctionNode> functionNodes) {
-        this.functions = functionNodes;
-    }
-
-    /**
-     * Add a variable declaration that should be visible to the entire function
-     * scope. Parser does this.
-     *
-     * @param varNode a var node
-     */
-    public void addDeclaration(final VarNode varNode) {
-        declarations.add(varNode);
-    }
-
-    /**
-     * Return all variable declarations from this function scope
-     *
-     * @return all VarNodes in scope
-     */
-    public List<VarNode> getDeclarations() {
-        return Collections.unmodifiableList(declarations);
-    }
-
-    /**
      * Get the compile unit used to compile this function
      * @see Compiler
      * @return the compile unit
@@ -1294,32 +1194,4 @@
     public void setMethodEmitter(final MethodEmitter method) {
         this.method = method;
     }
-
-    /**
-     * Each FunctionNode maintains a list of reference to its parent blocks.
-     * Add a parent block to this function.
-     *
-     * @param parentBlock  a block to remember as parent
-     */
-    public void addReferencingParentBlock(final Block parentBlock) {
-        assert parentBlock.getFunction() == function.findParentFunction() : Debug.id(parentBlock.getFunction()) + "!=" + Debug.id(function.findParentFunction()); // all parent blocks must be in the same function
-        if (parentBlock != function.getParent()) {
-            if (referencingParentBlocks == null) {
-                referencingParentBlocks = new LinkedList<>();
-            }
-            referencingParentBlocks.add(parentBlock);
-        }
-    }
-
-    /**
-     * Get the known parent blocks to this function
-     *
-     * @return list of parent blocks
-     */
-    public List<Block> getReferencingParentBlocks() {
-        if (referencingParentBlocks == null) {
-            return Collections.emptyList();
-        }
-        return Collections.unmodifiableList(referencingParentBlocks);
-    }
 }
--- a/nashorn/src/jdk/nashorn/internal/ir/IdentNode.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/IdentNode.java	Sat Mar 23 00:58:39 2013 +0100
@@ -38,18 +38,18 @@
 /**
  * IR representation for an identifier.
  */
-public class IdentNode extends Node implements PropertyKey, TypeOverride, FunctionCall {
+public class IdentNode extends Node implements PropertyKey, TypeOverride<IdentNode>, FunctionCall {
+    private static final int PROPERTY_NAME    = 1 << 0;
+    private static final int INITIALIZED_HERE = 1 << 1;
+    private static final int FUNCTION         = 1 << 2;
+
     /** Identifier. */
     private final String name;
 
     /** Type for a callsite, e.g. X in a get()X or a set(X)V */
     private Type callSiteType;
 
-    /** flag for an ident that is the property name of an AccessNode. */
-    private boolean isPropertyName;
-
-    /** flag for an ident on the left hand side of <code>var lhs = rhs;</code>. */
-    private boolean isInitializedHere;
+    private byte flags;
 
     /**
      * Constructor
@@ -71,9 +71,8 @@
      */
     public IdentNode(final IdentNode identNode) {
         super(identNode);
-        this.name              = identNode.getName();
-        this.isPropertyName    = identNode.isPropertyName;
-        this.isInitializedHere = identNode.isInitializedHere;
+        this.name  = identNode.getName();
+        this.flags = identNode.flags;
     }
 
     @Override
@@ -92,12 +91,17 @@
     }
 
     @Override
-    public void setType(final Type type) {
+    public IdentNode setType(final Type type) {
         if (DEBUG_FIELDS && getSymbol() != null && !Type.areEquivalent(getSymbol().getSymbolType(), type)) {
             ObjectClassGenerator.LOG.info(getClass().getName() + " " + this + " => " + type + " instead of " + getType());
         }
-        this.callSiteType = type;
         // do NOT, repeat NOT touch the symbol here. it might be a local variable or whatever. This is the override if it isn't
+        if(this.callSiteType == type) {
+            return this;
+        }
+        final IdentNode n = (IdentNode)clone();
+        n.callSiteType = type;
+        return n;
     }
 
     @Override
@@ -131,8 +135,8 @@
      */
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
-            return visitor.leave(this);
+        if (visitor.enterIdentNode(this) != null) {
+            return visitor.leaveIdentNode(this);
         }
 
         return this;
@@ -179,14 +183,18 @@
      * @return true if this is a property name
      */
     public boolean isPropertyName() {
-        return isPropertyName;
+        return (flags & PROPERTY_NAME) != 0;
     }
 
     /**
      * Flag this IdentNode as a property name
+     * @return a node equivalent to this one except for the requested change.
      */
-    public void setIsPropertyName() {
-        isPropertyName = true;
+    public IdentNode setIsPropertyName() {
+        if(isPropertyName()) return this;
+        final IdentNode n = (IdentNode)clone();
+        n.flags |= PROPERTY_NAME;
+        return n;
     }
 
     /**
@@ -194,14 +202,18 @@
      * @return true if IdentNode is initialized on creation
      */
     public boolean isInitializedHere() {
-        return isInitializedHere;
+        return (flags & INITIALIZED_HERE) != 0;
     }
 
     /**
      * Flag IdentNode to be initialized on creation
+     * @return a node equivalent to this one except for the requested change.
      */
-    public void setIsInitializedHere() {
-        isInitializedHere = true;
+    public IdentNode setIsInitializedHere() {
+        if(isInitializedHere()) return this;
+        final IdentNode n = (IdentNode)clone();
+        n.flags |= INITIALIZED_HERE;
+        return n;
     }
 
     /**
@@ -216,6 +228,17 @@
 
     @Override
     public boolean isFunction() {
-        return false;
+        return (flags & FUNCTION) != 0;
+    }
+
+    /**
+     * Mark this node as being the callee operand of a {@link CallNode}.
+     * @return an ident node identical to this one in all aspects except with its function flag set.
+     */
+    public IdentNode setIsFunction() {
+        if(isFunction()) return this;
+        final IdentNode n = (IdentNode)clone();
+        n.flags |= FUNCTION;
+        return n;
     }
 }
--- a/nashorn/src/jdk/nashorn/internal/ir/IfNode.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/IfNode.java	Sat Mar 23 00:58:39 2013 +0100
@@ -75,7 +75,7 @@
 
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
+        if (visitor.enterIfNode(this) != null) {
             test = test.accept(visitor);
 
             pass = (Block)pass.accept(visitor);
@@ -84,7 +84,7 @@
                 fail = (Block)fail.accept(visitor);
             }
 
-            return visitor.leave(this);
+            return visitor.leaveIfNode(this);
         }
 
         return this;
--- a/nashorn/src/jdk/nashorn/internal/ir/IndexNode.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/IndexNode.java	Sat Mar 23 00:58:39 2013 +0100
@@ -36,7 +36,7 @@
  * IR representation of an indexed access (brackets operator.)
  *
  */
-public class IndexNode extends BaseNode implements TypeOverride {
+public class IndexNode extends BaseNode implements TypeOverride<IndexNode> {
     /** Property ident. */
     private Node index;
 
@@ -92,10 +92,10 @@
 
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
+        if (visitor.enterIndexNode(this) != null) {
             base = base.accept(visitor);
             index = index.accept(visitor);
-            return visitor.leave(this);
+            return visitor.leaveIndexNode(this);
         }
 
         return this;
@@ -144,12 +144,13 @@
     }
 
     @Override
-    public void setType(final Type type) {
+    public IndexNode setType(final Type type) {
         if (DEBUG_FIELDS && !Type.areEquivalent(getSymbol().getSymbolType(), type)) {
             ObjectClassGenerator.LOG.info(getClass().getName() + " " + this + " => " + type + " instead of " + getType());
         }
         hasCallSiteType = true;
         getSymbol().setTypeOverride(type);
+        return this;
     }
 
     @Override
--- a/nashorn/src/jdk/nashorn/internal/ir/LabelNode.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/LabelNode.java	Sat Mar 23 00:58:39 2013 +0100
@@ -81,10 +81,10 @@
 
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
+        if (visitor.enterLabelNode(this) != null) {
             label = (IdentNode)label.accept(visitor);
             body  = (Block)body.accept(visitor);
-            return visitor.leave(this);
+            return visitor.leaveLabelNode(this);
         }
 
         return this;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/ir/LexicalContext.java	Sat Mar 23 00:58:39 2013 +0100
@@ -0,0 +1,198 @@
+package jdk.nashorn.internal.ir;
+
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * A class that tracks the current lexical context of node visitation as a stack of {@link Block} nodes. Has special
+ * methods to retrieve useful subsets of the context.
+ */
+public class LexicalContext implements Cloneable {
+    private final Deque<Block> lexicalContext;
+
+    /**
+     * Creates a new empty lexical context.
+     */
+    public LexicalContext() {
+        lexicalContext = new ArrayDeque<>();
+    }
+
+    /**
+     * Pushes a new block on top of the context, making it the innermost open block.
+     * @param block the new block
+     */
+    public void push(Block block) {
+        //new Exception(block.toString()).printStackTrace();
+        lexicalContext.push(block);
+    }
+
+    /**
+     * Pops the innermost block off the context.
+     * @param the block expected to be popped, used to detect unbalanced pushes/pops
+     */
+    public void pop(Block block) {
+        final Block popped = lexicalContext.pop();
+        assert popped == block;
+    }
+
+    /**
+     * Returns an iterator over all blocks in the context, with the top block (innermost lexical context) first.
+     * @return an iterator over all blocks in the context.
+     */
+    public Iterator<Block> getBlocks() {
+        return lexicalContext.iterator();
+    }
+
+    /**
+     * Returns an iterator over all functions in the context, with the top (innermost open) function first.
+     * @return an iterator over all functions in the context.
+     */
+    public Iterator<FunctionNode> getFunctions() {
+        return new FunctionIterator(getBlocks());
+    }
+
+    private static final class FunctionIterator implements Iterator<FunctionNode> {
+        private final Iterator<Block> it;
+        private FunctionNode next;
+
+        FunctionIterator(Iterator<Block> it) {
+            this.it = it;
+            next = findNext();
+        }
+
+        @Override
+        public boolean hasNext() {
+            return next != null;
+        }
+
+        @Override
+        public FunctionNode next() {
+            if(next == null) {
+                throw new NoSuchElementException();
+            }
+            FunctionNode lnext = next;
+            next = findNext();
+            return lnext;
+        }
+
+        private FunctionNode findNext() {
+            while(it.hasNext()) {
+                final Block block = it.next();
+                if(block instanceof FunctionNode) {
+                    return ((FunctionNode)block);
+                }
+            }
+            return null;
+        }
+
+        @Override
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    /**
+     * Returns an iterator over all ancestors block of the given block, with its parent block first.
+     * @param block the block whose ancestors are returned
+     * @return an iterator over all ancestors block of the given block.
+     */
+    public Iterator<Block> getAncestorBlocks(Block block) {
+        final Iterator<Block> it = getBlocks();
+        while(it.hasNext()) {
+            final Block b = it.next();
+            if(block == b) {
+                return it;
+            }
+        }
+        throw new AssertionError("Block is not on the current lexical context stack");
+    }
+
+    /**
+     * Returns an iterator over a block and all its ancestors blocks, with the block first.
+     * @param block the block that is the starting point of the iteration.
+     * @return an iterator over a block and all its ancestors.
+     */
+    public Iterator<Block> getBlocks(final Block block) {
+        final Iterator<Block> it = getAncestorBlocks(block);
+        return new Iterator<Block>() {
+            boolean blockReturned = false;
+            @Override
+            public boolean hasNext() {
+                return it.hasNext() || !blockReturned;
+            }
+            @Override
+            public Block next() {
+                if(blockReturned) {
+                    return it.next();
+                }
+                blockReturned = true;
+                return block;
+            }
+            @Override
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+        };
+    }
+
+    /**
+     * Returns the closest function node to the block. If the block is itself a function, it is returned.
+     * @param block the block
+     * @return the function closest to the block.
+     * @see #getParentFunction(Block)
+     */
+    public FunctionNode getFunction(Block block) {
+        if(block instanceof FunctionNode) {
+            return (FunctionNode)block;
+        }
+        return getParentFunction(block);
+    }
+
+    /**
+     * Returns the closest function node to the block and all its ancestor functions. If the block is itself a function,
+     * it is returned too.
+     * @param block the block
+     * @return the closest function node to the block and all its ancestor functions.
+     */
+    public Iterator<FunctionNode> getFunctions(final Block block) {
+        return new FunctionIterator(getBlocks(block));
+    }
+
+    /**
+     * Returns the containing function of the block. If the block is itself a function, its parent function is returned.
+     * @param block the block
+     * @return the containing function of the block.
+     * @see #getFunction(Block)
+     */
+    public FunctionNode getParentFunction(Block block) {
+        return getFirstFunction(getAncestorBlocks(block));
+    }
+
+    private static FunctionNode getFirstFunction(Iterator<Block> it) {
+        while(it.hasNext()) {
+            final Block ancestor = it.next();
+            if(ancestor instanceof FunctionNode) {
+                return (FunctionNode)ancestor;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns the innermost block in the context.
+     * @return the innermost block in the context.
+     */
+    public Block getCurrentBlock() {
+        return lexicalContext.element();
+    }
+
+    /**
+     * Returns the innermost function in the context.
+     * @return the innermost function in the context.
+     */
+    public FunctionNode getCurrentFunction() {
+        return getFirstFunction(getBlocks());
+    }
+}
--- a/nashorn/src/jdk/nashorn/internal/ir/LineNumberNode.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/LineNumberNode.java	Sat Mar 23 00:58:39 2013 +0100
@@ -63,8 +63,8 @@
 
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
-            return visitor.leave(this);
+        if (visitor.enterLineNumberNode(this) != null) {
+            return visitor.leaveLineNumberNode(this);
         }
 
         return this;
--- a/nashorn/src/jdk/nashorn/internal/ir/LiteralNode.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/LiteralNode.java	Sat Mar 23 00:58:39 2013 +0100
@@ -46,7 +46,7 @@
  */
 public abstract class LiteralNode<T> extends Node implements PropertyKey {
     /** Literal value */
-    protected T value;
+    protected final T value;
 
      /**
      * Constructor
@@ -67,8 +67,17 @@
      * @param literalNode source node
      */
     protected LiteralNode(final LiteralNode<T> literalNode) {
+        this(literalNode, literalNode.value);
+    }
+
+    /**
+     * A copy constructor with value change.
+     * @param literalNode the original literal node
+     * @param newValue new value for this node
+     */
+    protected LiteralNode(final LiteralNode<T> literalNode, final T newValue) {
         super(literalNode);
-        this.value = literalNode.value;
+        this.value = newValue;
     }
 
     @Override
@@ -217,8 +226,8 @@
      */
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
-            return visitor.leave(this);
+        if (visitor.enterLiteralNode(this) != null) {
+            return visitor.leaveLiteralNode(this);
         }
 
         return this;
@@ -544,6 +553,10 @@
             super(literalNode);
         }
 
+        private NodeLiteralNode(final LiteralNode<Node> literalNode, final Node value) {
+            super(literalNode, value);
+        }
+
         @Override
         protected Node copy(final CopyState cs) {
             return new NodeLiteralNode(this);
@@ -551,11 +564,14 @@
 
         @Override
         public Node accept(final NodeVisitor visitor) {
-            if (visitor.enter(this) != null) {
+            if (visitor.enterLiteralNode(this) != null) {
                 if (value != null) {
-                    value = value.accept(visitor);
+                    final Node newValue = value.accept(visitor);
+                    if(value != newValue) {
+                        return visitor.leaveLiteralNode(new NodeLiteralNode(this, newValue));
+                    }
                 }
-                return visitor.leave(this);
+                return visitor.leaveLiteralNode(this);
             }
 
             return this;
@@ -878,14 +894,14 @@
 
         @Override
         public Node accept(final NodeVisitor visitor) {
-            if (visitor.enter(this) != null) {
+            if (visitor.enterLiteralNode(this) != null) {
                 for (int i = 0; i < value.length; i++) {
                     final Node element = value[i];
                     if (element != null) {
                         value[i] = element.accept(visitor);
                     }
                 }
-                return visitor.leave(this);
+                return visitor.leaveLiteralNode(this);
             }
             return this;
         }
--- a/nashorn/src/jdk/nashorn/internal/ir/Location.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/Location.java	Sat Mar 23 00:58:39 2013 +0100
@@ -65,7 +65,11 @@
 
     @Override
     protected Object clone() {
-        return new Location(this);
+        try {
+            return super.clone();
+        } catch(CloneNotSupportedException e) {
+            throw new AssertionError(e);
+        }
     }
 
     @Override
--- a/nashorn/src/jdk/nashorn/internal/ir/Node.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/Node.java	Sat Mar 23 00:58:39 2013 +0100
@@ -165,12 +165,19 @@
             return true;
         }
 
-        setIsResolved();
+        setIsResolved(true);
 
         return false;
     }
 
     /**
+     * Reset the resolved flag.
+     */
+    public void resetResolved() {
+        setIsResolved(false);
+    }
+
+    /**
      * Is this a debug info node like LineNumberNode etc?
      *
      * @return true if this is a debug node
@@ -234,8 +241,7 @@
      *
      * @return Deep copy of the  Node.
      */
-    @Override
-    public final Node clone() {
+    public final Node copy() {
         return copy(new CopyState());
     }
 
@@ -349,10 +355,10 @@
     }
 
     /**
-     * Flag this node as resolved, i.e. code has been generated for it
+     * Flag this node as resolved or not, i.e. code has been generated for it
      */
-    public void setIsResolved() {
-        this.isResolved = true;
+    private void setIsResolved(boolean isResolved) {
+        this.isResolved = isResolved;
     }
 
     /**
--- a/nashorn/src/jdk/nashorn/internal/ir/ObjectNode.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/ObjectNode.java	Sat Mar 23 00:58:39 2013 +0100
@@ -72,12 +72,12 @@
 
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
+        if (visitor.enterObjectNode(this) != null) {
             for (int i = 0, count = elements.size(); i < count; i++) {
                 elements.set(i, elements.get(i).accept(visitor));
             }
 
-            return visitor.leave(this);
+            return visitor.leaveObjectNode(this);
         }
 
         return this;
--- a/nashorn/src/jdk/nashorn/internal/ir/PropertyNode.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/PropertyNode.java	Sat Mar 23 00:58:39 2013 +0100
@@ -88,7 +88,7 @@
 
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
+        if (visitor.enterPropertyNode(this) != null) {
             key = (PropertyKey)((Node)key).accept(visitor);
 
             if (value != null) {
@@ -103,7 +103,7 @@
                 setter = setter.accept(visitor);
             }
 
-            return visitor.leave(this);
+            return visitor.leavePropertyNode(this);
         }
 
         return this;
--- a/nashorn/src/jdk/nashorn/internal/ir/ReferenceNode.java	Tue Mar 19 11:03:24 2013 -0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,91 +0,0 @@
-/*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package jdk.nashorn.internal.ir;
-
-import jdk.nashorn.internal.ir.annotations.Reference;
-import jdk.nashorn.internal.ir.visitor.NodeVisitor;
-import jdk.nashorn.internal.runtime.Source;
-
-/**
- * IR representation of a reference to another entity (function.)
- */
-public class ReferenceNode extends Node {
-    /** Node referenced. */
-    @Reference
-    private final FunctionNode reference;
-
-    /**
-     * Constructor
-     *
-     * @param source the source
-     * @param token  token
-     * @param finish finish
-     * @param reference the function node to reference
-     */
-    public ReferenceNode(final Source source, final long token, final int finish, final FunctionNode reference) {
-        super(source, token, finish);
-
-        this.reference = reference;
-    }
-
-    private ReferenceNode(final ReferenceNode referenceNode) {
-        super(referenceNode);
-
-        this.reference = referenceNode.reference;
-    }
-
-    @Override
-    protected Node copy(final CopyState cs) {
-        return new ReferenceNode(this);
-    }
-
-    @Override
-    public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
-            return visitor.leave(this);
-        }
-
-        return this;
-    }
-
-    @Override
-    public void toString(final StringBuilder sb) {
-        if (reference == null) {
-            sb.append("null");
-        } else {
-            reference.toString(sb);
-        }
-    }
-
-    /**
-     * Get there function node reference that this node points tp
-     * @return a function node reference
-     */
-    public FunctionNode getReference() {
-        return reference;
-    }
-
-}
--- a/nashorn/src/jdk/nashorn/internal/ir/ReturnNode.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/ReturnNode.java	Sat Mar 23 00:58:39 2013 +0100
@@ -100,12 +100,12 @@
 
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
+        if (visitor.enterReturnNode(this) != null) {
             if (expression != null) {
                 expression = expression.accept(visitor);
             }
 
-            return visitor.leave(this);
+            return visitor.leaveReturnNode(this);
         }
 
         return this;
--- a/nashorn/src/jdk/nashorn/internal/ir/RuntimeNode.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/RuntimeNode.java	Sat Mar 23 00:58:39 2013 +0100
@@ -38,7 +38,7 @@
  * IR representation for a runtime call.
  *
  */
-public class RuntimeNode extends Node implements TypeOverride {
+public class RuntimeNode extends Node implements TypeOverride<RuntimeNode> {
 
     /**
      * Request enum used for meta-information about the runtime request
@@ -393,8 +393,9 @@
     }
 
     @Override
-    public void setType(final Type type) {
+    public RuntimeNode setType(final Type type) {
         this.callSiteType = type;
+        return this;
     }
 
     @Override
@@ -408,12 +409,12 @@
 
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
+        if (visitor.enterRuntimeNode(this) != null) {
             for (int i = 0, count = args.size(); i < count; i++) {
                 args.set(i, args.get(i).accept(visitor));
             }
 
-            return visitor.leave(this);
+            return visitor.leaveRuntimeNode(this);
         }
 
         return this;
--- a/nashorn/src/jdk/nashorn/internal/ir/SplitNode.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/SplitNode.java	Sat Mar 23 00:58:39 2013 +0100
@@ -108,10 +108,10 @@
         visitor.setCurrentMethodEmitter(getMethodEmitter());
 
         try {
-            if (visitor.enter(this) != null) {
+            if (visitor.enterSplitNode(this) != null) {
                 body = body.accept(visitor);
 
-                return visitor.leave(this);
+                return visitor.leaveSplitNode(this);
             }
         } finally {
             visitor.setCurrentCompileUnit(saveCompileUnit);
--- a/nashorn/src/jdk/nashorn/internal/ir/SwitchNode.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/SwitchNode.java	Sat Mar 23 00:58:39 2013 +0100
@@ -85,7 +85,7 @@
 
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
+        if (visitor.enterSwitchNode(this) != null) {
             expression = expression.accept(visitor);
 
             for (int i = 0, count = cases.size(); i < count; i++) {
@@ -94,7 +94,7 @@
 
             //the default case is in the cases list and should not be explicitly traversed!
 
-            return visitor.leave(this);
+            return visitor.leaveSwitchNode(this);
         }
 
         return this;
--- a/nashorn/src/jdk/nashorn/internal/ir/Symbol.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/Symbol.java	Sat Mar 23 00:58:39 2013 +0100
@@ -38,31 +38,31 @@
  */
 
 public final class Symbol implements Comparable<Symbol> {
-    /** Symbol flags. Kind ordered by precedence. */
-    public static final int IS_TEMP     = 0b0000_0001;
+    /** Symbol kinds. Kind ordered by precedence. */
+    public static final int IS_TEMP     = 1;
     /** Is this Global */
-    public static final int IS_GLOBAL   = 0b0000_0010;
+    public static final int IS_GLOBAL   = 2;
     /** Is this a variable */
-    public static final int IS_VAR      = 0b0000_0011;
+    public static final int IS_VAR      = 3;
     /** Is this a parameter */
-    public static final int IS_PARAM    = 0b0000_0100;
+    public static final int IS_PARAM    = 4;
     /** Is this a constant */
-    public static final int IS_CONSTANT = 0b0000_0101;
-
-    static final int KINDMASK = 0b0000_1111;
+    public static final int IS_CONSTANT = 5;
+    /** Mask for kind flags */
+    public static final int KINDMASK = (1 << 3) - 1; // Kinds are represented by lower three bits
 
     /** Is this scope */
-    public static final int IS_SCOPE         = 0b0000_0001_0000;
+    public static final int IS_SCOPE         = 1 << 4;
     /** Is this a this symbol */
-    public static final int IS_THIS          = 0b0000_0010_0000;
+    public static final int IS_THIS          = 1 << 5;
     /** Can this symbol ever be undefined */
-    public static final int CAN_BE_UNDEFINED = 0b0000_0100_0000;
+    public static final int CAN_BE_UNDEFINED = 1 << 6;
     /** Can this symbol ever have primitive types */
-    public static final int CAN_BE_PRIMITIVE = 0b0000_1000_0000;
+    public static final int CAN_BE_PRIMITIVE = 1 << 7;
     /** Is this a let */
-    public static final int IS_LET           = 0b0001_0000_0000;
+    public static final int IS_LET           = 1 << 8;
     /** Is this an internal symbol, never represented explicitly in source code */
-    public static final int IS_INTERNAL      = 0b0010_0000_0000;
+    public static final int IS_INTERNAL      = 1 << 9;
 
     /** Null or name identifying symbol. */
     private final String name;
@@ -269,15 +269,6 @@
         return type.isCategory2() ? 2 : 1;
     }
 
-    /**
-     * Return the defining function (scope.)
-     *
-     * @return Defining function.
-     */
-    public FunctionNode findFunction() {
-        return block != null ? block.getFunction() : null;
-    }
-
     @Override
     public boolean equals(final Object other) {
         if (!(other instanceof Symbol)) {
@@ -487,27 +478,6 @@
     }
 
     /**
-     * Check if this symbol can be accessed directly with a putfield or getfield or dynamic load
-     *
-     * @param currentFunction function to check for fast scope
-     * @return true if fast scope
-     */
-    public boolean isFastScope(final FunctionNode currentFunction) {
-        if (!isScope() || !block.needsScope()) {
-            return false;
-        }
-        // Allow fast scope access if no parent function contains with or eval
-        FunctionNode func = currentFunction;
-        while (func != null) {
-            if (func.hasWith() || func.hasEval()) {
-                return false;
-            }
-            func = func.findParentFunction();
-        }
-        return true;
-    }
-
-    /**
      * Get the block in which the symbol is defined
      * @return a block
      */
@@ -651,7 +621,7 @@
      * @return true if this this is a global scope symbol
      */
     public boolean isTopLevel() {
-        return block instanceof FunctionNode && ((FunctionNode) block).isScript();
+        return block instanceof FunctionNode && ((FunctionNode) block).isProgram();
     }
 
 
--- a/nashorn/src/jdk/nashorn/internal/ir/TernaryNode.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/TernaryNode.java	Sat Mar 23 00:58:39 2013 +0100
@@ -77,11 +77,11 @@
 
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
-            lhs = lhs.accept(visitor);
-            rhs = rhs.accept(visitor);
-            third = third.accept(visitor);
-            return visitor.leave(this);
+        if (visitor.enterTernaryNode(this) != null) {
+            final Node newLhs = lhs().accept(visitor);
+            final Node newRhs = rhs().accept(visitor);
+            final Node newThird = third.accept(visitor);
+            return visitor.leaveTernaryNode((TernaryNode)setThird(newThird).setLHS(newLhs).setRHS(newRhs));
         }
 
         return this;
@@ -133,8 +133,12 @@
     /**
      * Reset the "third" node for this ternary expression, i.e. "z" in x ? y : z
      * @param third a node
+     * @return a node equivalent to this one except for the requested change.
      */
-    public void setThird(final Node third) {
-        this.third = third;
+    public TernaryNode setThird(final Node third) {
+        if(this.third == third) return this;
+        final TernaryNode n = (TernaryNode)clone();
+        n.third = third;
+        return n;
     }
 }
--- a/nashorn/src/jdk/nashorn/internal/ir/ThrowNode.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/ThrowNode.java	Sat Mar 23 00:58:39 2013 +0100
@@ -75,9 +75,9 @@
      */
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
+        if (visitor.enterThrowNode(this) != null) {
             setExpression(expression.accept(visitor));
-            return visitor.leave(this);
+            return visitor.leaveThrowNode(this);
         }
 
         return this;
--- a/nashorn/src/jdk/nashorn/internal/ir/TryNode.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/TryNode.java	Sat Mar 23 00:58:39 2013 +0100
@@ -101,7 +101,7 @@
      */
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
+        if (visitor.enterTryNode(this) != null) {
             // Need to do first for termination analysis.
             if (finallyBody != null) {
                 finallyBody = (Block)finallyBody.accept(visitor);
@@ -115,7 +115,7 @@
             }
             this.catchBlocks = newCatchBlocks;
 
-            return visitor.leave(this);
+            return visitor.leaveTryNode(this);
         }
 
         return this;
@@ -155,6 +155,15 @@
     }
 
     /**
+     * Returns true if the specified block is the body of this try block, or any of its catch blocks.
+     * @param block the block
+     * @return true if the specified block is the body of this try block, or any of its catch blocks.
+     */
+    public boolean isChildBlock(Block block) {
+        return body == block || catchBlocks.contains(block);
+    }
+
+    /**
      * Get the catch blocks for this try block
      * @return a list of blocks
      */
--- a/nashorn/src/jdk/nashorn/internal/ir/TypeOverride.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/TypeOverride.java	Sat Mar 23 00:58:39 2013 +0100
@@ -36,15 +36,17 @@
  * by using JSType.toInt32. Especially in scenarios where the field is already stored
  * as a primitive, this will be much faster than the "object is all I see" scope
  * available in the method
+ * @param <T> the type of the node implementing the interface
  */
 
-public interface TypeOverride {
+public interface TypeOverride<T extends Node> {
     /**
      * Set the override type
      *
      * @param type  the type
+     * @return a node equivalent to this one except for the requested change.
      */
-    public void setType(final Type type);
+    public T setType(final Type type);
 
     /**
      * Returns true if this node can have a callsite override, e.g. all scope ident nodes
--- a/nashorn/src/jdk/nashorn/internal/ir/UnaryNode.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/UnaryNode.java	Sat Mar 23 00:58:39 2013 +0100
@@ -41,7 +41,7 @@
  */
 public class UnaryNode extends Node implements Assignment<Node> {
     /** Right hand side argument. */
-    protected Node rhs;
+    private Node rhs;
 
     /**
      * Constructor
@@ -104,6 +104,11 @@
     }
 
     @Override
+    public Node setAssignmentDest(Node n) {
+        return setRHS(n);
+    }
+
+    @Override
     public Node getAssignmentSource() {
         return getAssignmentDest();
     }
@@ -132,9 +137,8 @@
      */
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
-            rhs = rhs.accept(visitor);
-            return visitor.leave(this);
+        if (visitor.enterUnaryNode(this) != null) {
+            return visitor.leaveUnaryNode(setRHS(rhs.accept(visitor)));
         }
 
         return this;
@@ -212,10 +216,12 @@
      * @see BinaryNode
      *
      * @param rhs right hand side or expression node
+     * @return a node equivalent to this one except for the requested change.
      */
-    public void setRHS(final Node rhs) {
-        this.rhs = rhs;
+    public UnaryNode setRHS(final Node rhs) {
+        if(this.rhs == rhs) return this;
+        final UnaryNode n = (UnaryNode)clone();
+        n.rhs = rhs;
+        return n;
     }
-
-
 }
--- a/nashorn/src/jdk/nashorn/internal/ir/VarNode.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/VarNode.java	Sat Mar 23 00:58:39 2013 +0100
@@ -38,8 +38,8 @@
     /** Initialization expression. */
     private Node init;
 
-    /** Is this a function var node */
-    private boolean isFunctionVarNode;
+    /** Is this a var statement (as opposed to a "var" in a for loop statement) */
+    private final boolean isStatement;
 
     /**
      * Constructor
@@ -51,20 +51,34 @@
      * @param init   init node or null if just a declaration
      */
     public VarNode(final Source source, final long token, final int finish, final IdentNode name, final Node init) {
+        this(source, token, finish, name, init, true);
+    }
+
+    /**
+     * Constructor
+     *
+     * @param source the source
+     * @param token  token
+     * @param finish finish
+     * @param name   name of variable
+     * @param init   init node or null if just a declaration
+     * @param isStatement if this is a var statement (true), or a for-loop initializer (false)
+     */
+    public VarNode(final Source source, final long token, final int finish, final IdentNode name, final Node init, boolean isStatement) {
         super(source, token, finish);
 
-        this.name  = name;
+        this.name  = init == null ? name : name.setIsInitializedHere();
         this.init  = init;
-        if (init != null) {
-            this.name.setIsInitializedHere();
-        }
+        this.isStatement = isStatement;
     }
 
+
     private VarNode(final VarNode varNode, final CopyState cs) {
         super(varNode);
 
         this.name = (IdentNode)cs.existingOrCopy(varNode.name);
         this.init = cs.existingOrCopy(varNode.init);
+        this.isStatement = varNode.isStatement;
     }
 
     @Override
@@ -83,6 +97,11 @@
     }
 
     @Override
+    public Node setAssignmentDest(IdentNode n) {
+        return setName(n);
+    }
+
+    @Override
     public Node getAssignmentSource() {
         return isAssignment() ? getInit() : null;
     }
@@ -127,16 +146,19 @@
      */
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
-            name = (IdentNode)name.accept(visitor);
-
-            if (init != null) {
-                init = init.accept(visitor);
+        if (visitor.enterVarNode(this) != null) {
+            final IdentNode newName = (IdentNode)name.accept(visitor);
+            final Node newInit = init == null ? null : init.accept(visitor);
+            final VarNode newThis;
+            if(name != newName || init != newInit) {
+                newThis = (VarNode)clone();
+                newThis.init = newInit;
+                newThis.name = newInit == null ? newName : newName.setIsInitializedHere();
+            } else {
+                newThis = this;
             }
-
-            return visitor.leave(this);
+            return visitor.leaveVarNode(newThis);
         }
-
         return this;
     }
 
@@ -162,9 +184,13 @@
     /**
      * Reset the initialization expression
      * @param init new initialization expression
+     * @return a node equivalent to this one except for the requested change.
      */
-    public void setInit(final Node init) {
-        this.init = init;
+    public VarNode setInit(final Node init) {
+        if(this.init == init) return this;
+        final VarNode n = (VarNode)clone();
+        n.init = init;
+        return n;
     }
 
     /**
@@ -179,30 +205,26 @@
      * Reset the identifier for this VarNode
      * @param name new IdentNode representing the variable being set or declared
      */
-    public void setName(final IdentNode name) {
-        this.name = name;
+    private VarNode setName(final IdentNode name) {
+        if(this.name == name) return this;
+        final VarNode n = (VarNode)clone();
+        n.name = name;
+        return n;
     }
 
     /**
-     * Check if this is a virtual assignment of a function node. Function nodes declared
-     * with a name are hoisted to the top of the scope and appear as symbols too. This is
-     * implemented by representing them as virtual VarNode assignments added to the code
-     * during lowering
-     *
-     * @see FunctionNode
-     *
-     * @return true if this is a virtual function declaration
+     * Returns true if this is a var statement (as opposed to a var initializer in a for loop).
+     * @return true if this is a var statement (as opposed to a var initializer in a for loop).
      */
-    public boolean isFunctionVarNode() {
-        return isFunctionVarNode;
+    public boolean isStatement() {
+        return isStatement;
     }
 
     /**
-     * Flag this var node as a virtual function var node assignment as described in
-     * {@link VarNode#isFunctionVarNode()}
+     * Returns true if this is a function declaration.
+     * @return true if this is a function declaration.
      */
-    public void setIsFunctionVarNode() {
-        this.isFunctionVarNode = true;
+    public boolean isFunctionDeclaration() {
+        return init instanceof FunctionNode && ((FunctionNode)init).isDeclared();
     }
-
 }
--- a/nashorn/src/jdk/nashorn/internal/ir/WhileNode.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/WhileNode.java	Sat Mar 23 00:58:39 2013 +0100
@@ -88,11 +88,11 @@
      */
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
+        if (visitor.enterWhileNode(this) != null) {
             test = test.accept(visitor);
             body = (Block)body.accept(visitor);
 
-            return visitor.leave(this);
+            return visitor.leaveWhileNode(this);
         }
         return this;
     }
--- a/nashorn/src/jdk/nashorn/internal/ir/WithNode.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/WithNode.java	Sat Mar 23 00:58:39 2013 +0100
@@ -73,10 +73,10 @@
      */
     @Override
     public Node accept(final NodeVisitor visitor) {
-        if (visitor.enter(this) != null) {
+        if (visitor.enterWithNode(this) != null) {
             expression = expression.accept(visitor);
             body = (Block)body.accept(visitor);
-            return visitor.leave(this);
+            return visitor.leaveWithNode(this);
         }
 
         return this;
--- a/nashorn/src/jdk/nashorn/internal/ir/debug/JSONWriter.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/debug/JSONWriter.java	Sat Mar 23 00:58:39 2013 +0100
@@ -112,7 +112,7 @@
     }
 
     @Override
-    public Node enter(final AccessNode accessNode) {
+    public Node enterAccessNode(final AccessNode accessNode) {
         enterDefault(accessNode);
 
         type("MemberExpression");
@@ -132,7 +132,7 @@
     }
 
     @Override
-    public Node enter(final Block block) {
+    public Node enterBlock(final Block block) {
         enterDefault(block);
 
         type("BlockStatement");
@@ -154,7 +154,7 @@
     }
 
     @Override
-    public Node enter(final BinaryNode binaryNode) {
+    public Node enterBinaryNode(final BinaryNode binaryNode) {
         enterDefault(binaryNode);
 
         final String name;
@@ -183,7 +183,7 @@
     }
 
     @Override
-    public Node enter(final BreakNode breakNode) {
+    public Node enterBreakNode(final BreakNode breakNode) {
         enterDefault(breakNode);
 
         type("BreakStatement");
@@ -201,7 +201,7 @@
     }
 
     @Override
-    public Node enter(final CallNode callNode) {
+    public Node enterCallNode(final CallNode callNode) {
         enterDefault(callNode);
 
         type("CallExpression");
@@ -217,7 +217,7 @@
     }
 
     @Override
-    public Node enter(final CaseNode caseNode) {
+    public Node enterCaseNode(final CaseNode caseNode) {
         enterDefault(caseNode);
 
         type("SwitchCase");
@@ -238,7 +238,7 @@
     }
 
     @Override
-    public Node enter(final CatchNode catchNode) {
+    public Node enterCatchNode(final CatchNode catchNode) {
         enterDefault(catchNode);
 
         type("CatchClause");
@@ -264,7 +264,7 @@
     }
 
     @Override
-    public Node enter(final ContinueNode continueNode) {
+    public Node enterContinueNode(final ContinueNode continueNode) {
         enterDefault(continueNode);
 
         type("ContinueStatement");
@@ -282,7 +282,7 @@
     }
 
     @Override
-    public Node enter(final DoWhileNode doWhileNode) {
+    public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
         enterDefault(doWhileNode);
 
         type("DoWhileStatement");
@@ -299,7 +299,7 @@
     }
 
     @Override
-    public Node enter(final EmptyNode emptyNode) {
+    public Node enterEmptyNode(final EmptyNode emptyNode) {
         enterDefault(emptyNode);
 
         type("EmptyStatement");
@@ -308,7 +308,7 @@
     }
 
     @Override
-    public Node enter(final ExecuteNode executeNode) {
+    public Node enterExecuteNode(final ExecuteNode executeNode) {
         enterDefault(executeNode);
 
         type("ExpressionStatement");
@@ -321,7 +321,7 @@
     }
 
     @Override
-    public Node enter(final ForNode forNode) {
+    public Node enterForNode(final ForNode forNode) {
         enterDefault(forNode);
 
         if (forNode.isForIn() || (forNode.isForEach() && forNode.getInit() != null)) {
@@ -384,14 +384,14 @@
     }
 
     @Override
-    public Node enter(final FunctionNode functionNode) {
+    public Node enterFunctionNode(final FunctionNode functionNode) {
         enterDefault(functionNode);
 
-        final boolean program = functionNode.isScript();
+        final boolean program = functionNode.isProgram();
         final String name;
         if (program) {
             name = "Program";
-        } else if (functionNode.isStatement()) {
+        } else if (functionNode.isDeclared()) {
             name = "FunctionDeclaration";
         } else {
             name = "FunctionExpression";
@@ -419,20 +419,11 @@
         }
 
         // body consists of nested functions and statements
-        final List<FunctionNode> funcs = functionNode.getFunctions();
         final List<Node> stats = functionNode.getStatements();
-        final int size = stats.size() + funcs.size();
+        final int size = stats.size();
         int idx = 0;
         arrayStart("body");
 
-        for (final Node func : funcs) {
-            func.accept(this);
-            if (idx != (size - 1)) {
-                comma();
-            }
-            idx++;
-        }
-
         for (final Node stat : stats) {
             if (! stat.isDebug()) {
                 stat.accept(this);
@@ -448,7 +439,7 @@
     }
 
     @Override
-    public Node enter(final IdentNode identNode) {
+    public Node enterIdentNode(final IdentNode identNode) {
         enterDefault(identNode);
 
         final String name = identNode.getName();
@@ -464,7 +455,7 @@
     }
 
     @Override
-    public Node enter(final IfNode ifNode) {
+    public Node enterIfNode(final IfNode ifNode) {
         enterDefault(ifNode);
 
         type("IfStatement");
@@ -490,7 +481,7 @@
     }
 
     @Override
-    public Node enter(final IndexNode indexNode) {
+    public Node enterIndexNode(final IndexNode indexNode) {
         enterDefault(indexNode);
 
         type("MemberExpression");
@@ -510,7 +501,7 @@
     }
 
     @Override
-    public Node enter(final LabelNode labelNode) {
+    public Node enterLabelNode(final LabelNode labelNode) {
         enterDefault(labelNode);
 
         type("LabeledStatement");
@@ -527,13 +518,13 @@
     }
 
     @Override
-    public Node enter(final LineNumberNode lineNumberNode) {
+    public Node enterLineNumberNode(final LineNumberNode lineNumberNode) {
         return null;
     }
 
     @SuppressWarnings("rawtypes")
     @Override
-    public Node enter(final LiteralNode literalNode) {
+    public Node enterLiteralNode(final LiteralNode literalNode) {
         enterDefault(literalNode);
 
         if (literalNode instanceof LiteralNode.ArrayLiteralNode) {
@@ -569,7 +560,7 @@
     }
 
     @Override
-    public Node enter(final ObjectNode objectNode) {
+    public Node enterObjectNode(final ObjectNode objectNode) {
         enterDefault(objectNode);
 
         type("ObjectExpression");
@@ -581,7 +572,7 @@
     }
 
     @Override
-    public Node enter(final PropertyNode propertyNode) {
+    public Node enterPropertyNode(final PropertyNode propertyNode) {
         final Node key = propertyNode.getKey();
 
         final Node value = propertyNode.getValue();
@@ -647,7 +638,7 @@
     }
 
     @Override
-    public Node enter(final ReturnNode returnNode) {
+    public Node enterReturnNode(final ReturnNode returnNode) {
         enterDefault(returnNode);
 
         type("ReturnStatement");
@@ -665,7 +656,7 @@
     }
 
     @Override
-    public Node enter(final RuntimeNode runtimeNode) {
+    public Node enterRuntimeNode(final RuntimeNode runtimeNode) {
         final RuntimeNode.Request req = runtimeNode.getRequest();
 
         if (req == RuntimeNode.Request.DEBUGGER) {
@@ -680,12 +671,12 @@
     }
 
     @Override
-    public Node enter(final SplitNode splitNode) {
+    public Node enterSplitNode(final SplitNode splitNode) {
         return null;
     }
 
     @Override
-    public Node enter(final SwitchNode switchNode) {
+    public Node enterSwitchNode(final SwitchNode switchNode) {
         enterDefault(switchNode);
 
         type("SwitchStatement");
@@ -701,7 +692,7 @@
     }
 
     @Override
-    public Node enter(final TernaryNode ternaryNode) {
+    public Node enterTernaryNode(final TernaryNode ternaryNode) {
         enterDefault(ternaryNode);
 
         type("ConditionalExpression");
@@ -722,7 +713,7 @@
     }
 
     @Override
-    public Node enter(final ThrowNode throwNode) {
+    public Node enterThrowNode(final ThrowNode throwNode) {
         enterDefault(throwNode);
 
         type("ThrowStatement");
@@ -735,7 +726,7 @@
     }
 
     @Override
-    public Node enter(final TryNode tryNode) {
+    public Node enterTryNode(final TryNode tryNode) {
         enterDefault(tryNode);
 
         type("TryStatement");
@@ -760,7 +751,7 @@
     }
 
     @Override
-    public Node enter(final UnaryNode unaryNode) {
+    public Node enterUnaryNode(final UnaryNode unaryNode) {
         enterDefault(unaryNode);
 
         final TokenType tokenType = unaryNode.tokenType();
@@ -816,7 +807,7 @@
     }
 
     @Override
-    public Node enter(final VarNode varNode) {
+    public Node enterVarNode(final VarNode varNode) {
         enterDefault(varNode);
 
         type("VariableDeclaration");
@@ -852,7 +843,7 @@
     }
 
     @Override
-    public Node enter(final WhileNode whileNode) {
+    public Node enterWhileNode(final WhileNode whileNode) {
         enterDefault(whileNode);
 
         type("WhileStatement");
@@ -869,7 +860,7 @@
     }
 
     @Override
-    public Node enter(final WithNode withNode) {
+    public Node enterWithNode(final WithNode withNode) {
         enterDefault(withNode);
 
         type("WithStatement");
--- a/nashorn/src/jdk/nashorn/internal/ir/debug/PrintVisitor.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/debug/PrintVisitor.java	Sat Mar 23 00:58:39 2013 +0100
@@ -42,7 +42,6 @@
 import jdk.nashorn.internal.ir.LabelNode;
 import jdk.nashorn.internal.ir.LineNumberNode;
 import jdk.nashorn.internal.ir.Node;
-import jdk.nashorn.internal.ir.ReferenceNode;
 import jdk.nashorn.internal.ir.ReturnNode;
 import jdk.nashorn.internal.ir.RuntimeNode;
 import jdk.nashorn.internal.ir.SplitNode;
@@ -138,13 +137,13 @@
      * Visits.
      */
     @Override
-    public Node enter(final AccessNode accessNode) {
+    public Node enterAccessNode(final AccessNode accessNode) {
         accessNode.toString(sb);
         return null;
     }
 
     @Override
-    public Node enter(final Block block) {
+    public Node enterBlock(final Block block) {
         sb.append(' ');
         sb.append('{');
 
@@ -152,21 +151,6 @@
 
         final boolean isFunction = block instanceof FunctionNode;
 
-        if (isFunction) {
-            final FunctionNode       function  = (FunctionNode)block;
-            final List<FunctionNode> functions = function.getFunctions();
-
-            for (final FunctionNode f : functions) {
-                sb.append(EOLN);
-                indent();
-                f.accept(this);
-            }
-
-            if (!functions.isEmpty()) {
-                sb.append(EOLN);
-            }
-        }
-
         final List<Node> statements = block.getStatements();
 
         boolean lastLineNumber = false;
@@ -224,25 +208,25 @@
     }
 
     @Override
-    public Node enter(final BreakNode breakNode) {
+    public Node enterBreakNode(final BreakNode breakNode) {
         breakNode.toString(sb);
         return null;
     }
 
     @Override
-    public Node enter(final CallNode callNode) {
+    public Node enterCallNode(final CallNode callNode) {
         callNode.toString(sb);
         return null;
     }
 
     @Override
-    public Node enter(final ContinueNode continueNode) {
+    public Node enterContinueNode(final ContinueNode continueNode) {
         continueNode.toString(sb);
         return null;
     }
 
     @Override
-    public Node enter(final DoWhileNode doWhileNode) {
+    public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
         sb.append("do");
         doWhileNode.getBody().accept(this);
         sb.append(' ');
@@ -252,7 +236,7 @@
     }
 
     @Override
-    public Node enter(final ExecuteNode executeNode) {
+    public Node enterExecuteNode(final ExecuteNode executeNode) {
         final Node expression = executeNode.getExpression();
 
         if (expression instanceof UnaryNode) {
@@ -265,7 +249,7 @@
     }
 
     @Override
-    public Node enter(final ForNode forNode) {
+    public Node enterForNode(final ForNode forNode) {
         forNode.toString(sb);
         forNode.getBody().accept(this);
 
@@ -273,15 +257,15 @@
     }
 
     @Override
-    public Node enter(final FunctionNode functionNode) {
+    public Node enterFunctionNode(final FunctionNode functionNode) {
         functionNode.toString(sb);
-        enter((Block)functionNode);
+        enterBlock(functionNode);
 
         return null;
     }
 
     @Override
-    public Node enter(final IfNode ifNode) {
+    public Node enterIfNode(final IfNode ifNode) {
         ifNode.toString(sb);
         ifNode.getPass().accept(this);
 
@@ -296,13 +280,13 @@
     }
 
     @Override
-    public Node enter(final IndexNode indexNode) {
+    public Node enterIndexNode(final IndexNode indexNode) {
         indexNode.toString(sb);
         return null;
     }
 
     @Override
-    public Node enter(final LabelNode labeledNode) {
+    public Node enterLabelNode(final LabelNode labeledNode) {
         indent -= TABWIDTH;
         indent();
         indent += TABWIDTH;
@@ -313,7 +297,7 @@
     }
 
     @Override
-    public Node enter(final LineNumberNode lineNumberNode) {
+    public Node enterLineNumberNode(final LineNumberNode lineNumberNode) {
         if (printLineNumbers) {
             lineNumberNode.toString(sb);
         }
@@ -323,25 +307,19 @@
 
 
     @Override
-    public Node enter(final ReferenceNode referenceNode) {
-        referenceNode.toString(sb);
-        return null;
-    }
-
-    @Override
-    public Node enter(final ReturnNode returnNode) {
+    public Node enterReturnNode(final ReturnNode returnNode) {
         returnNode.toString(sb);
         return null;
     }
 
     @Override
-    public Node enter(final RuntimeNode runtimeNode) {
+    public Node enterRuntimeNode(final RuntimeNode runtimeNode) {
         runtimeNode.toString(sb);
         return null;
     }
 
     @Override
-    public Node enter(final SplitNode splitNode) {
+    public Node enterSplitNode(final SplitNode splitNode) {
         splitNode.toString(sb);
         sb.append(EOLN);
         indent += TABWIDTH;
@@ -350,7 +328,7 @@
     }
 
     @Override
-    public Node leave(final SplitNode splitNode) {
+    public Node leaveSplitNode(final SplitNode splitNode) {
         sb.append("</split>");
         sb.append(EOLN);
         indent -= TABWIDTH;
@@ -359,7 +337,7 @@
     }
 
     @Override
-    public Node enter(final SwitchNode switchNode) {
+    public Node enterSwitchNode(final SwitchNode switchNode) {
         switchNode.toString(sb);
         sb.append(" {");
 
@@ -383,13 +361,13 @@
    }
 
     @Override
-    public Node enter(final ThrowNode throwNode) {
+    public Node enterThrowNode(final ThrowNode throwNode) {
         throwNode.toString(sb);
         return null;
     }
 
     @Override
-    public Node enter(final TryNode tryNode) {
+    public Node enterTryNode(final TryNode tryNode) {
         tryNode.toString(sb);
         tryNode.getBody().accept(this);
 
@@ -412,13 +390,19 @@
     }
 
     @Override
-    public Node enter(final VarNode varNode) {
-        varNode.toString(sb);
+    public Node enterVarNode(final VarNode varNode) {
+        sb.append("var ");
+        varNode.getName().toString(sb);
+        final Node init = varNode.getInit();
+        if(init != null) {
+            sb.append(" = ");
+            init.accept(this);
+        }
         return null;
     }
 
     @Override
-    public Node enter(final WhileNode whileNode) {
+    public Node enterWhileNode(final WhileNode whileNode) {
         whileNode.toString(sb);
         whileNode.getBody().accept(this);
 
@@ -426,7 +410,7 @@
     }
 
     @Override
-    public Node enter(final WithNode withNode) {
+    public Node enterWithNode(final WithNode withNode) {
         withNode.toString(sb);
         withNode.getBody().accept(this);
 
--- a/nashorn/src/jdk/nashorn/internal/ir/visitor/NodeOperatorVisitor.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/visitor/NodeOperatorVisitor.java	Sat Mar 23 00:58:39 2013 +0100
@@ -53,7 +53,7 @@
     }
 
     @Override
-    public final Node enter(final UnaryNode unaryNode) {
+    public final Node enterUnaryNode(final UnaryNode unaryNode) {
         switch (unaryNode.tokenType()) {
         case ADD:
             return enterADD(unaryNode);
@@ -81,12 +81,12 @@
         case INCPOSTFIX:
             return enterDECINC(unaryNode);
         default:
-            return super.enter(unaryNode);
+            return super.enterUnaryNode(unaryNode);
         }
     }
 
     @Override
-    public final Node leave(final UnaryNode unaryNode) {
+    public final Node leaveUnaryNode(final UnaryNode unaryNode) {
         switch (unaryNode.tokenType()) {
         case ADD:
             return leaveADD(unaryNode);
@@ -114,12 +114,12 @@
         case INCPOSTFIX:
             return leaveDECINC(unaryNode);
         default:
-            return super.leave(unaryNode);
+            return super.leaveUnaryNode(unaryNode);
         }
     }
 
     @Override
-    public final Node enter(final BinaryNode binaryNode) {
+    public final Node enterBinaryNode(final BinaryNode binaryNode) {
         switch (binaryNode.tokenType()) {
         case ADD:
             return enterADD(binaryNode);
@@ -198,12 +198,12 @@
         case SUB:
             return enterSUB(binaryNode);
         default:
-            return super.enter(binaryNode);
+            return super.enterBinaryNode(binaryNode);
         }
     }
 
     @Override
-    public final Node leave(final BinaryNode binaryNode) {
+    public final Node leaveBinaryNode(final BinaryNode binaryNode) {
         switch (binaryNode.tokenType()) {
         case ADD:
             return leaveADD(binaryNode);
@@ -282,7 +282,7 @@
         case SUB:
             return leaveSUB(binaryNode);
         default:
-            return super.leave(binaryNode);
+            return super.leaveBinaryNode(binaryNode);
         }
     }
 
--- a/nashorn/src/jdk/nashorn/internal/ir/visitor/NodeVisitor.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/visitor/NodeVisitor.java	Sat Mar 23 00:58:39 2013 +0100
@@ -49,7 +49,6 @@
 import jdk.nashorn.internal.ir.Node;
 import jdk.nashorn.internal.ir.ObjectNode;
 import jdk.nashorn.internal.ir.PropertyNode;
-import jdk.nashorn.internal.ir.ReferenceNode;
 import jdk.nashorn.internal.ir.ReturnNode;
 import jdk.nashorn.internal.ir.RuntimeNode;
 import jdk.nashorn.internal.ir.SplitNode;
@@ -153,7 +152,7 @@
      * @param  accessNode the node
      * @return processed node, null if traversal should end, null if traversal should end
      */
-    public Node enter(final AccessNode accessNode) {
+    public Node enterAccessNode(final AccessNode accessNode) {
         return enterDefault(accessNode);
     }
 
@@ -163,7 +162,7 @@
      * @param  accessNode the node
      * @return processed node, null if traversal should end
      */
-    public Node leave(final AccessNode accessNode) {
+    public Node leaveAccessNode(final AccessNode accessNode) {
         return leaveDefault(accessNode);
     }
 
@@ -173,7 +172,7 @@
      * @param  block     the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final Block block) {
+    public Node enterBlock(final Block block) {
         return enterDefault(block);
     }
 
@@ -183,7 +182,7 @@
      * @param  block the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final Block block) {
+    public Node leaveBlock(final Block block) {
         return leaveDefault(block);
     }
 
@@ -193,7 +192,7 @@
      * @param  binaryNode  the node
      * @return processed   node
      */
-    public Node enter(final BinaryNode binaryNode) {
+    public Node enterBinaryNode(final BinaryNode binaryNode) {
         return enterDefault(binaryNode);
     }
 
@@ -203,7 +202,7 @@
      * @param  binaryNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final BinaryNode binaryNode) {
+    public Node leaveBinaryNode(final BinaryNode binaryNode) {
         return leaveDefault(binaryNode);
     }
 
@@ -213,7 +212,7 @@
      * @param  breakNode the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final BreakNode breakNode) {
+    public Node enterBreakNode(final BreakNode breakNode) {
         return enterDefault(breakNode);
     }
 
@@ -223,7 +222,7 @@
      * @param  breakNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final BreakNode breakNode) {
+    public Node leaveBreakNode(final BreakNode breakNode) {
         return leaveDefault(breakNode);
     }
 
@@ -233,7 +232,7 @@
      * @param  callNode  the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final CallNode callNode) {
+    public Node enterCallNode(final CallNode callNode) {
         return enterDefault(callNode);
     }
 
@@ -243,7 +242,7 @@
      * @param  callNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final CallNode callNode) {
+    public Node leaveCallNode(final CallNode callNode) {
         return leaveDefault(callNode);
     }
 
@@ -253,7 +252,7 @@
      * @param  caseNode  the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final CaseNode caseNode) {
+    public Node enterCaseNode(final CaseNode caseNode) {
         return enterDefault(caseNode);
     }
 
@@ -263,7 +262,7 @@
      * @param  caseNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final CaseNode caseNode) {
+    public Node leaveCaseNode(final CaseNode caseNode) {
         return leaveDefault(caseNode);
     }
 
@@ -273,7 +272,7 @@
      * @param  catchNode the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final CatchNode catchNode) {
+    public Node enterCatchNode(final CatchNode catchNode) {
         return enterDefault(catchNode);
     }
 
@@ -283,7 +282,7 @@
      * @param  catchNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final CatchNode catchNode) {
+    public Node leaveCatchNode(final CatchNode catchNode) {
         return leaveDefault(catchNode);
     }
 
@@ -293,7 +292,7 @@
      * @param  continueNode the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final ContinueNode continueNode) {
+    public Node enterContinueNode(final ContinueNode continueNode) {
         return enterDefault(continueNode);
     }
 
@@ -303,7 +302,7 @@
      * @param  continueNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final ContinueNode continueNode) {
+    public Node leaveContinueNode(final ContinueNode continueNode) {
         return leaveDefault(continueNode);
     }
 
@@ -313,7 +312,7 @@
      * @param  doWhileNode the node
      * @return processed   node
      */
-    public Node enter(final DoWhileNode doWhileNode) {
+    public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
         return enterDefault(doWhileNode);
     }
 
@@ -323,7 +322,7 @@
      * @param  doWhileNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final DoWhileNode doWhileNode) {
+    public Node leaveDoWhileNode(final DoWhileNode doWhileNode) {
         return leaveDefault(doWhileNode);
     }
 
@@ -333,7 +332,7 @@
      * @param  emptyNode   the node
      * @return processed   node
      */
-    public Node enter(final EmptyNode emptyNode) {
+    public Node enterEmptyNode(final EmptyNode emptyNode) {
         return enterDefault(emptyNode);
     }
 
@@ -343,7 +342,7 @@
      * @param  emptyNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final EmptyNode emptyNode) {
+    public Node leaveEmptyNode(final EmptyNode emptyNode) {
         return leaveDefault(emptyNode);
     }
 
@@ -353,7 +352,7 @@
      * @param  executeNode the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final ExecuteNode executeNode) {
+    public Node enterExecuteNode(final ExecuteNode executeNode) {
         return enterDefault(executeNode);
     }
 
@@ -363,7 +362,7 @@
      * @param  executeNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final ExecuteNode executeNode) {
+    public Node leaveExecuteNode(final ExecuteNode executeNode) {
         return leaveDefault(executeNode);
     }
 
@@ -373,7 +372,7 @@
      * @param  forNode   the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final ForNode forNode) {
+    public Node enterForNode(final ForNode forNode) {
         return enterDefault(forNode);
     }
 
@@ -383,7 +382,7 @@
      * @param  forNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final ForNode forNode) {
+    public Node leaveForNode(final ForNode forNode) {
         return leaveDefault(forNode);
     }
 
@@ -393,7 +392,7 @@
      * @param  functionNode the node
      * @return processed    node
      */
-    public Node enter(final FunctionNode functionNode) {
+    public Node enterFunctionNode(final FunctionNode functionNode) {
         return enterDefault(functionNode);
     }
 
@@ -403,7 +402,7 @@
      * @param  functionNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final FunctionNode functionNode) {
+    public Node leaveFunctionNode(final FunctionNode functionNode) {
         return leaveDefault(functionNode);
     }
 
@@ -413,7 +412,7 @@
      * @param  identNode the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final IdentNode identNode) {
+    public Node enterIdentNode(final IdentNode identNode) {
         return enterDefault(identNode);
     }
 
@@ -423,7 +422,7 @@
      * @param  identNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final IdentNode identNode) {
+    public Node leaveIdentNode(final IdentNode identNode) {
         return leaveDefault(identNode);
     }
 
@@ -433,7 +432,7 @@
      * @param  ifNode    the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final IfNode ifNode) {
+    public Node enterIfNode(final IfNode ifNode) {
         return enterDefault(ifNode);
     }
 
@@ -443,7 +442,7 @@
      * @param  ifNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final IfNode ifNode) {
+    public Node leaveIfNode(final IfNode ifNode) {
         return leaveDefault(ifNode);
     }
 
@@ -453,7 +452,7 @@
      * @param  indexNode  the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final IndexNode indexNode) {
+    public Node enterIndexNode(final IndexNode indexNode) {
         return enterDefault(indexNode);
     }
 
@@ -463,7 +462,7 @@
      * @param  indexNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final IndexNode indexNode) {
+    public Node leaveIndexNode(final IndexNode indexNode) {
         return leaveDefault(indexNode);
     }
 
@@ -473,7 +472,7 @@
      * @param  labelNode the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final LabelNode labelNode) {
+    public Node enterLabelNode(final LabelNode labelNode) {
         return enterDefault(labelNode);
     }
 
@@ -483,7 +482,7 @@
      * @param  labelNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final LabelNode labelNode) {
+    public Node leaveLabelNode(final LabelNode labelNode) {
         return leaveDefault(labelNode);
     }
 
@@ -493,7 +492,7 @@
      * @param  lineNumberNode the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final LineNumberNode lineNumberNode) {
+    public Node enterLineNumberNode(final LineNumberNode lineNumberNode) {
         return enterDefault(lineNumberNode);
     }
 
@@ -503,7 +502,7 @@
      * @param  lineNumberNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final LineNumberNode lineNumberNode) {
+    public Node leaveLineNumberNode(final LineNumberNode lineNumberNode) {
         return leaveDefault(lineNumberNode);
     }
 
@@ -513,8 +512,7 @@
      * @param  literalNode the node
      * @return processed   node
      */
-    @SuppressWarnings("rawtypes")
-    public Node enter(final LiteralNode literalNode) {
+    public Node enterLiteralNode(final LiteralNode<?> literalNode) {
         return enterDefault(literalNode);
     }
 
@@ -524,8 +522,7 @@
      * @param  literalNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    @SuppressWarnings("rawtypes")
-    public Node leave(final LiteralNode literalNode) {
+    public Node leaveLiteralNode(final LiteralNode<?> literalNode) {
         return leaveDefault(literalNode);
     }
 
@@ -535,7 +532,7 @@
      * @param  objectNode the node
      * @return processed  node
      */
-    public Node enter(final ObjectNode objectNode) {
+    public Node enterObjectNode(final ObjectNode objectNode) {
         return enterDefault(objectNode);
     }
 
@@ -545,7 +542,7 @@
      * @param  objectNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final ObjectNode objectNode) {
+    public Node leaveObjectNode(final ObjectNode objectNode) {
         return leaveDefault(objectNode);
     }
 
@@ -555,7 +552,7 @@
      * @param  propertyNode the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final PropertyNode propertyNode) {
+    public Node enterPropertyNode(final PropertyNode propertyNode) {
         return enterDefault(propertyNode);
     }
 
@@ -565,37 +562,17 @@
      * @param  propertyNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final PropertyNode propertyNode) {
+    public Node leavePropertyNode(final PropertyNode propertyNode) {
         return leaveDefault(propertyNode);
     }
 
     /**
-     * Callback for entering a ReferenceNode
-     *
-     * @param  referenceNode the node
-     * @return processed node, null if traversal should end
-     */
-    public Node enter(final ReferenceNode referenceNode) {
-        return enterDefault(referenceNode);
-    }
-
-    /**
-     * Callback for leaving a ReferenceNode
-     *
-     * @param  referenceNode the node
-     * @return processed node, which will replace the original one, or the original node
-     */
-    public Node leave(final ReferenceNode referenceNode) {
-        return leaveDefault(referenceNode);
-    }
-
-    /**
      * Callback for entering a ReturnNode
      *
      * @param  returnNode the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final ReturnNode returnNode) {
+    public Node enterReturnNode(final ReturnNode returnNode) {
         return enterDefault(returnNode);
     }
 
@@ -605,7 +582,7 @@
      * @param  returnNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final ReturnNode returnNode) {
+    public Node leaveReturnNode(final ReturnNode returnNode) {
         return leaveDefault(returnNode);
     }
 
@@ -615,7 +592,7 @@
      * @param  runtimeNode the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final RuntimeNode runtimeNode) {
+    public Node enterRuntimeNode(final RuntimeNode runtimeNode) {
         return enterDefault(runtimeNode);
     }
 
@@ -625,7 +602,7 @@
      * @param  runtimeNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final RuntimeNode runtimeNode) {
+    public Node leaveRuntimeNode(final RuntimeNode runtimeNode) {
         return leaveDefault(runtimeNode);
     }
 
@@ -635,7 +612,7 @@
      * @param  splitNode the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final SplitNode splitNode) {
+    public Node enterSplitNode(final SplitNode splitNode) {
         return enterDefault(splitNode);
     }
 
@@ -645,7 +622,7 @@
      * @param  splitNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final SplitNode splitNode) {
+    public Node leaveSplitNode(final SplitNode splitNode) {
         return leaveDefault(splitNode);
     }
 
@@ -655,7 +632,7 @@
      * @param  switchNode the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final SwitchNode switchNode) {
+    public Node enterSwitchNode(final SwitchNode switchNode) {
         return enterDefault(switchNode);
     }
 
@@ -665,7 +642,7 @@
      * @param  switchNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final SwitchNode switchNode) {
+    public Node leaveSwitchNode(final SwitchNode switchNode) {
         return leaveDefault(switchNode);
     }
 
@@ -675,7 +652,7 @@
      * @param  ternaryNode the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final TernaryNode ternaryNode) {
+    public Node enterTernaryNode(final TernaryNode ternaryNode) {
         return enterDefault(ternaryNode);
     }
 
@@ -685,7 +662,7 @@
      * @param  ternaryNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final TernaryNode ternaryNode) {
+    public Node leaveTernaryNode(final TernaryNode ternaryNode) {
         return leaveDefault(ternaryNode);
     }
 
@@ -695,7 +672,7 @@
      * @param  throwNode the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final ThrowNode throwNode) {
+    public Node enterThrowNode(final ThrowNode throwNode) {
         return enterDefault(throwNode);
     }
 
@@ -705,7 +682,7 @@
      * @param  throwNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final ThrowNode throwNode) {
+    public Node leaveThrowNode(final ThrowNode throwNode) {
         return leaveDefault(throwNode);
     }
 
@@ -715,7 +692,7 @@
      * @param  tryNode the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final TryNode tryNode) {
+    public Node enterTryNode(final TryNode tryNode) {
         return enterDefault(tryNode);
     }
 
@@ -725,7 +702,7 @@
      * @param  tryNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final TryNode tryNode) {
+    public Node leaveTryNode(final TryNode tryNode) {
         return leaveDefault(tryNode);
     }
 
@@ -735,7 +712,7 @@
      * @param  unaryNode the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final UnaryNode unaryNode) {
+    public Node enterUnaryNode(final UnaryNode unaryNode) {
         return enterDefault(unaryNode);
     }
 
@@ -745,7 +722,7 @@
      * @param  unaryNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final UnaryNode unaryNode) {
+    public Node leaveUnaryNode(final UnaryNode unaryNode) {
         return leaveDefault(unaryNode);
     }
 
@@ -755,7 +732,7 @@
      * @param  varNode   the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final VarNode varNode) {
+    public Node enterVarNode(final VarNode varNode) {
         return enterDefault(varNode);
     }
 
@@ -765,7 +742,7 @@
      * @param  varNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final VarNode varNode) {
+    public Node leaveVarNode(final VarNode varNode) {
         return leaveDefault(varNode);
     }
 
@@ -775,7 +752,7 @@
      * @param  whileNode the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final WhileNode whileNode) {
+    public Node enterWhileNode(final WhileNode whileNode) {
         return enterDefault(whileNode);
     }
 
@@ -785,7 +762,7 @@
      * @param  whileNode the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final WhileNode whileNode) {
+    public Node leaveWhileNode(final WhileNode whileNode) {
         return leaveDefault(whileNode);
     }
 
@@ -795,7 +772,7 @@
      * @param  withNode  the node
      * @return processed node, null if traversal should end
      */
-    public Node enter(final WithNode withNode) {
+    public Node enterWithNode(final WithNode withNode) {
         return enterDefault(withNode);
     }
 
@@ -805,7 +782,7 @@
      * @param  withNode  the node
      * @return processed node, which will replace the original one, or the original node
      */
-    public Node leave(final WithNode withNode) {
+    public Node leaveWithNode(final WithNode withNode) {
         return leaveDefault(withNode);
     }
 
--- a/nashorn/src/jdk/nashorn/internal/parser/Parser.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/parser/Parser.java	Sat Mar 23 00:58:39 2013 +0100
@@ -56,10 +56,10 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Stack;
-
 import jdk.nashorn.internal.codegen.CompilerConstants;
 import jdk.nashorn.internal.codegen.Namespace;
 import jdk.nashorn.internal.ir.AccessNode;
@@ -76,17 +76,18 @@
 import jdk.nashorn.internal.ir.ExecuteNode;
 import jdk.nashorn.internal.ir.ForNode;
 import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
 import jdk.nashorn.internal.ir.IdentNode;
 import jdk.nashorn.internal.ir.IfNode;
 import jdk.nashorn.internal.ir.IndexNode;
 import jdk.nashorn.internal.ir.LabelNode;
+import jdk.nashorn.internal.ir.LexicalContext;
 import jdk.nashorn.internal.ir.LineNumberNode;
 import jdk.nashorn.internal.ir.LiteralNode;
 import jdk.nashorn.internal.ir.Node;
 import jdk.nashorn.internal.ir.ObjectNode;
 import jdk.nashorn.internal.ir.PropertyKey;
 import jdk.nashorn.internal.ir.PropertyNode;
-import jdk.nashorn.internal.ir.ReferenceNode;
 import jdk.nashorn.internal.ir.ReturnNode;
 import jdk.nashorn.internal.ir.RuntimeNode;
 import jdk.nashorn.internal.ir.SwitchNode;
@@ -97,7 +98,6 @@
 import jdk.nashorn.internal.ir.VarNode;
 import jdk.nashorn.internal.ir.WhileNode;
 import jdk.nashorn.internal.ir.WithNode;
-import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
 import jdk.nashorn.internal.runtime.DebugLogger;
 import jdk.nashorn.internal.runtime.ErrorManager;
 import jdk.nashorn.internal.runtime.JSErrorType;
@@ -117,11 +117,8 @@
     /** Is scripting mode. */
     private final boolean scripting;
 
-    /** Current function being parsed. */
-    private FunctionNode function;
-
-    /** Current parsing block. */
-    private Block block;
+    private final LexicalContext lexicalContext = new LexicalContext();
+    private List<Node> functionDeclarations;
 
     /** Namespace for function names where not explicitly given */
     private final Namespace namespace;
@@ -277,7 +274,9 @@
      * @return New block.
      */
     private Block newBlock() {
-        return block = new Block(source, token, Token.descPosition(token), block, function);
+        final Block block = new Block(source, token, Token.descPosition(token));
+        lexicalContext.push(block);
+        return block;
     }
 
     /**
@@ -290,19 +289,23 @@
         // Build function name.
         final StringBuilder sb = new StringBuilder();
 
-        if (block != null) {
-            block.addParentName(sb);
+        final FunctionNode parentFunction = getFunction();
+        if(parentFunction != null && !parentFunction.isProgram()) {
+            sb.append(parentFunction.getName()).append('$');
         }
 
         sb.append(ident != null ? ident.getName() : FUNCTION_PREFIX.tag());
         final String name = namespace.uniqueName(sb.toString());
-        assert function != null || name.equals(RUN_SCRIPT.tag())  : "name = " + name;// must not rename runScript().
+        assert parentFunction != null || name.equals(RUN_SCRIPT.tag())  : "name = " + name;// must not rename runScript().
 
         // Start new block.
-        final FunctionNode functionBlock = new FunctionNode(source, token, Token.descPosition(token), namespace, block, ident, name);
-        block = function = functionBlock;
-        function.setStrictMode(isStrictMode);
-        function.setState(errors.hasErrors() ? CompilationState.PARSE_ERROR : CompilationState.PARSED);
+        final FunctionNode functionBlock = new FunctionNode(source, token, Token.descPosition(token), namespace, ident, name);
+        if(parentFunction == null) {
+            functionBlock.setProgram();
+        }
+        functionBlock.setStrictMode(isStrictMode);
+        functionBlock.setState(errors.hasErrors() ? CompilationState.PARSE_ERROR : CompilationState.PARSED);
+        lexicalContext.push(functionBlock);
 
         return functionBlock;
     }
@@ -310,9 +313,8 @@
     /**
      * Restore the current block.
      */
-    private void restoreBlock() {
-        block    = block.getParent();
-        function = block.getFunction();
+    private void restoreBlock(Block block) {
+        lexicalContext.pop(block);
     }
 
     /**
@@ -322,19 +324,22 @@
     private Block getBlock(final boolean needsBraces) {
         // Set up new block. Captures LBRACE.
         final Block newBlock = newBlock();
-        pushControlNode(newBlock);
-
-        // Block opening brace.
-        if (needsBraces) {
-            expect(LBRACE);
-        }
-
         try {
-            // Accumulate block statements.
-            statementList();
+            pushControlNode(newBlock);
+
+            // Block opening brace.
+            if (needsBraces) {
+                expect(LBRACE);
+            }
+
+            try {
+                // Accumulate block statements.
+                statementList();
+            } finally {
+                popControlNode();
+            }
         } finally {
-            restoreBlock();
-            popControlNode();
+            restoreBlock(newBlock);
         }
 
         final int possibleEnd = Token.descPosition(token) + Token.descLength(token);
@@ -364,7 +369,7 @@
             // Accumulate statements.
             statement();
         } finally {
-            restoreBlock();
+            restoreBlock(newBlock);
         }
 
         return newBlock;
@@ -378,7 +383,10 @@
         final String name = ident.getName();
 
         if (EVAL.tag().equals(name)) {
-            function.setHasEval();
+            final Iterator<FunctionNode> it = lexicalContext.getFunctions();
+            if(it.hasNext()) {
+                it.next().setHasEval(it);
+            }
         }
     }
 
@@ -390,7 +398,7 @@
         final String name = ident.getName();
 
         if (ARGUMENTS.tag().equals(name)) {
-            function.setUsesArguments();
+            getFunction().setUsesArguments();
         }
     }
 
@@ -481,7 +489,7 @@
      * @return null or the found label node.
      */
     private LabelNode findLabel(final IdentNode ident) {
-        for (final LabelNode labelNode : function.getLabelStack()) {
+        for (final LabelNode labelNode : getFunction().getLabelStack()) {
             if (labelNode.getLabel().equals(ident)) {
                 return labelNode;
             }
@@ -495,14 +503,14 @@
      * @param labelNode Label to add.
      */
     private void pushLabel(final LabelNode labelNode) {
-        function.getLabelStack().push(labelNode);
+        getFunction().getLabelStack().push(labelNode);
     }
 
     /**
       * Remove a label from the label stack.
       */
     private void popLabel() {
-        function.getLabelStack().pop();
+        getFunction().getLabelStack().pop();
     }
 
     /**
@@ -512,6 +520,7 @@
     private void pushControlNode(final Node node) {
         final boolean isLoop = node instanceof WhileNode;
         final boolean isBreakable = node instanceof BreakableNode || node instanceof Block;
+        final FunctionNode function = getFunction();
         function.getControlStack().push(node);
 
         for (final LabelNode labelNode : function.getLabelStack()) {
@@ -530,7 +539,7 @@
      */
     private void popControlNode() {
         // Get control stack.
-        final Stack<Node> controlStack = function.getControlStack();
+        final Stack<Node> controlStack = getFunction().getControlStack();
 
         // Can be empty if missing brace.
         if (!controlStack.isEmpty()) {
@@ -540,7 +549,7 @@
 
     private void popControlNode(final Node node) {
         // Get control stack.
-        final Stack<Node> controlStack = function.getControlStack();
+        final Stack<Node> controlStack = getFunction().getControlStack();
 
         // Can be empty if missing brace.
         if (!controlStack.isEmpty() && controlStack.peek() == node) {
@@ -549,7 +558,7 @@
     }
 
     private boolean isInWithBlock() {
-        final Stack<Node> controlStack = function.getControlStack();
+        final Stack<Node> controlStack = getFunction().getControlStack();
         for (int i = controlStack.size() - 1; i >= 0; i--) {
             final Node node = controlStack.get(i);
 
@@ -562,7 +571,7 @@
     }
 
     private <T extends Node> T findControl(final Class<T> ctype) {
-        final Stack<Node> controlStack = function.getControlStack();
+        final Stack<Node> controlStack = getFunction().getControlStack();
         for (int i = controlStack.size() - 1; i >= 0; i--) {
             final Node node = controlStack.get(i);
 
@@ -576,7 +585,7 @@
 
     private <T extends Node> List<T> findControls(final Class<T> ctype, final Node to) {
         final List<T> nodes = new ArrayList<>();
-        final Stack<Node> controlStack = function.getControlStack();
+        final Stack<Node> controlStack = getFunction().getControlStack();
         for (int i = controlStack.size() - 1; i >= 0; i--) {
             final Node node = controlStack.get(i);
 
@@ -625,7 +634,10 @@
 
         script.setKind(FunctionNode.Kind.SCRIPT);
         script.setFirstToken(functionToken);
+        functionDeclarations = new ArrayList<>();
         sourceElements();
+        script.prependStatements(functionDeclarations);
+        functionDeclarations = null;
         expect(EOF);
         script.setLastToken(token);
         script.setFinish(source.getLength() - 1);
@@ -704,7 +716,7 @@
                     // check for directive prologues
                     if (checkDirective) {
                         // skip any debug statement like line number to get actual first line
-                        final Node lastStatement = lastStatement(block.getStatements());
+                        final Node lastStatement = lastStatement(getBlock().getStatements());
 
                         // get directive prologue, if any
                         final String directive = getDirective(lastStatement);
@@ -724,6 +736,7 @@
                             // handle use strict directive
                             if ("use strict".equals(directive)) {
                                 isStrictMode = true;
+                                final FunctionNode function = getFunction();
                                 function.setStrictMode(true);
 
                                 // We don't need to check these, if lexical environment is already strict
@@ -796,11 +809,11 @@
             if (isStrictMode && !topLevel) {
                 error(AbstractParser.message("strict.no.func.here"), token);
             }
-            functionExpression(true);
+            functionExpression(true, topLevel);
             return;
         }
 
-        block.addStatement(lineNumberNode);
+        getBlock().addStatement(lineNumberNode);
 
         switch (type) {
         case LBRACE:
@@ -886,7 +899,7 @@
         // Force block execution.
         final ExecuteNode executeNode = new ExecuteNode(source, newBlock.getToken(), finish, newBlock);
 
-        block.addStatement(executeNode);
+        getBlock().addStatement(executeNode);
     }
 
     /**
@@ -981,13 +994,9 @@
 
             // Allocate var node.
             final VarNode var = new VarNode(source, varToken, finish, name, init);
-            if (isStatement) {
-                function.addDeclaration(var);
-            }
-
             vars.add(var);
             // Add to current block.
-            block.addStatement(var);
+            getBlock().addStatement(var);
 
             if (type != COMMARIGHT) {
                 break;
@@ -1000,7 +1009,7 @@
             boolean semicolon = type == SEMICOLON;
             endOfLine();
             if (semicolon) {
-                block.setFinish(finish);
+                getBlock().setFinish(finish);
             }
         }
 
@@ -1017,7 +1026,7 @@
      */
     private void emptyStatement() {
         if (env._empty_statements) {
-            block.addStatement(new EmptyNode(source, token,
+            getBlock().addStatement(new EmptyNode(source, token,
                     Token.descPosition(token) + Token.descLength(token)));
         }
 
@@ -1043,7 +1052,7 @@
         ExecuteNode executeNode = null;
         if (expression != null) {
             executeNode = new ExecuteNode(source, expressionToken, finish, expression);
-            block.addStatement(executeNode);
+            getBlock().addStatement(executeNode);
         } else {
             expect(null);
         }
@@ -1052,7 +1061,7 @@
 
         if (executeNode != null) {
             executeNode.setFinish(finish);
-            block.setFinish(finish);
+            getBlock().setFinish(finish);
         }
     }
 
@@ -1094,7 +1103,7 @@
         // Construct and add new if node.
         final IfNode ifNode = new IfNode(source, ifToken, fail != null ? fail.getFinish() : pass.getFinish(), test, pass, fail);
 
-        block.addStatement(ifNode);
+        getBlock().addStatement(ifNode);
     }
 
     /**
@@ -1143,13 +1152,13 @@
             outer.setFinish(body.getFinish());
 
             // Add for to current block.
-            block.addStatement(forNode);
+            getBlock().addStatement(forNode);
         } finally {
-            restoreBlock();
+            restoreBlock(outer);
             popControlNode();
         }
 
-        block.addStatement(new ExecuteNode(source, outer.getToken(), outer.getFinish(), outer));
+        getBlock().addStatement(new ExecuteNode(source, outer.getToken(), outer.getFinish(), outer));
      }
 
     /**
@@ -1283,7 +1292,7 @@
             whileNode.setFinish(statements.getFinish());
 
             // Add WHILE node.
-            block.addStatement(whileNode);
+            getBlock().addStatement(whileNode);
         } finally {
             popControlNode();
         }
@@ -1330,7 +1339,7 @@
             doWhileNode.setFinish(finish);
 
             // Add DO node.
-            block.addStatement(doWhileNode);
+            getBlock().addStatement(doWhileNode);
         } finally {
             popControlNode();
         }
@@ -1382,7 +1391,7 @@
         final ContinueNode continueNode = new ContinueNode(source, continueToken, finish, labelNode, targetNode, findControl(TryNode.class));
         continueNode.setScopeNestingLevel(countControls(WithNode.class, targetNode));
 
-        block.addStatement(continueNode);
+        getBlock().addStatement(continueNode);
     }
 
     /**
@@ -1430,7 +1439,7 @@
         final BreakNode breakNode = new BreakNode(source, breakToken, finish, labelNode, targetNode, findControl(TryNode.class));
         breakNode.setScopeNestingLevel(countControls(WithNode.class, targetNode));
 
-        block.addStatement(breakNode);
+        getBlock().addStatement(breakNode);
     }
 
     /**
@@ -1443,7 +1452,7 @@
      */
     private void returnStatement() {
         // check for return outside function
-        if (function.getKind() == FunctionNode.Kind.SCRIPT) {
+        if (getFunction().getKind() == FunctionNode.Kind.SCRIPT) {
             error(AbstractParser.message("invalid.return"));
         }
 
@@ -1470,7 +1479,7 @@
 
         // Construct and add RETURN node.
         final ReturnNode returnNode = new ReturnNode(source, returnToken, finish, expression, findControl(TryNode.class));
-        block.addStatement(returnNode);
+        getBlock().addStatement(returnNode);
     }
 
     /**
@@ -1505,7 +1514,7 @@
 
         // Construct and add YIELD node.
         final ReturnNode yieldNode = new ReturnNode(source, yieldToken, finish, expression, findControl(TryNode.class));
-        block.addStatement(yieldNode);
+        getBlock().addStatement(yieldNode);
     }
 
     /**
@@ -1529,7 +1538,10 @@
 
         // Get WITH expression.
         final WithNode withNode = new WithNode(source, withToken, finish, null, null);
-        function.setHasWith();
+        final Iterator<FunctionNode> it = lexicalContext.getFunctions();
+        if(it.hasNext()) {
+            it.next().setHasWith(it);
+        }
 
         try {
             pushControlNode(withNode);
@@ -1549,7 +1561,7 @@
             popControlNode(withNode);
         }
 
-        block.addStatement(withNode);
+        getBlock().addStatement(withNode);
     }
 
     /**
@@ -1649,7 +1661,7 @@
 
             switchNode.setFinish(finish);
 
-            block.addStatement(switchNode);
+            getBlock().addStatement(switchNode);
         } finally {
             popControlNode();
         }
@@ -1684,7 +1696,7 @@
             labelNode.setBody(statements);
             labelNode.setFinish(finish);
 
-            block.addStatement(labelNode);
+            getBlock().addStatement(labelNode);
         } finally {
             // Remove label.
             popLabel();
@@ -1727,7 +1739,7 @@
 
         // Construct and add THROW node.
         final ThrowNode throwNode = new ThrowNode(source, throwToken, finish, expression, findControl(TryNode.class));
-        block.addStatement(throwNode);
+        getBlock().addStatement(throwNode);
     }
 
     /**
@@ -1793,18 +1805,18 @@
 
                 expect(RPAREN);
 
+                final Block catchBlock = newBlock();
                 try {
-                    final Block catchBlock = newBlock();
 
                     // Get CATCH body.
                     final Block catchBody = getBlock(true);
 
                     // Create and add catch.
                     final CatchNode catchNode = new CatchNode(source, catchToken, finish, exception, ifExpression, catchBody);
-                    block.addStatement(catchNode);
+                    getBlock().addStatement(catchNode);
                     catchBlocks.add(catchBlock);
                 } finally {
-                    restoreBlock();
+                    restoreBlock(catchBlock);
                 }
 
                 // If unconditional catch then should to be the end.
@@ -1840,11 +1852,11 @@
             outer.addStatement(tryNode);
         } finally {
             popControlNode(tryNode);
-            restoreBlock();
+            restoreBlock(outer);
             popControlNode(outer);
         }
 
-        block.addStatement(new ExecuteNode(source, outer.getToken(), outer.getFinish(), outer));
+        getBlock().addStatement(new ExecuteNode(source, outer.getToken(), outer.getFinish(), outer));
     }
 
     /**
@@ -1864,7 +1876,7 @@
         endOfLine();
 
         final RuntimeNode runtimeNode = new RuntimeNode(source, debuggerToken, finish, RuntimeNode.Request.DEBUGGER, new ArrayList<Node>());
-        block.addStatement(runtimeNode);
+        getBlock().addStatement(runtimeNode);
     }
 
     /**
@@ -2244,7 +2256,7 @@
                     parameters = new ArrayList<>();
                     functionNode = functionBody(getSetToken, getNameNode, parameters, FunctionNode.Kind.GETTER);
                     propertyNode = new PropertyNode(source, propertyToken, finish, getIdent, null);
-                    propertyNode.setGetter(new ReferenceNode(source, propertyToken, finish, functionNode));
+                    propertyNode.setGetter(functionNode);
                     return propertyNode;
 
                 case "set":
@@ -2259,7 +2271,7 @@
                     parameters.add(argIdent);
                     functionNode = functionBody(getSetToken, setNameNode, parameters, FunctionNode.Kind.SETTER);
                     propertyNode = new PropertyNode(source, propertyToken, finish, setIdent, null);
-                    propertyNode.setSetter(new ReferenceNode(source, propertyToken, finish, functionNode));
+                    propertyNode.setSetter(functionNode);
                     return propertyNode;
 
                 default:
@@ -2440,7 +2452,7 @@
 
         case FUNCTION:
             // Get function expression.
-            lhs = functionExpression(false);
+            lhs = functionExpression(false, false);
             break;
 
         default:
@@ -2545,7 +2557,7 @@
      *
      * @return Expression node.
      */
-    private Node functionExpression(final boolean isStatement) {
+    private Node functionExpression(final boolean isStatement, final boolean topLevel) {
         final LineNumberNode lineNumber = lineNumber();
 
         final long functionToken = token;
@@ -2580,10 +2592,12 @@
 
         final FunctionNode functionNode = functionBody(functionToken, name, parameters, FunctionNode.Kind.NORMAL);
 
-        if (isStatement && !isInWithBlock()) {
-            functionNode.setIsStatement();
+        if (isStatement) {
+            if(topLevel) {
+                functionNode.setIsDeclared();
+            }
             if(ARGUMENTS.tag().equals(name.getName())) {
-                functionNode.findParentFunction().setDefinesArguments();
+                getFunction().setDefinesArguments();
             }
         }
 
@@ -2591,8 +2605,6 @@
             functionNode.setIsAnonymous();
         }
 
-        final ReferenceNode referenceNode = new ReferenceNode(source, functionToken, finish, functionNode);
-
         final int arity = parameters.size();
 
         final boolean strict = functionNode.isStrictMode();
@@ -2628,17 +2640,18 @@
         }
 
         if (isStatement) {
-            final VarNode var = new VarNode(source, functionToken, finish, name, referenceNode);
-            if (isInWithBlock()) {
-                function.addDeclaration(var);
-                // Add to current block.
-                block.addStatement(var);
+            final VarNode varNode = new VarNode(source, functionToken, finish, name, functionNode, true);
+            if(topLevel) {
+                functionDeclarations.add(lineNumber);
+                functionDeclarations.add(varNode);
             } else {
-                functionNode.setFunctionVarNode(var, lineNumber);
+                final Block block = getBlock();
+                block.addStatement(lineNumber);
+                block.addStatement(varNode);
             }
         }
 
-        return referenceNode;
+        return functionNode;
     }
 
     /**
@@ -2721,7 +2734,14 @@
                 expect(LBRACE);
 
                 // Gather the function elements.
-                sourceElements();
+                final List<Node> prevFunctionDecls = functionDeclarations;
+                functionDeclarations = new ArrayList<>();
+                try {
+                    sourceElements();
+                    functionNode.prependStatements(functionDeclarations);
+                } finally {
+                    functionDeclarations = prevFunctionDecls;
+                }
 
                 functionNode.setLastToken(token);
                 expect(RBRACE);
@@ -2729,12 +2749,9 @@
 
             }
         } finally {
-            restoreBlock();
+            restoreBlock(functionNode);
         }
 
-        // Add the body of the function to the current block.
-        block.addFunction(functionNode);
-
         return functionNode;
     }
 
@@ -3069,4 +3086,12 @@
     public String toString() {
         return "[JavaScript Parsing]";
     }
+
+    private Block getBlock() {
+        return lexicalContext.getCurrentBlock();
+    }
+
+    private FunctionNode getFunction() {
+        return lexicalContext.getCurrentFunction();
+    }
 }
--- a/nashorn/src/jdk/nashorn/internal/runtime/Context.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/runtime/Context.java	Sat Mar 23 00:58:39 2013 +0100
@@ -27,9 +27,9 @@
 
 import static jdk.nashorn.internal.codegen.CompilerConstants.RUN_SCRIPT;
 import static jdk.nashorn.internal.codegen.CompilerConstants.STRICT_MODE;
+import static jdk.nashorn.internal.lookup.Lookup.MH;
 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
-import static jdk.nashorn.internal.lookup.Lookup.MH;
 
 import java.io.File;
 import java.io.IOException;
--- a/nashorn/src/jdk/nashorn/internal/runtime/resources/Messages.properties	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/runtime/resources/Messages.properties	Sat Mar 23 00:58:39 2013 +0100
@@ -42,6 +42,8 @@
 parser.error.expected.comma=Expected comma but found {0}
 parser.error.expected=Expected {0} but found {1}
 parser.error.invalid.return=Invalid return statement
+parser.error.no.func.decl.here=Function declarations can only occur at program or function body level. You should use a function expression here instead.
+parser.error.no.func.decl.here.warn=Function declarations should only occur at program or function body level. Function declaration in nested block was converted to a function expression.
 parser.error.property.redefinition=Property "{0}" already defined
 parser.error.unexpected.token=Unexpected token: {0}
 parser.error.many.vars.in.for.in.loop=Only one variable allowed in for..in loop
@@ -57,7 +59,7 @@
 parser.error.strict.cant.delete.ident=cannot delete identifier "{0}" in strict mode
 parser.error.strict.param.redefinition=strict mode function cannot have duplicate parameter name "{0}"
 parser.error.strict.no.octal=cannot use octal value in strict mode
-parser.error.strict.no.func.here=In strict mode, functions can only be declared at top-level or immediately within a function
+parser.error.strict.no.func.decl.here=In strict mode, function declarations can only occur at program or function body level. You should use a function expression here instead.
 type.error.strict.getter.setter.poison=In strict mode, "caller", "callee", and "arguments" properties can not be accessed on functions or the arguments object
 
 # not the expected type in a given context
--- a/nashorn/test/script/basic/JDK-8006755.js	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/test/script/basic/JDK-8006755.js	Sat Mar 23 00:58:39 2013 +0100
@@ -31,7 +31,7 @@
 var scope = { x: "hello" };
 
 with (scope) {
-    function main() {
+    var main = function() {
         if (x != "hello") {
             fail("x != 'hello'");
         }
--- a/nashorn/test/script/basic/NASHORN-837.js	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/test/script/basic/NASHORN-837.js	Sat Mar 23 00:58:39 2013 +0100
@@ -28,23 +28,13 @@
  * @run
  */
 
-var failed = false;
-
 try {
-    try {
-	throw new TypeError('error');
-    } catch (iox) {
-	function f() {
-	    print(iox.message);
-	}
+    throw new TypeError('error');
+} catch (iox) {
+    var f = function() {
+        if(iox.message != 'error') {
+            print("Failure! iox did not throw correct exception");
+        }
     }
-    f();
-} catch (e) {
-    failed = (e instanceof ReferenceError);
-    //iox not defined should be thrown
 }
-
-if (!failed) {
-    print("Failure! iox did not throw correct exception");
-}
-
+f();
--- a/nashorn/test/src/jdk/nashorn/internal/codegen/CompilerTest.java	Tue Mar 19 11:03:24 2013 -0300
+++ b/nashorn/test/src/jdk/nashorn/internal/codegen/CompilerTest.java	Sat Mar 23 00:58:39 2013 +0100
@@ -44,6 +44,7 @@
     private static final boolean VERBOSE  = Boolean.valueOf(System.getProperty("compilertest.verbose"));
     private static final boolean TEST262  = Boolean.valueOf(System.getProperty("compilertest.test262"));
     private static final String TEST_BASIC_DIR  = System.getProperty("test.basic.dir");
+    private static final String TEST_NODE_DIR  = System.getProperty("test.node.dir");
     private static final String TEST262_SUITE_DIR = System.getProperty("test262.suite.dir");
 
     interface TestFilter {
@@ -81,21 +82,22 @@
     @Test
     public void compileAllTests() {
         if (TEST262) {
-            compileTestSet(TEST262_SUITE_DIR, new TestFilter() {
+            compileTestSet(new File(TEST262_SUITE_DIR), new TestFilter() {
                 @Override
                 public boolean exclude(final File file, final String content) {
                     return content.indexOf("@negative") != -1;
                 }
             });
         }
-        compileTestSet(TEST_BASIC_DIR, null);
+        compileTestSet(new File(TEST_BASIC_DIR), null);
+        compileTestSet(new File(TEST_NODE_DIR, "node"), null);
+        compileTestSet(new File(TEST_NODE_DIR, "src"), null);
     }
 
-    private void compileTestSet(final String testSet, final TestFilter filter) {
+    private void compileTestSet(final File testSetDir, final TestFilter filter) {
         passed = 0;
         failed = 0;
         skipped = 0;
-        final File testSetDir = new File(testSet);
         if (! testSetDir.isDirectory()) {
             log("WARNING: " + testSetDir + " not found or not a directory");
             return;
@@ -103,7 +105,7 @@
         log(testSetDir.getAbsolutePath());
         compileJSDirectory(testSetDir, filter);
 
-        log(testSet + " compile done!");
+        log(testSetDir + " compile done!");
         log("compile ok: " + passed);
         log("compile failed: " + failed);
         log("compile skipped: " + skipped);